From 74708bbdf8d6f172b08343bdc578a20aa4b39148 Mon Sep 17 00:00:00 2001 From: Bjorn Bringert Date: Tue, 28 Apr 2009 11:26:52 +0100 Subject: [PATCH] Add GLOBAL_SEARCH intent for finding global search provider. --- core/java/android/app/SearchManager.java | 9 ++ .../server/search/SearchManagerService.java | 18 --- .../android/server/search/Searchables.java | 30 ++-- .../android/unit_tests/SearchablesTest.java | 137 +++++++++++------- 4 files changed, 104 insertions(+), 90 deletions(-) diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 3e9f3dd1e4249..76d780a1731d3 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1335,6 +1335,15 @@ public class SearchManager public final static String INTENT_ACTION_CURSOR_RESPOND = "android.search.action.CURSOR_RESPOND"; + /** + * Intent action for finding the global search activity. + * The global search provider should handle this intent. + * + * @hide Pending API council approval. + */ + public final static String INTENT_ACTION_GLOBAL_SEARCH + = "android.search.action.GLOBAL_SEARCH"; + /** * Intent action for starting the global search settings activity. * The global search provider should handle this intent. diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index eaace6b3d920e..f180823997754 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -22,7 +22,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.PackageManager.NameNotFoundException; import android.os.Handler; /** @@ -116,23 +115,6 @@ public class SearchManagerService extends ISearchManager.Stub private void updateSearchables() { mSearchables.buildSearchableList(); mSearchablesDirty = false; - - // TODO SearchableInfo should be the source of truth about whether a searchable exists. - // As it stands, if the package exists but is misconfigured in some way, then this - // would fail, and needs to be fixed. - ComponentName defaultSearch = new ComponentName( - "com.android.globalsearch", - "com.android.globalsearch.GlobalSearch"); - - try { - mContext.getPackageManager().getActivityInfo(defaultSearch, 0); - } catch (NameNotFoundException e) { - defaultSearch = new ComponentName( - "com.android.googlesearch", - "com.android.googlesearch.GoogleSearch"); - } - - mSearchables.setDefaultSearchable(defaultSearch); } /** diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java index ba75d21a3307e..e338bd2329bc3 100644 --- a/core/java/android/server/search/Searchables.java +++ b/core/java/android/server/search/Searchables.java @@ -16,6 +16,7 @@ package android.server.search; +import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -147,22 +148,6 @@ public class Searchables { } - /** - * Set the default searchable activity (when none is specified). - */ - public synchronized void setDefaultSearchable(ComponentName activity) { - SearchableInfo si = null; - if (activity != null) { - si = getSearchableInfo(activity); - if (si != null) { - // move to front of list - mSearchablesList.remove(si); - mSearchablesList.add(0, si); - } - } - mDefaultSearchable = si; - } - /** * Provides the system-default search activity, which you can use * whenever getSearchableInfo() returns null; @@ -199,14 +184,15 @@ public class Searchables { */ public void buildSearchableList() { - // create empty hash & list + // These will become the new values at the end of the method HashMap newSearchablesMap = new HashMap(); ArrayList newSearchablesList = new ArrayList(); - // use intent resolver to generate list of ACTION_SEARCH receivers final PackageManager pm = mContext.getPackageManager(); + + // use intent resolver to generate list of ACTION_SEARCH receivers List infoList; final Intent intent = new Intent(Intent.ACTION_SEARCH); infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); @@ -226,10 +212,16 @@ public class Searchables { } } - // record the final values as a coherent pair + // Find the global search provider + Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); + ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm); + SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity); + + // Store a consistent set of new values synchronized (this) { mSearchablesList = newSearchablesList; mSearchablesMap = newSearchablesMap; + mDefaultSearchable = newDefaultSearchable; } } diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java index c299b10cca1a8..d2eb7e727c110 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java @@ -16,6 +16,7 @@ package com.android.unit_tests; +import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -25,6 +26,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.XmlResourceParser; +import android.os.RemoteException; import android.server.search.SearchableInfo; import android.server.search.Searchables; import android.server.search.SearchableInfo.ActionKeyInfo; @@ -112,6 +114,17 @@ public class SearchablesTest extends AndroidTestCase { assertNull(si); } + /** + * Test that there is a default searchable (aka global search provider). + */ + public void testDefaultSearchable() { + Searchables searchables = new Searchables(mContext); + searchables.buildSearchableList(); + SearchableInfo si = searchables.getDefaultSearchable(); + checkSearchable(si); + assertTrue(searchables.isDefaultSearchable(si)); + } + /** * This is an attempt to run the searchable info list with a mocked context. Here are some * things I'd like to test. @@ -172,62 +185,66 @@ public class SearchablesTest extends AndroidTestCase { int count = searchablesList.size(); for (int ii = 0; ii < count; ii++) { SearchableInfo si = searchablesList.get(ii); - assertNotNull(si); - assertTrue(si.mSearchable); - assertTrue(si.getLabelId() != 0); // This must be a useable string - assertNotEmpty(si.mSearchActivity.getClassName()); - assertNotEmpty(si.mSearchActivity.getPackageName()); - if (si.getSuggestAuthority() != null) { - // The suggestion fields are largely optional, so we'll just confirm basic health - assertNotEmpty(si.getSuggestAuthority()); - assertNullOrNotEmpty(si.getSuggestPath()); - assertNullOrNotEmpty(si.getSuggestSelection()); - assertNullOrNotEmpty(si.getSuggestIntentAction()); - assertNullOrNotEmpty(si.getSuggestIntentData()); - } - /* Add a way to get the entire action key list, then explicitly test its elements */ - /* For now, test the most common action key (CALL) */ - ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL); - if (ai != null) { - assertEquals(ai.mKeyCode, KeyEvent.KEYCODE_CALL); - // one of these three fields must be non-null & non-empty - boolean m1 = (ai.mQueryActionMsg != null) && (ai.mQueryActionMsg.length() > 0); - boolean m2 = (ai.mSuggestActionMsg != null) && (ai.mSuggestActionMsg.length() > 0); - boolean m3 = (ai.mSuggestActionMsgColumn != null) && - (ai.mSuggestActionMsgColumn.length() > 0); - assertTrue(m1 || m2 || m3); - } - - /* - * Find ways to test these: - * - * private int mSearchMode - * private Drawable mIcon - */ - - /* - * Explicitly not tested here: - * - * Can be null, so not much to see: - * public String mSearchHint - * private String mZeroQueryBanner - * - * To be deprecated/removed, so don't bother: - * public boolean mFilterMode - * public boolean mQuickStart - * private boolean mIconResized - * private int mIconResizeWidth - * private int mIconResizeHeight - * - * All of these are "internal" working variables, not part of any contract - * private ActivityInfo mActivityInfo - * private Rect mTempRect - * private String mSuggestProviderPackage - * private String mCacheActivityContext - */ + checkSearchable(si); } } + private void checkSearchable(SearchableInfo si) { + assertNotNull(si); + assertTrue(si.mSearchable); + assertTrue(si.getLabelId() != 0); // This must be a useable string + assertNotEmpty(si.mSearchActivity.getClassName()); + assertNotEmpty(si.mSearchActivity.getPackageName()); + if (si.getSuggestAuthority() != null) { + // The suggestion fields are largely optional, so we'll just confirm basic health + assertNotEmpty(si.getSuggestAuthority()); + assertNullOrNotEmpty(si.getSuggestPath()); + assertNullOrNotEmpty(si.getSuggestSelection()); + assertNullOrNotEmpty(si.getSuggestIntentAction()); + assertNullOrNotEmpty(si.getSuggestIntentData()); + } + /* Add a way to get the entire action key list, then explicitly test its elements */ + /* For now, test the most common action key (CALL) */ + ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL); + if (ai != null) { + assertEquals(ai.mKeyCode, KeyEvent.KEYCODE_CALL); + // one of these three fields must be non-null & non-empty + boolean m1 = (ai.mQueryActionMsg != null) && (ai.mQueryActionMsg.length() > 0); + boolean m2 = (ai.mSuggestActionMsg != null) && (ai.mSuggestActionMsg.length() > 0); + boolean m3 = (ai.mSuggestActionMsgColumn != null) && + (ai.mSuggestActionMsgColumn.length() > 0); + assertTrue(m1 || m2 || m3); + } + + /* + * Find ways to test these: + * + * private int mSearchMode + * private Drawable mIcon + */ + + /* + * Explicitly not tested here: + * + * Can be null, so not much to see: + * public String mSearchHint + * private String mZeroQueryBanner + * + * To be deprecated/removed, so don't bother: + * public boolean mFilterMode + * public boolean mQuickStart + * private boolean mIconResized + * private int mIconResizeWidth + * private int mIconResizeHeight + * + * All of these are "internal" working variables, not part of any contract + * private ActivityInfo mActivityInfo + * private Rect mTempRect + * private String mSuggestProviderPackage + * private String mCacheActivityContext + */ + } + /** * Combo assert for "string not null and not empty" */ @@ -354,6 +371,20 @@ public class SearchablesTest extends AndroidTestCase { } } + @Override + public ResolveInfo resolveActivity(Intent intent, int flags) { + assertNotNull(intent); + assertEquals(intent.getAction(), SearchManager.INTENT_ACTION_GLOBAL_SEARCH); + switch (mSearchablesMode) { + case SEARCHABLES_PASSTHROUGH: + return mRealPackageManager.resolveActivity(intent, flags); + case SEARCHABLES_MOCK_ZERO: + return null; + default: + throw new UnsupportedOperationException(); + } + } + /** * Retrieve an XML file from a package. This is a low-level API used to * retrieve XML meta data.