Loading core/java/android/app/ISearchManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -23,4 +23,7 @@ import android.server.search.SearchableInfo; interface ISearchManager { SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch); List<SearchableInfo> getSearchablesInGlobalSearch(); List<SearchableInfo> getSearchablesForWebSearch(); SearchableInfo getDefaultSearchableForWebSearch(); void setDefaultWebSearch(in ComponentName component); } core/java/android/app/SearchManager.java +54 −0 Original line number Diff line number Diff line Loading @@ -1416,6 +1416,16 @@ public class SearchManager public final static String INTENT_ACTION_WEB_SEARCH_SETTINGS = "android.search.action.WEB_SEARCH_SETTINGS"; /** * Intent action broadcasted to inform that the searchables list or default have changed. * Components should handle this intent if they cache any searchable data and wish to stay * up to date on changes. * * @hide Pending API council approval. */ public final static String INTENT_ACTION_SEARCHABLES_CHANGED = "android.search.action.SEARCHABLES_CHANGED"; /** * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, * the search dialog will take no action. Loading Loading @@ -1744,4 +1754,48 @@ public class SearchManager return null; } } /** * Returns a list of the searchable activities that handle web searches. * * @return a a list of all searchable activities that handle {@link SearchManager#ACTION_WEB_SEARCH}. * * @hide because SearchableInfo is not part of the API. */ public static List<SearchableInfo> getSearchablesForWebSearch() { try { return sService.getSearchablesForWebSearch(); } catch (RemoteException e) { return null; } } /** * Returns the default searchable activity for web searches. * * @return searchable information for the activity handling web searches by default. * * @hide because SearchableInfo is not part of the API. */ public static SearchableInfo getDefaultSearchableForWebSearch() { try { return sService.getDefaultSearchableForWebSearch(); } catch (RemoteException e) { return null; } } /** * Sets the default searchable activity for web searches. * * @param component Name of the component to set as default activity for web searches. * * @hide */ public static void setDefaultWebSearch(ComponentName component) { try { sService.setDefaultWebSearch(component); } catch (RemoteException e) { } } } core/java/android/server/search/SearchManagerService.java +35 −12 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.RemoteException; import java.util.List; Loading Loading @@ -150,4 +151,26 @@ public class SearchManagerService extends ISearchManager.Stub return mSearchables.getSearchablesInGlobalSearchList(); } /** * Returns a list of the searchable activities that handle web searches. */ public List<SearchableInfo> getSearchablesForWebSearch() { updateSearchablesIfDirty(); return mSearchables.getSearchablesForWebSearchList(); } /** * Returns the default searchable activity for web searches. */ public SearchableInfo getDefaultSearchableForWebSearch() { updateSearchablesIfDirty(); return mSearchables.getDefaultSearchableForWebSearch(); } /** * Sets the default searchable activity for web searches. */ public void setDefaultWebSearch(ComponentName component) { mSearchables.setDefaultWebSearch(component); } } core/java/android/server/search/Searchables.java +179 −49 Original line number Diff line number Diff line Loading @@ -16,13 +16,18 @@ package android.server.search; import com.android.internal.app.ResolverActivity; import com.android.internal.R; import android.app.SearchManager; 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; import android.content.res.Resources; import android.os.Bundle; import android.util.Log; Loading @@ -48,7 +53,9 @@ 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; /** * Loading Loading @@ -169,7 +176,7 @@ public class Searchables { /** * Builds an entire list (suitable for display) of * activities that are searchable, by iterating the entire set of * ACTION_SEARCH intents. * ACTION_SEARCH & ACTION_WEB_SEARCH intents. * * Also clears the hash of all activities -> searches which will * refill as the user clicks "search". Loading @@ -194,21 +201,33 @@ public class Searchables { = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesInGlobalSearchList = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesForWebSearchList = new ArrayList<SearchableInfo>(); final PackageManager pm = mContext.getPackageManager(); // use intent resolver to generate list of ACTION_SEARCH receivers List<ResolveInfo> infoList; // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers. List<ResolveInfo> searchList; final Intent intent = new Intent(Intent.ACTION_SEARCH); infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); List<ResolveInfo> webSearchInfoList; final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH); webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); // analyze each one, generate a Searchables record, and record if (infoList != null) { int count = infoList.size(); if (searchList != null || webSearchInfoList != null) { int search_count = (searchList == null ? 0 : searchList.size()); int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size()); int count = search_count + web_search_count; for (int ii = 0; ii < count; ii++) { // for each component, try to find metadata ResolveInfo info = infoList.get(ii); ResolveInfo info = (ii < search_count) ? searchList.get(ii) : webSearchInfoList.get(ii - search_count); ActivityInfo ai = info.activityInfo; // Check first to avoid duplicate entries. if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) { SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai); if (searchable != null) { newSearchablesList.add(searchable); Loading @@ -219,6 +238,15 @@ 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); newSearchablesForWebSearchList.add(newSearchablesMap.get(component)); } } // Find the global search provider Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); Loading @@ -230,13 +258,93 @@ public class Searchables { + globalSearchActivity); } // Find the default web search provider. ComponentName webSearchActivity = getPreferredWebSearchActivity(); 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); } // Store a consistent set of new values synchronized (this) { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; mSearchablesForWebSearchList = newSearchablesForWebSearchList; mDefaultSearchable = newDefaultSearchable; mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch; } // Inform all listeners that the list of searchables has been updated. mContext.sendBroadcast(new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED)); } /** * 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. */ private boolean setPreferredActivity(ComponentName component, String action) { Log.d(LOG_TAG, "Checking component " + component); PackageManager pm = mContext.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; } public ComponentName getPreferredWebSearchActivity() { // Check if we have a preferred web search activity. Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); PackageManager pm = mContext.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 = mContext.getResources().getStringArray( com.android.internal.R.array.default_web_search_providers); for (String componentName : preferredActivities) { ComponentName component = ComponentName.unflattenFromString(componentName); if (setPreferredActivity(component, Intent.ACTION_WEB_SEARCH)) { return component; } } } if (ri == null) return null; return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); } /** Loading @@ -253,4 +361,26 @@ public class Searchables { public synchronized ArrayList<SearchableInfo> getSearchablesInGlobalSearchList() { return new ArrayList<SearchableInfo>(mSearchablesInGlobalSearchList); } /** * 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. */ public synchronized SearchableInfo getDefaultSearchableForWebSearch() { return mDefaultSearchableForWebSearch; } /** * Sets the default searchable activity for web searches. */ public synchronized void setDefaultWebSearch(ComponentName component) { setPreferredActivity(component, Intent.ACTION_WEB_SEARCH); buildSearchableList(); } } core/res/res/values/arrays.xml +10 −0 Original line number Diff line number Diff line Loading @@ -125,4 +125,14 @@ <item><xliff:g id="id">sync_failing</xliff:g></item> <item><xliff:g id="id">ime</xliff:g></item> </string-array> <!-- Do not translate. Each string points to the component name of an ACTION_WEB_SEARCH handling activity. On startup if there were no preferred ACTION_WEB_SEARCH handlers, the first component from this list which is found to be installed is set as the preferred activity. --> <string-array name="default_web_search_providers"> <item>com.google.android.providers.genie/.GenieLauncher</item> <item>com.android.googlesearch/.GoogleSearch</item> <item>com.android.websearch/.Search.1</item> </string-array> </resources> Loading
core/java/android/app/ISearchManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -23,4 +23,7 @@ import android.server.search.SearchableInfo; interface ISearchManager { SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch); List<SearchableInfo> getSearchablesInGlobalSearch(); List<SearchableInfo> getSearchablesForWebSearch(); SearchableInfo getDefaultSearchableForWebSearch(); void setDefaultWebSearch(in ComponentName component); }
core/java/android/app/SearchManager.java +54 −0 Original line number Diff line number Diff line Loading @@ -1416,6 +1416,16 @@ public class SearchManager public final static String INTENT_ACTION_WEB_SEARCH_SETTINGS = "android.search.action.WEB_SEARCH_SETTINGS"; /** * Intent action broadcasted to inform that the searchables list or default have changed. * Components should handle this intent if they cache any searchable data and wish to stay * up to date on changes. * * @hide Pending API council approval. */ public final static String INTENT_ACTION_SEARCHABLES_CHANGED = "android.search.action.SEARCHABLES_CHANGED"; /** * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, * the search dialog will take no action. Loading Loading @@ -1744,4 +1754,48 @@ public class SearchManager return null; } } /** * Returns a list of the searchable activities that handle web searches. * * @return a a list of all searchable activities that handle {@link SearchManager#ACTION_WEB_SEARCH}. * * @hide because SearchableInfo is not part of the API. */ public static List<SearchableInfo> getSearchablesForWebSearch() { try { return sService.getSearchablesForWebSearch(); } catch (RemoteException e) { return null; } } /** * Returns the default searchable activity for web searches. * * @return searchable information for the activity handling web searches by default. * * @hide because SearchableInfo is not part of the API. */ public static SearchableInfo getDefaultSearchableForWebSearch() { try { return sService.getDefaultSearchableForWebSearch(); } catch (RemoteException e) { return null; } } /** * Sets the default searchable activity for web searches. * * @param component Name of the component to set as default activity for web searches. * * @hide */ public static void setDefaultWebSearch(ComponentName component) { try { sService.setDefaultWebSearch(component); } catch (RemoteException e) { } } }
core/java/android/server/search/SearchManagerService.java +35 −12 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.RemoteException; import java.util.List; Loading Loading @@ -150,4 +151,26 @@ public class SearchManagerService extends ISearchManager.Stub return mSearchables.getSearchablesInGlobalSearchList(); } /** * Returns a list of the searchable activities that handle web searches. */ public List<SearchableInfo> getSearchablesForWebSearch() { updateSearchablesIfDirty(); return mSearchables.getSearchablesForWebSearchList(); } /** * Returns the default searchable activity for web searches. */ public SearchableInfo getDefaultSearchableForWebSearch() { updateSearchablesIfDirty(); return mSearchables.getDefaultSearchableForWebSearch(); } /** * Sets the default searchable activity for web searches. */ public void setDefaultWebSearch(ComponentName component) { mSearchables.setDefaultWebSearch(component); } }
core/java/android/server/search/Searchables.java +179 −49 Original line number Diff line number Diff line Loading @@ -16,13 +16,18 @@ package android.server.search; import com.android.internal.app.ResolverActivity; import com.android.internal.R; import android.app.SearchManager; 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; import android.content.res.Resources; import android.os.Bundle; import android.util.Log; Loading @@ -48,7 +53,9 @@ 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; /** * Loading Loading @@ -169,7 +176,7 @@ public class Searchables { /** * Builds an entire list (suitable for display) of * activities that are searchable, by iterating the entire set of * ACTION_SEARCH intents. * ACTION_SEARCH & ACTION_WEB_SEARCH intents. * * Also clears the hash of all activities -> searches which will * refill as the user clicks "search". Loading @@ -194,21 +201,33 @@ public class Searchables { = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesInGlobalSearchList = new ArrayList<SearchableInfo>(); ArrayList<SearchableInfo> newSearchablesForWebSearchList = new ArrayList<SearchableInfo>(); final PackageManager pm = mContext.getPackageManager(); // use intent resolver to generate list of ACTION_SEARCH receivers List<ResolveInfo> infoList; // Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers. List<ResolveInfo> searchList; final Intent intent = new Intent(Intent.ACTION_SEARCH); infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); searchList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); List<ResolveInfo> webSearchInfoList; final Intent webSearchIntent = new Intent(Intent.ACTION_WEB_SEARCH); webSearchInfoList = pm.queryIntentActivities(webSearchIntent, PackageManager.GET_META_DATA); // analyze each one, generate a Searchables record, and record if (infoList != null) { int count = infoList.size(); if (searchList != null || webSearchInfoList != null) { int search_count = (searchList == null ? 0 : searchList.size()); int web_search_count = (webSearchInfoList == null ? 0 : webSearchInfoList.size()); int count = search_count + web_search_count; for (int ii = 0; ii < count; ii++) { // for each component, try to find metadata ResolveInfo info = infoList.get(ii); ResolveInfo info = (ii < search_count) ? searchList.get(ii) : webSearchInfoList.get(ii - search_count); ActivityInfo ai = info.activityInfo; // Check first to avoid duplicate entries. if (newSearchablesMap.get(new ComponentName(ai.packageName, ai.name)) == null) { SearchableInfo searchable = SearchableInfo.getActivityMetaData(mContext, ai); if (searchable != null) { newSearchablesList.add(searchable); Loading @@ -219,6 +238,15 @@ 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); newSearchablesForWebSearchList.add(newSearchablesMap.get(component)); } } // Find the global search provider Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); Loading @@ -230,13 +258,93 @@ public class Searchables { + globalSearchActivity); } // Find the default web search provider. ComponentName webSearchActivity = getPreferredWebSearchActivity(); 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); } // Store a consistent set of new values synchronized (this) { mSearchablesMap = newSearchablesMap; mSearchablesList = newSearchablesList; mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList; mSearchablesForWebSearchList = newSearchablesForWebSearchList; mDefaultSearchable = newDefaultSearchable; mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch; } // Inform all listeners that the list of searchables has been updated. mContext.sendBroadcast(new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED)); } /** * 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. */ private boolean setPreferredActivity(ComponentName component, String action) { Log.d(LOG_TAG, "Checking component " + component); PackageManager pm = mContext.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; } public ComponentName getPreferredWebSearchActivity() { // Check if we have a preferred web search activity. Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); PackageManager pm = mContext.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 = mContext.getResources().getStringArray( com.android.internal.R.array.default_web_search_providers); for (String componentName : preferredActivities) { ComponentName component = ComponentName.unflattenFromString(componentName); if (setPreferredActivity(component, Intent.ACTION_WEB_SEARCH)) { return component; } } } if (ri == null) return null; return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); } /** Loading @@ -253,4 +361,26 @@ public class Searchables { public synchronized ArrayList<SearchableInfo> getSearchablesInGlobalSearchList() { return new ArrayList<SearchableInfo>(mSearchablesInGlobalSearchList); } /** * 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. */ public synchronized SearchableInfo getDefaultSearchableForWebSearch() { return mDefaultSearchableForWebSearch; } /** * Sets the default searchable activity for web searches. */ public synchronized void setDefaultWebSearch(ComponentName component) { setPreferredActivity(component, Intent.ACTION_WEB_SEARCH); buildSearchableList(); } }
core/res/res/values/arrays.xml +10 −0 Original line number Diff line number Diff line Loading @@ -125,4 +125,14 @@ <item><xliff:g id="id">sync_failing</xliff:g></item> <item><xliff:g id="id">ime</xliff:g></item> </string-array> <!-- Do not translate. Each string points to the component name of an ACTION_WEB_SEARCH handling activity. On startup if there were no preferred ACTION_WEB_SEARCH handlers, the first component from this list which is found to be installed is set as the preferred activity. --> <string-array name="default_web_search_providers"> <item>com.google.android.providers.genie/.GenieLauncher</item> <item>com.android.googlesearch/.GoogleSearch</item> <item>com.android.websearch/.Search.1</item> </string-array> </resources>