Loading core/java/android/app/ISearchManager.aidl +3 −4 Original line number Diff line number Diff line Loading @@ -24,9 +24,8 @@ import android.os.Bundle; /** @hide */ interface ISearchManager { SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch); SearchableInfo getSearchableInfo(in ComponentName launchActivity); List<SearchableInfo> getSearchablesInGlobalSearch(); List<SearchableInfo> getSearchablesForWebSearch(); SearchableInfo getDefaultSearchableForWebSearch(); void setDefaultWebSearch(in ComponentName component); ComponentName getGlobalSearchActivity(); ComponentName getWebSearchActivity(); } core/java/android/app/SearchDialog.java +1 −1 Original line number Diff line number Diff line Loading @@ -274,7 +274,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS SearchManager searchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); // Try to get the searchable info for the provided component. mSearchable = searchManager.getSearchableInfo(componentName, false); mSearchable = searchManager.getSearchableInfo(componentName); if (mSearchable == null) { return false; Loading core/java/android/app/SearchManager.java +16 −68 Original line number Diff line number Diff line Loading @@ -137,21 +137,11 @@ import java.util.List; * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // search within your activity * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL); // search using platform global search</pre> * * <p><b>How to enable global search with Quick Search Box.</b> In addition to searching within * <p><b>How to start global search.</b> In addition to searching within * your activity or application, you can also use the Search Manager to invoke a platform-global * search, which uses Quick Search Box to search across the device and the web. There are two ways * to do this: * <ul><li>You can simply define "search" within your application or activity to mean global search. * This is described in more detail in the * <a href="#SearchabilityMetadata">Searchability Metadata</a> section. Briefly, you will * add a single meta-data entry to your manifest, declaring that the default search * for your application is "*". This indicates to the system that no application-specific * search activity is provided, and that it should launch web-based search instead.</li> * <li>Simply do nothing and the default implementation of * {@link android.app.Activity#onSearchRequested} will cause global search to be triggered. * (You can also always trigger search via a direct call to {@link android.app.Activity#startSearch}. * This is most useful if you wish to provide local searchability <i>and</i> access to global * search.)</li></ul> * search, which uses Quick Search Box to search across the device and the web. * Override {@link android.app.Activity#onSearchRequested} and call * {@link android.app.Activity#startSearch} with {@code globalSearch} set to {@code true}. * * <p><b>How to disable search from your activity.</b> Search is a system-wide feature and users * will expect it to be available in all contexts. If your UI design absolutely precludes Loading Loading @@ -871,12 +861,8 @@ import java.util.List; * * <p>The simplest way to specify this is to add a <i>search reference</i> element to the * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file. * The value of this reference can be either of: * <ul><li>The name of your searchable activity. * It is typically prefixed by '.' to indicate that it's in the same package.</li> * <li>A "*" indicates that the system may select a default searchable activity, in which * case it will typically select web-based search.</li> * </ul> * The value of this reference should be the name of your searchable activity. * It is typically prefixed by '.' to indicate that it's in the same package. * * <p>Here is a snippet showing the necessary addition to the manifest entry for your * non-searchable activities. Loading Loading @@ -1662,33 +1648,16 @@ public class SearchManager /** * Gets the name of the global search activity. * * This is currently implemented by returning the first activity that handles * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow * more than one global search acitivity to be installed, this code must be changed. * * TODO: Doing this every time we start global search is inefficient. Will fix that once * we have settled on the right mechanism for finding the global search activity. * * @hide */ public ComponentName getGlobalSearchActivity() { Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); int count = activities.size(); for (int i = 0; i < count; i++) { ActivityInfo ai = activities.get(i).activityInfo; if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH, ai.packageName) == PackageManager.PERMISSION_GRANTED) { return new ComponentName(ai.packageName, ai.name); } else { Log.w(TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, " + "but does not have the GLOBAL_SEARCH permission."); } } try { return mService.getGlobalSearchActivity(); } catch (RemoteException ex) { Log.e(TAG, "getGlobalSearchActivity() failed: " + ex); return null; } } /** * Gets the name of the web search activity. Loading @@ -1700,13 +1669,12 @@ public class SearchManager * @hide */ public ComponentName getWebSearchActivity() { ComponentName globalSearch = getGlobalSearchActivity(); if (globalSearch == null) { try { return mService.getWebSearchActivity(); } catch (RemoteException ex) { Log.e(TAG, "getWebSearchActivity() failed: " + ex); return null; } Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.setPackage(globalSearch.getPackageName()); return intent.resolveActivity(mContext.getPackageManager()); } /** Loading Loading @@ -1840,27 +1808,7 @@ public class SearchManager */ public SearchableInfo getSearchableInfo(ComponentName componentName) { try { return mService.getSearchableInfo(componentName, false); } catch (RemoteException ex) { Log.e(TAG, "getSearchableInfo() failed: " + ex); return null; } } /** * Gets information about a searchable activity. * * @param componentName The activity to get searchable information for. * @param globalSearch If <code>false</code>, return information about the given activity. * If <code>true</code>, return information about the global search activity. * @return Searchable information, or <code>null</code> if the activity is not searchable. * * @hide because SearchableInfo is not part of the API. */ public SearchableInfo getSearchableInfo(ComponentName componentName, boolean globalSearch) { try { return mService.getSearchableInfo(componentName, globalSearch); return mService.getSearchableInfo(componentName); } catch (RemoteException ex) { Log.e(TAG, "getSearchableInfo() failed: " + ex); return null; Loading core/java/android/server/search/SearchManagerService.java +11 −30 Original line number Diff line number Diff line Loading @@ -123,25 +123,16 @@ public class SearchManagerService extends ISearchManager.Stub { * 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 { public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { 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. Loading @@ -151,27 +142,17 @@ public class SearchManagerService extends ISearchManager.Stub { } /** * Returns a list of the searchable activities that handle web searches. * Can be called from any thread. * Gets the name of the global search activity. */ public List<SearchableInfo> getSearchablesForWebSearch() { return getSearchables().getSearchablesForWebSearchList(); public ComponentName getGlobalSearchActivity() { return getSearchables().getGlobalSearchActivity(); } /** * Returns the default searchable activity for web searches. * Can be called from any thread. * Gets the name of the web search activity. */ public SearchableInfo getDefaultSearchableForWebSearch() { return getSearchables().getDefaultSearchableForWebSearch(); public ComponentName getWebSearchActivity() { return getSearchables().getWebSearchActivity(); } /** * Sets the default searchable activity for web searches. * Can be called from any thread. */ public void setDefaultWebSearch(final ComponentName component) { getSearchables().setDefaultWebSearch(component); broadcastSearchablesChanged(); } } core/java/android/server/search/Searchables.java +59 −143 Original line number Diff line number Diff line Loading @@ -16,14 +16,12 @@ package android.server.search; import com.android.internal.app.ResolverActivity; import android.Manifest; import android.app.SearchManager; import android.app.SearchableInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; Loading Loading @@ -52,9 +50,8 @@ public class Searchables { private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null; private ArrayList<SearchableInfo> mSearchablesList = null; private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null; private ArrayList<SearchableInfo> mSearchablesForWebSearchList = null; private SearchableInfo mDefaultSearchable = null; private SearchableInfo mDefaultSearchableForWebSearch = null; private ComponentName mGlobalSearchActivity = null; private ComponentName mWebSearchActivity = null; public static String GOOGLE_SEARCH_COMPONENT_NAME = "com.android.googlesearch/.GoogleSearch"; Loading Loading @@ -131,10 +128,9 @@ public class Searchables { // Irrespective of source, if a reference was found, follow it. if (refActivityName != null) { // An app or activity can declare that we should simply launch // "system default search" if search is invoked. // This value is deprecated, return null if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) { return getDefaultSearchable(); return null; } String pkg = activity.getPackageName(); ComponentName referredActivity; Loading Loading @@ -163,20 +159,6 @@ public class Searchables { } /** * Provides the system-default search activity, which you can use * whenever getSearchableInfo() returns null; * * @return Returns the system-default search activity, null if never defined */ public synchronized SearchableInfo getDefaultSearchable() { return mDefaultSearchable; } public synchronized boolean isDefaultSearchable(SearchableInfo searchable) { return searchable == mDefaultSearchable; } /** * Builds an entire list (suitable for display) of * activities that are searchable, by iterating the entire set of Loading Loading @@ -205,8 +187,6 @@ public class Searchables { = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesInGlobalSearchList = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesForWebSearchList = new ArrayList<SearchableInfo>(); final PackageManager pm = mContext.getPackageManager(); Loading Loading @@ -244,127 +224,71 @@ public class Searchables { } } if (webSearchInfoList != null) { for (int i = 0; i < webSearchInfoList.size(); ++i) { ActivityInfo ai = webSearchInfoList.get(i).activityInfo; ComponentName component = new ComponentName(ai.packageName, ai.name); SearchableInfo searchable = newSearchablesMap.get(component); if (searchable == null) { Log.w(LOG_TAG, "did not find component in searchables: " + component); } else { newSearchablesForWebSearchList.add(searchable); } } } // Find the global search provider Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm); SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity); // Find the global search activity ComponentName newGlobalSearchActivity = findGlobalSearchActivity(); if (newDefaultSearchable == null) { Log.w(LOG_TAG, "No searchable info found for new default searchable activity " + globalSearchActivity); } // Find the default web search provider. ComponentName webSearchActivity = getPreferredWebSearchActivity(mContext); SearchableInfo newDefaultSearchableForWebSearch = null; if (webSearchActivity != null) { newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity); } if (newDefaultSearchableForWebSearch == null) { Log.w(LOG_TAG, "No searchable info found for new default web search activity " + webSearchActivity); } // Find the web search activity ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity); // Store a consistent set of new values synchronized (this) { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; mSearchablesForWebSearchList = newSearchablesForWebSearchList; mDefaultSearchable = newDefaultSearchable; mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch; mGlobalSearchActivity = newGlobalSearchActivity; mWebSearchActivity = newWebSearchActivity; } } /** * Checks if the given activity component is present in the system and if so makes it the * preferred activity for handling ACTION_WEB_SEARCH. * @param component Name of the component to check and set as preferred. * @param action Intent action for which this activity is to be set as preferred. * @return true if component was detected and set as preferred activity, false if not. * Finds the global search activity. * * This is currently implemented by returning the first activity that handles * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow * more than one global search activity to be installed, this code must be changed. */ private static boolean setPreferredActivity(Context context, ComponentName component, String action) { Log.d(LOG_TAG, "Checking component " + component); PackageManager pm = context.getPackageManager(); ActivityInfo ai; try { ai = pm.getActivityInfo(component, 0); } catch (PackageManager.NameNotFoundException e) { return false; } // The code here to find the value for bestMatch is heavily inspired by the code // in ResolverActivity where the preferred activity is set. Intent intent = new Intent(action); intent.addCategory(Intent.CATEGORY_DEFAULT); List<ResolveInfo> webSearchActivities = pm.queryIntentActivities(intent, 0); ComponentName set[] = new ComponentName[webSearchActivities.size()]; int bestMatch = 0; for (int i = 0; i < webSearchActivities.size(); ++i) { ResolveInfo ri = webSearchActivities.get(i); set[i] = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); if (ri.match > bestMatch) bestMatch = ri.match; } Log.d(LOG_TAG, "Setting preferred web search activity to " + component); IntentFilter filter = new IntentFilter(action); filter.addCategory(Intent.CATEGORY_DEFAULT); pm.replacePreferredActivity(filter, bestMatch, set, component); return true; } private static ComponentName getPreferredWebSearchActivity(Context context) { // Check if we have a preferred web search activity. Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); PackageManager pm = context.getPackageManager(); ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) { Log.d(LOG_TAG, "No preferred activity set for action web search."); // The components in the providers array are checked in the order of declaration so the // first one has the highest priority. If the component exists in the system it is set // as the preferred activity to handle intent action web search. String[] preferredActivities = context.getResources().getStringArray( com.android.internal.R.array.default_web_search_providers); for (String componentName : preferredActivities) { ComponentName component = ComponentName.unflattenFromString(componentName); if (setPreferredActivity(context, component, Intent.ACTION_WEB_SEARCH)) { return component; } } private ComponentName findGlobalSearchActivity() { Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); int count = activities == null ? 0 : activities.size(); for (int i = 0; i < count; i++) { ActivityInfo ai = activities.get(i).activityInfo; if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH, ai.packageName) == PackageManager.PERMISSION_GRANTED) { return new ComponentName(ai.packageName, ai.name); } else { // If the current preferred activity is GoogleSearch, and we detect // EnhancedGoogleSearch installed as well, set the latter as preferred since that // is a superset and provides more functionality. ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) { ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString( ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME); if (setPreferredActivity(context, enhancedGoogleSearch, Intent.ACTION_WEB_SEARCH)) { return enhancedGoogleSearch; Log.w(LOG_TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, " + "but does not have the GLOBAL_SEARCH permission."); } } Log.w(LOG_TAG, "No global search activity found"); return null; } if (ri == null) return null; return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); /** * Finds the web search activity. * * Only looks in the package of the global search activity. */ private ComponentName findWebSearchActivity(ComponentName globalSearchActivity) { if (globalSearchActivity == null) { return null; } Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.setPackage(globalSearchActivity.getPackageName()); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); int count = activities == null ? 0 : activities.size(); for (int i = 0; i < count; i++) { ActivityInfo ai = activities.get(i).activityInfo; // TODO: do some sanity checks here? return new ComponentName(ai.packageName, ai.name); } Log.w(LOG_TAG, "No web search activity found"); return null; } /** Loading @@ -383,24 +307,16 @@ public class Searchables { } /** * Returns a list of the searchable activities that handle web searches. */ public synchronized ArrayList<SearchableInfo> getSearchablesForWebSearchList() { return new ArrayList<SearchableInfo>(mSearchablesForWebSearchList); } /** * Returns the default searchable activity for web searches. * Gets the name of the global search activity. */ public synchronized SearchableInfo getDefaultSearchableForWebSearch() { return mDefaultSearchableForWebSearch; public synchronized ComponentName getGlobalSearchActivity() { return mGlobalSearchActivity; } /** * Sets the default searchable activity for web searches. * Gets the name of the web search activity. */ public synchronized void setDefaultWebSearch(ComponentName component) { setPreferredActivity(mContext, component, Intent.ACTION_WEB_SEARCH); buildSearchableList(); public synchronized ComponentName getWebSearchActivity() { return mWebSearchActivity; } } Loading
core/java/android/app/ISearchManager.aidl +3 −4 Original line number Diff line number Diff line Loading @@ -24,9 +24,8 @@ import android.os.Bundle; /** @hide */ interface ISearchManager { SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch); SearchableInfo getSearchableInfo(in ComponentName launchActivity); List<SearchableInfo> getSearchablesInGlobalSearch(); List<SearchableInfo> getSearchablesForWebSearch(); SearchableInfo getDefaultSearchableForWebSearch(); void setDefaultWebSearch(in ComponentName component); ComponentName getGlobalSearchActivity(); ComponentName getWebSearchActivity(); }
core/java/android/app/SearchDialog.java +1 −1 Original line number Diff line number Diff line Loading @@ -274,7 +274,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS SearchManager searchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); // Try to get the searchable info for the provided component. mSearchable = searchManager.getSearchableInfo(componentName, false); mSearchable = searchManager.getSearchableInfo(componentName); if (mSearchable == null) { return false; Loading
core/java/android/app/SearchManager.java +16 −68 Original line number Diff line number Diff line Loading @@ -137,21 +137,11 @@ import java.util.List; * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // search within your activity * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL); // search using platform global search</pre> * * <p><b>How to enable global search with Quick Search Box.</b> In addition to searching within * <p><b>How to start global search.</b> In addition to searching within * your activity or application, you can also use the Search Manager to invoke a platform-global * search, which uses Quick Search Box to search across the device and the web. There are two ways * to do this: * <ul><li>You can simply define "search" within your application or activity to mean global search. * This is described in more detail in the * <a href="#SearchabilityMetadata">Searchability Metadata</a> section. Briefly, you will * add a single meta-data entry to your manifest, declaring that the default search * for your application is "*". This indicates to the system that no application-specific * search activity is provided, and that it should launch web-based search instead.</li> * <li>Simply do nothing and the default implementation of * {@link android.app.Activity#onSearchRequested} will cause global search to be triggered. * (You can also always trigger search via a direct call to {@link android.app.Activity#startSearch}. * This is most useful if you wish to provide local searchability <i>and</i> access to global * search.)</li></ul> * search, which uses Quick Search Box to search across the device and the web. * Override {@link android.app.Activity#onSearchRequested} and call * {@link android.app.Activity#startSearch} with {@code globalSearch} set to {@code true}. * * <p><b>How to disable search from your activity.</b> Search is a system-wide feature and users * will expect it to be available in all contexts. If your UI design absolutely precludes Loading Loading @@ -871,12 +861,8 @@ import java.util.List; * * <p>The simplest way to specify this is to add a <i>search reference</i> element to the * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file. * The value of this reference can be either of: * <ul><li>The name of your searchable activity. * It is typically prefixed by '.' to indicate that it's in the same package.</li> * <li>A "*" indicates that the system may select a default searchable activity, in which * case it will typically select web-based search.</li> * </ul> * The value of this reference should be the name of your searchable activity. * It is typically prefixed by '.' to indicate that it's in the same package. * * <p>Here is a snippet showing the necessary addition to the manifest entry for your * non-searchable activities. Loading Loading @@ -1662,33 +1648,16 @@ public class SearchManager /** * Gets the name of the global search activity. * * This is currently implemented by returning the first activity that handles * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow * more than one global search acitivity to be installed, this code must be changed. * * TODO: Doing this every time we start global search is inefficient. Will fix that once * we have settled on the right mechanism for finding the global search activity. * * @hide */ public ComponentName getGlobalSearchActivity() { Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); int count = activities.size(); for (int i = 0; i < count; i++) { ActivityInfo ai = activities.get(i).activityInfo; if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH, ai.packageName) == PackageManager.PERMISSION_GRANTED) { return new ComponentName(ai.packageName, ai.name); } else { Log.w(TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, " + "but does not have the GLOBAL_SEARCH permission."); } } try { return mService.getGlobalSearchActivity(); } catch (RemoteException ex) { Log.e(TAG, "getGlobalSearchActivity() failed: " + ex); return null; } } /** * Gets the name of the web search activity. Loading @@ -1700,13 +1669,12 @@ public class SearchManager * @hide */ public ComponentName getWebSearchActivity() { ComponentName globalSearch = getGlobalSearchActivity(); if (globalSearch == null) { try { return mService.getWebSearchActivity(); } catch (RemoteException ex) { Log.e(TAG, "getWebSearchActivity() failed: " + ex); return null; } Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.setPackage(globalSearch.getPackageName()); return intent.resolveActivity(mContext.getPackageManager()); } /** Loading Loading @@ -1840,27 +1808,7 @@ public class SearchManager */ public SearchableInfo getSearchableInfo(ComponentName componentName) { try { return mService.getSearchableInfo(componentName, false); } catch (RemoteException ex) { Log.e(TAG, "getSearchableInfo() failed: " + ex); return null; } } /** * Gets information about a searchable activity. * * @param componentName The activity to get searchable information for. * @param globalSearch If <code>false</code>, return information about the given activity. * If <code>true</code>, return information about the global search activity. * @return Searchable information, or <code>null</code> if the activity is not searchable. * * @hide because SearchableInfo is not part of the API. */ public SearchableInfo getSearchableInfo(ComponentName componentName, boolean globalSearch) { try { return mService.getSearchableInfo(componentName, globalSearch); return mService.getSearchableInfo(componentName); } catch (RemoteException ex) { Log.e(TAG, "getSearchableInfo() failed: " + ex); return null; Loading
core/java/android/server/search/SearchManagerService.java +11 −30 Original line number Diff line number Diff line Loading @@ -123,25 +123,16 @@ public class SearchManagerService extends ISearchManager.Stub { * 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 { public SearchableInfo getSearchableInfo(final ComponentName launchActivity) { 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. Loading @@ -151,27 +142,17 @@ public class SearchManagerService extends ISearchManager.Stub { } /** * Returns a list of the searchable activities that handle web searches. * Can be called from any thread. * Gets the name of the global search activity. */ public List<SearchableInfo> getSearchablesForWebSearch() { return getSearchables().getSearchablesForWebSearchList(); public ComponentName getGlobalSearchActivity() { return getSearchables().getGlobalSearchActivity(); } /** * Returns the default searchable activity for web searches. * Can be called from any thread. * Gets the name of the web search activity. */ public SearchableInfo getDefaultSearchableForWebSearch() { return getSearchables().getDefaultSearchableForWebSearch(); public ComponentName getWebSearchActivity() { return getSearchables().getWebSearchActivity(); } /** * Sets the default searchable activity for web searches. * Can be called from any thread. */ public void setDefaultWebSearch(final ComponentName component) { getSearchables().setDefaultWebSearch(component); broadcastSearchablesChanged(); } }
core/java/android/server/search/Searchables.java +59 −143 Original line number Diff line number Diff line Loading @@ -16,14 +16,12 @@ package android.server.search; import com.android.internal.app.ResolverActivity; import android.Manifest; import android.app.SearchManager; import android.app.SearchableInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; Loading Loading @@ -52,9 +50,8 @@ public class Searchables { private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null; private ArrayList<SearchableInfo> mSearchablesList = null; private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null; private ArrayList<SearchableInfo> mSearchablesForWebSearchList = null; private SearchableInfo mDefaultSearchable = null; private SearchableInfo mDefaultSearchableForWebSearch = null; private ComponentName mGlobalSearchActivity = null; private ComponentName mWebSearchActivity = null; public static String GOOGLE_SEARCH_COMPONENT_NAME = "com.android.googlesearch/.GoogleSearch"; Loading Loading @@ -131,10 +128,9 @@ public class Searchables { // Irrespective of source, if a reference was found, follow it. if (refActivityName != null) { // An app or activity can declare that we should simply launch // "system default search" if search is invoked. // This value is deprecated, return null if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) { return getDefaultSearchable(); return null; } String pkg = activity.getPackageName(); ComponentName referredActivity; Loading Loading @@ -163,20 +159,6 @@ public class Searchables { } /** * Provides the system-default search activity, which you can use * whenever getSearchableInfo() returns null; * * @return Returns the system-default search activity, null if never defined */ public synchronized SearchableInfo getDefaultSearchable() { return mDefaultSearchable; } public synchronized boolean isDefaultSearchable(SearchableInfo searchable) { return searchable == mDefaultSearchable; } /** * Builds an entire list (suitable for display) of * activities that are searchable, by iterating the entire set of Loading Loading @@ -205,8 +187,6 @@ public class Searchables { = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesInGlobalSearchList = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesForWebSearchList = new ArrayList<SearchableInfo>(); final PackageManager pm = mContext.getPackageManager(); Loading Loading @@ -244,127 +224,71 @@ public class Searchables { } } if (webSearchInfoList != null) { for (int i = 0; i < webSearchInfoList.size(); ++i) { ActivityInfo ai = webSearchInfoList.get(i).activityInfo; ComponentName component = new ComponentName(ai.packageName, ai.name); SearchableInfo searchable = newSearchablesMap.get(component); if (searchable == null) { Log.w(LOG_TAG, "did not find component in searchables: " + component); } else { newSearchablesForWebSearchList.add(searchable); } } } // Find the global search provider Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm); SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity); // Find the global search activity ComponentName newGlobalSearchActivity = findGlobalSearchActivity(); if (newDefaultSearchable == null) { Log.w(LOG_TAG, "No searchable info found for new default searchable activity " + globalSearchActivity); } // Find the default web search provider. ComponentName webSearchActivity = getPreferredWebSearchActivity(mContext); SearchableInfo newDefaultSearchableForWebSearch = null; if (webSearchActivity != null) { newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity); } if (newDefaultSearchableForWebSearch == null) { Log.w(LOG_TAG, "No searchable info found for new default web search activity " + webSearchActivity); } // Find the web search activity ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity); // Store a consistent set of new values synchronized (this) { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; mSearchablesForWebSearchList = newSearchablesForWebSearchList; mDefaultSearchable = newDefaultSearchable; mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch; mGlobalSearchActivity = newGlobalSearchActivity; mWebSearchActivity = newWebSearchActivity; } } /** * Checks if the given activity component is present in the system and if so makes it the * preferred activity for handling ACTION_WEB_SEARCH. * @param component Name of the component to check and set as preferred. * @param action Intent action for which this activity is to be set as preferred. * @return true if component was detected and set as preferred activity, false if not. * Finds the global search activity. * * This is currently implemented by returning the first activity that handles * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow * more than one global search activity to be installed, this code must be changed. */ private static boolean setPreferredActivity(Context context, ComponentName component, String action) { Log.d(LOG_TAG, "Checking component " + component); PackageManager pm = context.getPackageManager(); ActivityInfo ai; try { ai = pm.getActivityInfo(component, 0); } catch (PackageManager.NameNotFoundException e) { return false; } // The code here to find the value for bestMatch is heavily inspired by the code // in ResolverActivity where the preferred activity is set. Intent intent = new Intent(action); intent.addCategory(Intent.CATEGORY_DEFAULT); List<ResolveInfo> webSearchActivities = pm.queryIntentActivities(intent, 0); ComponentName set[] = new ComponentName[webSearchActivities.size()]; int bestMatch = 0; for (int i = 0; i < webSearchActivities.size(); ++i) { ResolveInfo ri = webSearchActivities.get(i); set[i] = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); if (ri.match > bestMatch) bestMatch = ri.match; } Log.d(LOG_TAG, "Setting preferred web search activity to " + component); IntentFilter filter = new IntentFilter(action); filter.addCategory(Intent.CATEGORY_DEFAULT); pm.replacePreferredActivity(filter, bestMatch, set, component); return true; } private static ComponentName getPreferredWebSearchActivity(Context context) { // Check if we have a preferred web search activity. Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); PackageManager pm = context.getPackageManager(); ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) { Log.d(LOG_TAG, "No preferred activity set for action web search."); // The components in the providers array are checked in the order of declaration so the // first one has the highest priority. If the component exists in the system it is set // as the preferred activity to handle intent action web search. String[] preferredActivities = context.getResources().getStringArray( com.android.internal.R.array.default_web_search_providers); for (String componentName : preferredActivities) { ComponentName component = ComponentName.unflattenFromString(componentName); if (setPreferredActivity(context, component, Intent.ACTION_WEB_SEARCH)) { return component; } } private ComponentName findGlobalSearchActivity() { Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); int count = activities == null ? 0 : activities.size(); for (int i = 0; i < count; i++) { ActivityInfo ai = activities.get(i).activityInfo; if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH, ai.packageName) == PackageManager.PERMISSION_GRANTED) { return new ComponentName(ai.packageName, ai.name); } else { // If the current preferred activity is GoogleSearch, and we detect // EnhancedGoogleSearch installed as well, set the latter as preferred since that // is a superset and provides more functionality. ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) { ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString( ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME); if (setPreferredActivity(context, enhancedGoogleSearch, Intent.ACTION_WEB_SEARCH)) { return enhancedGoogleSearch; Log.w(LOG_TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, " + "but does not have the GLOBAL_SEARCH permission."); } } Log.w(LOG_TAG, "No global search activity found"); return null; } if (ri == null) return null; return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); /** * Finds the web search activity. * * Only looks in the package of the global search activity. */ private ComponentName findWebSearchActivity(ComponentName globalSearchActivity) { if (globalSearchActivity == null) { return null; } Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.setPackage(globalSearchActivity.getPackageName()); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); int count = activities == null ? 0 : activities.size(); for (int i = 0; i < count; i++) { ActivityInfo ai = activities.get(i).activityInfo; // TODO: do some sanity checks here? return new ComponentName(ai.packageName, ai.name); } Log.w(LOG_TAG, "No web search activity found"); return null; } /** Loading @@ -383,24 +307,16 @@ public class Searchables { } /** * Returns a list of the searchable activities that handle web searches. */ public synchronized ArrayList<SearchableInfo> getSearchablesForWebSearchList() { return new ArrayList<SearchableInfo>(mSearchablesForWebSearchList); } /** * Returns the default searchable activity for web searches. * Gets the name of the global search activity. */ public synchronized SearchableInfo getDefaultSearchableForWebSearch() { return mDefaultSearchableForWebSearch; public synchronized ComponentName getGlobalSearchActivity() { return mGlobalSearchActivity; } /** * Sets the default searchable activity for web searches. * Gets the name of the web search activity. */ public synchronized void setDefaultWebSearch(ComponentName component) { setPreferredActivity(mContext, component, Intent.ACTION_WEB_SEARCH); buildSearchableList(); public synchronized ComponentName getWebSearchActivity() { return mWebSearchActivity; } }