Searchables.java revision 6d72e029cb6e5a9cf26aa3314c3dca83614fc91b
1875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen/*
2875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Copyright (C) 2009 The Android Open Source Project
3875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen *
4875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Licensed under the Apache License, Version 2.0 (the "License");
5875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * you may not use this file except in compliance with the License.
6875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * You may obtain a copy of the License at
7875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen *
8875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen *      http://www.apache.org/licenses/LICENSE-2.0
9875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen *
10875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * Unless required by applicable law or agreed to in writing, software
11875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * distributed under the License is distributed on an "AS IS" BASIS,
12875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * See the License for the specific language governing permissions and
14875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * limitations under the License.
15875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */
16875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
17875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenpackage android.server.search;
18875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
1974708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringertimport android.app.SearchManager;
20875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.content.ComponentName;
21875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.content.Context;
22875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.content.Intent;
23875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.content.pm.ActivityInfo;
24875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.content.pm.PackageManager;
25875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.content.pm.ResolveInfo;
26875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport android.os.Bundle;
27875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
28875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport java.util.ArrayList;
29875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport java.util.HashMap;
30875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenimport java.util.List;
31875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
32875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen/**
33875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen * This class maintains the information about all searchable activities.
34875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen */
35875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaenpublic class Searchables {
36875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
37875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // static strings used for XML lookups, etc.
38875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // TODO how should these be documented for the developer, in a more structured way than
39875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    // the current long wordy javadoc in SearchManager.java ?
40875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private static final String MD_LABEL_DEFAULT_SEARCHABLE = "android.app.default_searchable";
41875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*";
42875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
43875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private Context mContext;
44875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
45875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
46875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private ArrayList<SearchableInfo> mSearchablesList = null;
476d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert    private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
48875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    private SearchableInfo mDefaultSearchable = null;
49875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
50875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
51875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
52875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param context Context to use for looking up activities etc.
53875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
54875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    public Searchables (Context context) {
55875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        mContext = context;
56875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
57875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
58875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
59875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Look up, or construct, based on the activity.
60875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
61875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * The activities fall into three cases, based on meta-data found in
62875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * the manifest entry:
63875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * <ol>
64875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * <li>The activity itself implements search.  This is indicated by the
65875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * presence of a "android.app.searchable" meta-data attribute.
66875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * The value is a reference to an XML file containing search information.</li>
67875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * <li>A related activity implements search.  This is indicated by the
68875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * presence of a "android.app.default_searchable" meta-data attribute.
69875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * The value is a string naming the activity implementing search.  In this
70875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * case the factory will "redirect" and return the searchable data.</li>
71875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * <li>No searchability data is provided.  We return null here and other
72875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * code will insert the "default" (e.g. contacts) search.
73875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
74875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * TODO: cache the result in the map, and check the map first.
75875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * TODO: it might make sense to implement the searchable reference as
76875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * an application meta-data entry.  This way we don't have to pepper each
77875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * and every activity.
78875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * TODO: can we skip the constructor step if it's a non-searchable?
79875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * TODO: does it make sense to plug the default into a slot here for
80875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * automatic return?  Probably not, but it's one way to do it.
81875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
82875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @param activity The name of the current activity, or null if the
83875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * activity does not define any explicit searchable metadata.
84875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
85875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    public SearchableInfo getSearchableInfo(ComponentName activity) {
86875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // Step 1.  Is the result already hashed?  (case 1)
87875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        SearchableInfo result;
88875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        synchronized (this) {
89875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            result = mSearchablesMap.get(activity);
90875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (result != null) return result;
91875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
92875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
93875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // Step 2.  See if the current activity references a searchable.
94875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // Note:  Conceptually, this could be a while(true) loop, but there's
95875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // no point in implementing reference chaining here and risking a loop.
96875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // References must point directly to searchable activities.
97875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
98875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        ActivityInfo ai = null;
99875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        try {
100875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            ai = mContext.getPackageManager().
101875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                       getActivityInfo(activity, PackageManager.GET_META_DATA );
102875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            String refActivityName = null;
103875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
104875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // First look for activity-specific reference
105875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            Bundle md = ai.metaData;
106875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (md != null) {
107875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE);
108875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            }
109875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // If not found, try for app-wide reference
110875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (refActivityName == null) {
111875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                md = ai.applicationInfo.metaData;
112875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (md != null) {
113875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    refActivityName = md.getString(MD_LABEL_DEFAULT_SEARCHABLE);
114875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                }
115875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            }
116875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
117875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // Irrespective of source, if a reference was found, follow it.
118875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            if (refActivityName != null)
119875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            {
120875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // An app or activity can declare that we should simply launch
121875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // "system default search" if search is invoked.
122875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) {
123875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    return getDefaultSearchable();
124875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                }
125875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                String pkg = activity.getPackageName();
126875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                ComponentName referredActivity;
127875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (refActivityName.charAt(0) == '.') {
128875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    referredActivity = new ComponentName(pkg, pkg + refActivityName);
129875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                } else {
130875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    referredActivity = new ComponentName(pkg, refActivityName);
131875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                }
132875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
133875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // Now try the referred activity, and if found, cache
134875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // it against the original name so we can skip the check
135875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                synchronized (this) {
136875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    result = mSearchablesMap.get(referredActivity);
137875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    if (result != null) {
138875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                        mSearchablesMap.put(activity, result);
139875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                        return result;
140875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    }
141875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                }
142875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            }
143875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        } catch (PackageManager.NameNotFoundException e) {
144875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            // case 3: no metadata
145875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
146875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
147875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // Step 3.  None found. Return null.
148875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return null;
149875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
150875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
151875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
152875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
153875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Provides the system-default search activity, which you can use
154875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * whenever getSearchableInfo() returns null;
155875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
156875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * @return Returns the system-default search activity, null if never defined
157875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
158875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    public synchronized SearchableInfo getDefaultSearchable() {
159875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return mDefaultSearchable;
160875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
161875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
162875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    public synchronized boolean isDefaultSearchable(SearchableInfo searchable) {
163875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return searchable == mDefaultSearchable;
164875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
165875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
166875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
167875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Builds an entire list (suitable for display) of
168875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * activities that are searchable, by iterating the entire set of
169875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * ACTION_SEARCH intents.
170875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
171875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Also clears the hash of all activities -> searches which will
172875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * refill as the user clicks "search".
173875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
174875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * This should only be done at startup and again if we know that the
175875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * list has changed.
176875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
177875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * TODO: every activity that provides a ACTION_SEARCH intent should
178875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * also provide searchability meta-data.  There are a bunch of checks here
179875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * that, if data is not found, silently skip to the next activity.  This
180875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * won't help a developer trying to figure out why their activity isn't
181875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * showing up in the list, but an exception here is too rough.  I would
182875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * like to find a better notification mechanism.
183875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     *
184875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * TODO: sort the list somehow?  UI choice.
185875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
186875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    public void buildSearchableList() {
187875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
18874708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert        // These will become the new values at the end of the method
189875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        HashMap<ComponentName, SearchableInfo> newSearchablesMap
190875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                                = new HashMap<ComponentName, SearchableInfo>();
191875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        ArrayList<SearchableInfo> newSearchablesList
192875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                                = new ArrayList<SearchableInfo>();
1936d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert        ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
1946d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert                                = new ArrayList<SearchableInfo>();
195875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
196875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        final PackageManager pm = mContext.getPackageManager();
19774708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert
19874708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert        // use intent resolver to generate list of ACTION_SEARCH receivers
199875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        List<ResolveInfo> infoList;
200875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        final Intent intent = new Intent(Intent.ACTION_SEARCH);
201875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
202875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
203875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        // analyze each one, generate a Searchables record, and record
204875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        if (infoList != null) {
205875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            int count = infoList.size();
206875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            for (int ii = 0; ii < count; ii++) {
207875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                // for each component, try to find metadata
208875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                ResolveInfo info = infoList.get(ii);
209875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                ActivityInfo ai = info.activityInfo;
210875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai);
211875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                if (searchable != null) {
212875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    newSearchablesList.add(searchable);
213875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                    newSearchablesMap.put(searchable.mSearchActivity, searchable);
2146d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert                    if (searchable.shouldIncludeInGlobalSearch()) {
2156d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert                        newSearchablesInGlobalSearchList.add(searchable);
2166d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert                    }
217875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen                }
218875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            }
219875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
220875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
22174708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert        // Find the global search provider
22274708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert        Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
22374708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert        ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm);
22474708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert        SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity);
22574708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert
22674708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert        // Store a consistent set of new values
227875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        synchronized (this) {
228875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen            mSearchablesMap = newSearchablesMap;
2296d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert            mSearchablesList = newSearchablesList;
2306d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert            mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
23174708bbdf8d6f172b08343bdc578a20aa4b39148Bjorn Bringert            mDefaultSearchable = newDefaultSearchable;
232875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        }
233875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
234875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen
235875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    /**
236875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     * Returns the list of searchable activities.
237875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen     */
238875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    public synchronized ArrayList<SearchableInfo> getSearchablesList() {
239875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        ArrayList<SearchableInfo> result = new ArrayList<SearchableInfo>(mSearchablesList);
240875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen        return result;
241875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen    }
2426d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert
2436d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert    /**
2446d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert     * Returns a list of the searchable activities that can be included in global search.
2456d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert     */
2466d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert    public synchronized ArrayList<SearchableInfo> getSearchablesInGlobalSearchList() {
2476d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert        return new ArrayList<SearchableInfo>(mSearchablesInGlobalSearchList);
2486d72e029cb6e5a9cf26aa3314c3dca83614fc91bBjorn Bringert    }
249875d50a4b9294b2be33cff6493cae7acd1d07ea7Karl Rosaen}
250