Cleanup of global search references in SearchDialog and SearchManager.

This commit is contained in:
Amith Yamasani
2010-02-04 14:52:32 -08:00
parent 1e84ac5122
commit 479ae0a280
5 changed files with 78 additions and 899 deletions

View File

@@ -822,11 +822,6 @@ public class Dialog implements DialogInterface, Window.Callback,
final SearchManager searchManager = (SearchManager) mContext
.getSystemService(Context.SEARCH_SERVICE);
// can't start search without an associated activity (e.g a system dialog)
if (!searchManager.hasIdent()) {
return false;
}
// associate search with owner activity if possible (otherwise it will default to
// global search).
final ComponentName appName = getAssociatedActivity();

View File

@@ -16,6 +16,9 @@
package android.app;
import com.android.common.Patterns;
import com.android.common.speech.Recognition;
import static android.app.SuggestionsAdapter.getColumnString;
import android.content.ActivityNotFoundException;
@@ -67,9 +70,6 @@ import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import com.android.common.Patterns;
import com.android.common.speech.Recognition;
import java.util.ArrayList;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicLong;
@@ -89,10 +89,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
private static final String INSTANCE_KEY_COMPONENT = "comp";
private static final String INSTANCE_KEY_APPDATA = "data";
private static final String INSTANCE_KEY_GLOBALSEARCH = "glob";
private static final String INSTANCE_KEY_STORED_COMPONENT = "sComp";
private static final String INSTANCE_KEY_STORED_APPDATA = "sData";
private static final String INSTANCE_KEY_PREVIOUS_COMPONENTS = "sPrev";
private static final String INSTANCE_KEY_USER_QUERY = "uQry";
// The string used for privateImeOptions to identify to the IME that it should not show
@@ -115,19 +112,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
private SearchableInfo mSearchable;
private ComponentName mLaunchComponent;
private Bundle mAppSearchData;
private boolean mGlobalSearchMode;
private Context mActivityContext;
private SearchManager mSearchManager;
// Values we store to allow user to toggle between in-app search and global search.
private ComponentName mStoredComponentName;
private Bundle mStoredAppSearchData;
// stack of previous searchables, to support the BACK key after
// SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.
// The top of the stack (= previous searchable) is the last element of the list,
// since adding and removing is efficient at the end of an ArrayList.
private ArrayList<ComponentName> mPreviousComponents;
// For voice searching
private final Intent mVoiceWebSearchIntent;
@@ -158,7 +144,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @param context Application Context we can use for system acess
*/
public SearchDialog(Context context, SearchManager searchManager) {
super(context, com.android.internal.R.style.Theme_GlobalSearchBar);
super(context, com.android.internal.R.style.Theme_SearchBar);
// Save voice intent for later queries/launching
mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
@@ -241,14 +227,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @return true if search dialog launched, false if not
*/
public boolean show(String initialQuery, boolean selectInitialQuery,
ComponentName componentName, Bundle appSearchData, boolean globalSearch) {
// Reset any stored values from last time dialog was shown.
mStoredComponentName = null;
mStoredAppSearchData = null;
boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData,
globalSearch);
ComponentName componentName, Bundle appSearchData) {
boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData);
if (success) {
// Display the drop down as soon as possible instead of waiting for the rest of the
// pending UI stuff to get done, so that things appear faster to the user.
@@ -257,67 +237,16 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
return success;
}
private boolean isInRealAppSearch() {
return !mGlobalSearchMode
&& (mPreviousComponents == null || mPreviousComponents.isEmpty());
}
/**
* Called in response to a press of the hard search button in
* {@link #onKeyDown(int, KeyEvent)}, this method toggles between in-app
* search and global search when relevant.
*
* If pressed within an in-app search context, this switches the search dialog out to
* global search. If pressed within a global search context that was originally an in-app
* search context, this switches back to the in-app search context. If pressed within a
* global search context that has no original in-app search context (e.g., global search
* from Home), this does nothing.
*
* @return false if we wanted to toggle context but could not do so successfully, true
* in all other cases
*/
private boolean toggleGlobalSearch() {
String currentSearchText = mSearchAutoComplete.getText().toString();
if (!mGlobalSearchMode) {
mStoredComponentName = mLaunchComponent;
mStoredAppSearchData = mAppSearchData;
// If this is the browser, we have a special case to not show the icon to the left
// of the text field, for extra space for url entry (this should be reconciled in
// Eclair). So special case a second tap of the search button to remove any
// already-entered text so that we can be sure to show the "Quick Search Box" hint
// text to still make it clear to the user that we've jumped out to global search.
//
// TODO: When the browser icon issue is reconciled in Eclair, remove this special case.
if (isBrowserSearch()) currentSearchText = "";
cancel();
mSearchManager.startGlobalSearch(currentSearchText, false, mStoredAppSearchData);
return true;
} else {
if (mStoredComponentName != null) {
// This means we should toggle *back* to an in-app search context from
// global search.
return doShow(currentSearchText, false, mStoredComponentName,
mStoredAppSearchData, false);
} else {
return true;
}
}
}
/**
* Does the rest of the work required to show the search dialog. Called by both
* {@link #show(String, boolean, ComponentName, Bundle, boolean)} and
* {@link #toggleGlobalSearch()}.
*
* Does the rest of the work required to show the search dialog. Called by
* {@link #show(String, boolean, ComponentName, Bundle)} and
*
* @return true if search dialog showed, false if not
*/
private boolean doShow(String initialQuery, boolean selectInitialQuery,
ComponentName componentName, Bundle appSearchData,
boolean globalSearch) {
ComponentName componentName, Bundle appSearchData) {
// set up the searchable and show the dialog
if (!show(componentName, appSearchData, globalSearch)) {
if (!show(componentName, appSearchData)) {
return false;
}
@@ -335,38 +264,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
*
* @return <code>true</code> if search dialog launched
*/
private boolean show(ComponentName componentName, Bundle appSearchData,
boolean globalSearch) {
private boolean show(ComponentName componentName, Bundle appSearchData) {
if (DBG) {
Log.d(LOG_TAG, "show(" + componentName + ", "
+ appSearchData + ", " + globalSearch + ")");
+ appSearchData + ")");
}
SearchManager searchManager = (SearchManager)
mContext.getSystemService(Context.SEARCH_SERVICE);
// Try to get the searchable info for the provided component (or for global search,
// if globalSearch == true).
mSearchable = searchManager.getSearchableInfo(componentName, globalSearch);
// If we got back nothing, and it wasn't a request for global search, then try again
// for global search, as we'll try to launch that in lieu of any component-specific search.
if (!globalSearch && mSearchable == null) {
globalSearch = true;
mSearchable = searchManager.getSearchableInfo(componentName, globalSearch);
}
// Try to get the searchable info for the provided component.
mSearchable = searchManager.getSearchableInfo(componentName, false);
// If there's not even a searchable info available for global search, then really give up.
if (mSearchable == null) {
Log.w(LOG_TAG, "No global search provider.");
return false;
}
mLaunchComponent = componentName;
mAppSearchData = appSearchData;
// Using globalSearch here is just an optimization, just calling
// isDefaultSearchable() should always give the same result.
mGlobalSearchMode = globalSearch || searchManager.isDefaultSearchable(mSearchable);
mActivityContext = mSearchable.getActivityContext(getContext());
// show the dialog. this will call onStart().
@@ -375,20 +290,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// of any bad state in the AutoCompleteTextView etc
createContentView();
// The Dialog uses a ContextThemeWrapper for the context; use this to change the
// theme out from underneath us, between the global search theme and the in-app
// search theme. They are identical except that the global search theme does not
// dim the background of the window (because global search is full screen so it's
// not needed and this should save a little bit of time on global search invocation).
Object context = getContext();
if (context instanceof ContextThemeWrapper) {
ContextThemeWrapper wrapper = (ContextThemeWrapper) context;
if (globalSearch) {
wrapper.setTheme(com.android.internal.R.style.Theme_GlobalSearchBar);
} else {
wrapper.setTheme(com.android.internal.R.style.Theme_SearchBar);
}
}
show();
}
updateUI();
@@ -414,7 +315,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mSearchable = null;
mActivityContext = null;
mUserQuery = null;
mPreviousComponents = null;
}
/**
@@ -428,7 +328,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mWorkingSpinner.setVisible(working, false);
mWorkingSpinner.invalidateSelf();
}
/**
* Closes and gets rid of the suggestions adapter.
*/
@@ -442,7 +342,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
mSuggestionsAdapter = null;
}
/**
* Save the minimal set of data necessary to recreate the search
*
@@ -458,10 +358,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// setup info so I can recreate this particular search
bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent);
bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData);
bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode);
bundle.putParcelable(INSTANCE_KEY_STORED_COMPONENT, mStoredComponentName);
bundle.putBundle(INSTANCE_KEY_STORED_APPDATA, mStoredAppSearchData);
bundle.putParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS, mPreviousComponents);
bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
return bundle;
@@ -481,22 +377,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH);
ComponentName storedComponentName =
savedInstanceState.getParcelable(INSTANCE_KEY_STORED_COMPONENT);
Bundle storedAppSearchData =
savedInstanceState.getBundle(INSTANCE_KEY_STORED_APPDATA);
ArrayList<ComponentName> previousComponents =
savedInstanceState.getParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS);
String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
// Set stored state
mStoredComponentName = storedComponentName;
mStoredAppSearchData = storedAppSearchData;
mPreviousComponents = previousComponents;
// show the dialog.
if (!doShow(userQuery, false, launchComponent, appSearchData, globalSearch)) {
if (!doShow(userQuery, false, launchComponent, appSearchData)) {
// for some reason, we couldn't re-instantiate
return;
}
@@ -506,7 +390,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* Called after resources have changed, e.g. after screen rotation or locale change.
*/
public void onConfigurationChanged() {
if (isShowing()) {
if (mSearchable != null && isShowing()) {
// Redraw (resources may have changed)
updateSearchButton();
updateSearchAppIcon();
@@ -571,19 +455,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// we dismiss the entire dialog instead
mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
if (!isInRealAppSearch()) {
mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in
} else {
mSearchAutoComplete.setDropDownAlwaysVisible(false);
}
mSearchAutoComplete.setForceIgnoreOutsideTouch(true);
// attach the suggestions adapter, if suggestions are available
// The existence of a suggestions authority is the proxy for "suggestions available here"
if (mSearchable.getSuggestAuthority() != null) {
mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable,
mOutsideDrawablesCache, mGlobalSearchMode);
mOutsideDrawablesCache);
mSearchAutoComplete.setAdapter(mSuggestionsAdapter);
}
}
@@ -611,7 +489,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// global search, for extra space for url entry.
//
// TODO: Remove this special case once the issue has been reconciled in Eclair.
if (mGlobalSearchMode || isBrowserSearch()) {
if (isBrowserSearch()) {
mAppIcon.setImageResource(0);
mAppIcon.setVisibility(View.GONE);
mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL,
@@ -705,15 +583,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
/**
* Hack to determine whether this is the browser, so we can remove the browser icon
* to the left of the search field, as a special requirement for Donut.
*
* TODO: For Eclair, reconcile this with the rest of the global search UI.
* to the left of the search field.
*/
private boolean isBrowserSearch() {
return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/");
}
/**
/*
* Listeners of various types
*/
@@ -769,7 +645,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
return super.onKeyDown(keyCode, event);
}
/**
* Callback to watch the textedit field for empty/non-empty
*/
@@ -799,12 +675,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (mSearchable.autoUrlDetect() && !mSearchAutoComplete.isPerformingCompletion()) {
// The user changed the query, check if it is a URL and if so change the search
// button in the soft keyboard to the 'Go' button.
int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION));
if (Patterns.WEB_URL.matcher(mUserQuery).matches()) {
options = options | EditorInfo.IME_ACTION_GO;
} else {
options = options | EditorInfo.IME_ACTION_SEARCH;
}
int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION))
| EditorInfo.IME_ACTION_GO;
if (options != mSearchAutoCompleteImeOptions) {
mSearchAutoCompleteImeOptions = options;
mSearchAutoComplete.setImeOptions(options);
@@ -881,7 +753,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (searchable.getVoiceSearchLaunchWebSearch()) {
getContext().startActivity(mVoiceWebSearchIntent);
} else if (searchable.getVoiceSearchLaunchRecognizer()) {
Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent);
Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent,
searchable);
getContext().startActivity(appSearchIntent);
}
} catch (ActivityNotFoundException e) {
@@ -899,8 +772,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @param baseIntent The voice app search intent to start from
* @return A completely-configured intent ready to send to the voice search activity
*/
private Intent createVoiceAppSearchIntent(Intent baseIntent) {
ComponentName searchActivity = mSearchable.getSearchActivity();
private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) {
ComponentName searchActivity = searchable.getSearchActivity();
// create the necessary intent to set up a search-and-forward operation
// in the voice search system. We have to keep the bundle separate,
@@ -930,17 +803,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
String language = null;
int maxResults = 1;
Resources resources = mActivityContext.getResources();
if (mSearchable.getVoiceLanguageModeId() != 0) {
languageModel = resources.getString(mSearchable.getVoiceLanguageModeId());
if (searchable.getVoiceLanguageModeId() != 0) {
languageModel = resources.getString(searchable.getVoiceLanguageModeId());
}
if (mSearchable.getVoicePromptTextId() != 0) {
prompt = resources.getString(mSearchable.getVoicePromptTextId());
if (searchable.getVoicePromptTextId() != 0) {
prompt = resources.getString(searchable.getVoicePromptTextId());
}
if (mSearchable.getVoiceLanguageId() != 0) {
language = resources.getString(mSearchable.getVoiceLanguageId());
if (searchable.getVoiceLanguageId() != 0) {
language = resources.getString(searchable.getVoiceLanguageId());
}
if (mSearchable.getVoiceMaxResults() != 0) {
maxResults = mSearchable.getVoiceMaxResults();
if (searchable.getVoiceMaxResults() != 0) {
maxResults = searchable.getVoiceMaxResults();
}
voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
@@ -1140,14 +1013,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
*/
protected void launchQuerySearch(int actionKey, String actionMsg) {
String query = mSearchAutoComplete.getText().toString();
String action = mGlobalSearchMode ? Intent.ACTION_WEB_SEARCH : Intent.ACTION_SEARCH;
String action = Intent.ACTION_SEARCH;
Intent intent = createIntent(action, null, null, query, null,
actionKey, actionMsg, null);
// Allow GlobalSearch to log and create shortcut for searches launched by
// the search button, enter key or an action key.
if (mGlobalSearchMode) {
mSuggestionsAdapter.reportSearch(query);
}
actionKey, actionMsg);
launchIntent(intent);
}
@@ -1160,7 +1028,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
protected boolean launchSuggestion(int position) {
return launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
}
/**
* Launches an intent based on a suggestion.
*
@@ -1177,20 +1045,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
// report back about the click
if (mGlobalSearchMode) {
// in global search mode, do it via cursor
mSuggestionsAdapter.callCursorOnClick(c, position, actionKey, actionMsg);
} else if (intent != null
&& mPreviousComponents != null
&& !mPreviousComponents.isEmpty()) {
// in-app search (and we have pivoted in as told by mPreviousComponents,
// which is used for keeping track of what we pop back to when we are pivoting into
// in app search.)
reportInAppClickToGlobalSearch(c, intent);
}
// launch the intent
// launch the intent
launchIntent(intent);
return true;
@@ -1198,93 +1053,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
return false;
}
/**
* Report a click from an in app search result back to global search for shortcutting porpoises.
*
* @param c The cursor that is pointing to the clicked position.
* @param intent The intent that will be launched for the click.
*/
private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) {
// for in app search, still tell global search via content provider
Uri uri = getClickReportingUri();
final ContentValues cv = new ContentValues();
cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery);
final ComponentName source = mSearchable.getSearchActivity();
cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString());
// grab the intent columns from the intent we created since it has additional
// logic for falling back on the searchable default
cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction());
cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString());
cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME,
intent.getComponent().flattenToShortString());
// ensure the icons will work for global search
cv.put(SearchManager.SUGGEST_COLUMN_ICON_1,
wrapIconForPackage(
mSearchable.getSuggestPackage(),
getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1)));
cv.put(SearchManager.SUGGEST_COLUMN_ICON_2,
wrapIconForPackage(
mSearchable.getSuggestPackage(),
getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2)));
// the rest can be passed through directly
cv.put(SearchManager.SUGGEST_COLUMN_FORMAT,
getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT));
cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1,
getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1));
cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2,
getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2));
cv.put(SearchManager.SUGGEST_COLUMN_QUERY,
getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY));
cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID));
// note: deliberately omitting background color since it is only for global search
// "more results" entries
mContext.getContentResolver().insert(uri, cv);
}
/**
* @return A URI appropriate for reporting a click.
*/
private Uri getClickReportingUri() {
Uri.Builder uriBuilder = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY);
uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH);
return uriBuilder
.query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
.fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
.build();
}
/**
* Wraps an icon for a particular package. If the icon is a resource id, it is converted into
* an android.resource:// URI.
*
* @param packageName The source of the icon
* @param icon The icon retrieved from a suggestion column
* @return An icon string appropriate for the package.
*/
private String wrapIconForPackage(String packageName, String icon) {
if (icon == null || icon.length() == 0 || "0".equals(icon)) {
// SearchManager specifies that null or zero can be returned to indicate
// no icon. We also allow empty string.
return null;
} else if (!Character.isDigit(icon.charAt(0))){
return icon;
} else {
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(packageName)
.encodedPath(icon)
.toString();
}
}
/**
* Launches an intent, including any special intent handling.
*/
@@ -1292,70 +1060,22 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (intent == null) {
return;
}
if (handleSpecialIntent(intent)){
return;
}
Log.d(LOG_TAG, "launching " + intent);
try {
// in global search mode, we send the activity straight to the original suggestion
// source. this is because GlobalSearch may not have permission to launch the
// intent, and to avoid the extra step of going through GlobalSearch.
if (mGlobalSearchMode) {
launchGlobalSearchIntent(intent);
if (mStoredComponentName != null) {
// If we're embedded in an application, dismiss the dialog.
// This ensures that if the intent is handled by the current
// activity, it's not obscured by the dialog.
dismiss();
}
} else {
// If the intent was created from a suggestion, it will always have an explicit
// component here.
Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI());
getContext().startActivity(intent);
// If the search switches to a different activity,
// SearchDialogWrapper#performActivityResuming
// will handle hiding the dialog when the next activity starts, but for
// real in-app search, we still need to dismiss the dialog.
if (isInRealAppSearch()) {
dismiss();
}
}
// If the intent was created from a suggestion, it will always have an explicit
// component here.
Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI());
getContext().startActivity(intent);
// If the search switches to a different activity,
// SearchDialogWrapper#performActivityResuming
// will handle hiding the dialog when the next activity starts, but for
// real in-app search, we still need to dismiss the dialog.
dismiss();
} catch (RuntimeException ex) {
Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
}
}
private void launchGlobalSearchIntent(Intent intent) {
final String packageName;
// GlobalSearch puts the original source of the suggestion in the
// 'component name' column. If set, we send the intent to that activity.
// We trust GlobalSearch to always set this to the suggestion source.
String intentComponent = intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY);
if (intentComponent != null) {
ComponentName componentName = ComponentName.unflattenFromString(intentComponent);
intent.setComponent(componentName);
intent.removeExtra(SearchManager.COMPONENT_NAME_KEY);
// Launch the intent as the suggestion source.
// This prevents sources from using the search dialog to launch
// intents that they don't have permission for themselves.
packageName = componentName.getPackageName();
} else {
// If there is no component in the suggestion, it must be a built-in suggestion
// from GlobalSearch (e.g. "Search the web for") or the intent
// launched when pressing the search/go button in the search dialog.
// Launch the intent with the permissions of GlobalSearch.
packageName = mSearchable.getSearchActivity().getPackageName();
}
// Launch all global search suggestions as new tasks, since they don't relate
// to the current task.
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
setBrowserApplicationId(intent);
startActivityInPackage(intent, packageName);
}
/**
* If the intent is to open an HTTP or HTTPS URL, we set
* {@link Browser#EXTRA_APPLICATION_ID} so that any existing browser window that
@@ -1371,106 +1091,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
}
/**
* Starts an activity as if it had been started by the given package.
*
* @param intent The description of the activity to start.
* @param packageName
* @throws ActivityNotFoundException If the intent could not be resolved to
* and existing activity.
* @throws SecurityException If the package does not have permission to start
* start the activity.
* @throws AndroidRuntimeException If some other error occurs.
*/
private void startActivityInPackage(Intent intent, String packageName) {
try {
int uid = ActivityThread.getPackageManager().getPackageUid(packageName);
if (uid < 0) {
throw new AndroidRuntimeException("Package UID not found " + packageName);
}
String resolvedType = intent.resolveTypeIfNeeded(getContext().getContentResolver());
IBinder resultTo = null;
String resultWho = null;
int requestCode = -1;
boolean onlyIfNeeded = false;
Log.i(LOG_TAG, "Starting (uid " + uid + ", " + packageName + ") " + intent.toURI());
int result = ActivityManagerNative.getDefault().startActivityInPackage(
uid, intent, resolvedType, resultTo, resultWho, requestCode, onlyIfNeeded);
checkStartActivityResult(result, intent);
} catch (RemoteException ex) {
throw new AndroidRuntimeException(ex);
}
}
// Stolen from Instrumentation.checkStartActivityResult()
private static void checkStartActivityResult(int res, Intent intent) {
if (res >= IActivityManager.START_SUCCESS) {
return;
}
switch (res) {
case IActivityManager.START_INTENT_NOT_RESOLVED:
case IActivityManager.START_CLASS_NOT_FOUND:
if (intent.getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ intent.getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
case IActivityManager.START_PERMISSION_DENIED:
throw new SecurityException("Not allowed to start activity "
+ intent);
case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
throw new AndroidRuntimeException(
"FORWARD_RESULT_FLAG used while also requesting a result");
default:
throw new AndroidRuntimeException("Unknown error code "
+ res + " when starting " + intent);
}
}
/**
* Handles the special intent actions declared in {@link SearchManager}.
*
* @return <code>true</code> if the intent was handled.
*/
private boolean handleSpecialIntent(Intent intent) {
String action = intent.getAction();
if (SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.equals(action)) {
handleChangeSourceIntent(intent);
return true;
}
return false;
}
/**
* Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}.
*/
private void handleChangeSourceIntent(Intent intent) {
Uri dataUri = intent.getData();
if (dataUri == null) {
Log.w(LOG_TAG, "SearchManager.INTENT_ACTION_CHANGE_SOURCE without intent data.");
return;
}
ComponentName componentName = ComponentName.unflattenFromString(dataUri.toString());
if (componentName == null) {
Log.w(LOG_TAG, "Invalid ComponentName: " + dataUri);
return;
}
if (DBG) Log.d(LOG_TAG, "Switching to " + componentName);
pushPreviousComponent(mLaunchComponent);
if (!show(componentName, mAppSearchData, false)) {
Log.w(LOG_TAG, "Failed to switch to source " + componentName);
popPreviousComponent();
return;
}
String query = intent.getStringExtra(SearchManager.QUERY);
setUserQuery(query);
mSearchAutoComplete.showDropDown();
}
/**
* Sets the list item selection in the AutoCompleteTextView's ListView.
*/
@@ -1478,61 +1098,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mSearchAutoComplete.setListSelection(index);
}
/**
* Checks if there are any previous searchable components in the history stack.
*/
private boolean hasPreviousComponent() {
return mPreviousComponents != null && !mPreviousComponents.isEmpty();
}
/**
* Saves the previous component that was searched, so that we can go
* back to it.
*/
private void pushPreviousComponent(ComponentName componentName) {
if (mPreviousComponents == null) {
mPreviousComponents = new ArrayList<ComponentName>();
}
mPreviousComponents.add(componentName);
}
/**
* Pops the previous component off the stack and returns it.
*
* @return The component name, or <code>null</code> if there was
* no previous component.
*/
private ComponentName popPreviousComponent() {
if (!hasPreviousComponent()) {
return null;
}
return mPreviousComponents.remove(mPreviousComponents.size() - 1);
}
/**
* Goes back to the previous component that was searched, if any.
*
* @return <code>true</code> if there was a previous component that we could go back to.
*/
private boolean backToPreviousComponent() {
ComponentName previous = popPreviousComponent();
if (previous == null) {
return false;
}
if (!show(previous, mAppSearchData, false)) {
Log.w(LOG_TAG, "Failed to switch to source " + previous);
return false;
}
// must touch text to trigger suggestions
// TODO: should this be the text as it was when the user left
// the source that we are now going back to?
String query = mSearchAutoComplete.getText().toString();
setUserQuery(query);
return true;
}
/**
* When a particular suggestion has been selected, perform the various lookups required
* to use the suggestion. This includes checking the cursor for suggestion-specific data,
@@ -1582,10 +1147,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
String mode = mGlobalSearchMode ? SearchManager.MODE_GLOBAL_SEARCH_SUGGESTION : null;
return createIntent(action, dataUri, extraData, query, componentName, actionKey,
actionMsg, mode);
actionMsg);
} catch (RuntimeException e ) {
int rowNum;
try { // be really paranoid now
@@ -1616,16 +1180,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
String componentName, int actionKey, String actionMsg, String mode) {
String componentName, int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// We need CLEAR_TOP to avoid reusing an old task that has other activities
// on top of the one we want. We don't want to do this in in-app search though,
// as it can be destructive to the activity stack.
if (mGlobalSearchMode) {
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
if (data != null) {
intent.setData(data);
}
@@ -1636,9 +1197,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
if (componentName != null) {
intent.putExtra(SearchManager.COMPONENT_NAME_KEY, componentName);
}
if (mAppSearchData != null) {
intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
}
@@ -1646,16 +1204,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
if (mode != null) {
intent.putExtra(SearchManager.SEARCH_MODE, mode);
}
// Only allow 3rd-party intents from GlobalSearch
if (!mGlobalSearchMode) {
intent.setComponent(mSearchable.getSearchActivity());
}
intent.setComponent(mSearchable.getSearchActivity());
return intent;
}
/**
* For a given suggestion and a given cursor row, get the action message. If not provided
* by the specific row/column, also check for a single definition (for the action key).
@@ -1812,12 +1364,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0)) {
return;
}
// Otherwise, go back to any previous source (e.g. back to QSB when
// pivoted into a source.
if (!backToPreviousComponent()) {
// If no previous source, close search dialog
cancel();
}
// Close search dialog
cancel();
}
/**

View File

@@ -350,7 +350,7 @@ import java.util.List;
* <p><b>Configuring your Content Provider to Receive Suggestion Queries.</b> The basic job of
* a search suggestions {@link android.content.ContentProvider Content Provider} is to provide
* "live" (while-you-type) conversion of the user's query text into a set of zero or more
* suggestions. Each application is free to define the conversion, and as described above there are
* suggestions. Each application is free to define the conversion, and as described above there are
* many possible solutions. This section simply defines how to communicate with the suggestion
* provider.
*
@@ -360,7 +360,8 @@ import java.util.List;
*
* <p>Every query includes a Uri, and the Search Manager will format the Uri as shown:
* <p><pre class="prettyprint">
* content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY</pre>
* content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY
* </pre>
*
* <p>Your Content Provider can receive the query text in one of two ways.
* <ul>
@@ -379,7 +380,7 @@ import java.util.List;
* <p><b>Providing access to Content Providers that require permissions.</b> If your content
* provider declares an android:readPermission in your application's manifest, you must provide
* access to the search infrastructure to the search suggestion path by including a path-permission
* that grants android:readPermission access to "android.permission.GLOBAL_SEARCH". Granting access
* that grants android:readPermission access to "android.permission.GLOBAL_SEARCH". Granting access
* explicitly to the search infrastructure ensures it will be able to access the search suggestions
* without needing to know ahead of time any other details of the permissions protecting your
* provider. Content providers that require no permissions are already available to the search
@@ -546,7 +547,8 @@ import java.util.List;
* taken directly to a specific result.
* <ul>
* <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li>
* <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.</li>
* <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.
* </li>
* <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li>
* </ul>
* </li>
@@ -570,7 +572,7 @@ import java.util.List;
*
* <p><b>Suggestion Rewriting.</b> If the user navigates through the suggestions list, the UI
* may temporarily rewrite the user's query with a query that matches the currently selected
* suggestion. This enables the user to see what query is being suggested, and also allows the user
* suggestion. This enables the user to see what query is being suggested, and also allows the user
* to click or touch in the entry EditText element and make further edits to the query before
* dispatching it. In order to perform this correctly, the Search UI needs to know exactly what
* text to rewrite the query with.
@@ -1288,15 +1290,6 @@ public class SearchManager
*/
public final static String SEARCH_MODE = "search_mode";
/**
* Value for the {@link #SEARCH_MODE} key.
* This is used if the intent was launched by clicking a suggestion in global search
* mode (Quick Search Box).
*
* @hide
*/
public static final String MODE_GLOBAL_SEARCH_SUGGESTION = "global_search_suggestion";
/**
* Intent extra data key: Use this key with Intent.ACTION_SEARCH and
* {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()}
@@ -1307,14 +1300,6 @@ public class SearchManager
*/
public final static String ACTION_KEY = "action_key";
/**
* Intent component name key: This key will be used for the extra populated by the
* {@link #SUGGEST_COLUMN_INTENT_COMPONENT_NAME} column.
*
* {@hide}
*/
public final static String COMPONENT_NAME_KEY = "intent_component_name_key";
/**
* Intent extra data key: This key will be used for the extra populated by the
* {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column.
@@ -1328,58 +1313,6 @@ public class SearchManager
*/
public final static String EXTRA_SELECT_QUERY = "select_query";
/**
* Defines the constants used in the communication between {@link android.app.SearchDialog} and
* the global search provider via {@link Cursor#respond(android.os.Bundle)}.
*
* @hide
*/
public static class DialogCursorProtocol {
/**
* The sent bundle will contain this integer key, with a value set to one of the events
* below.
*/
public final static String METHOD = "DialogCursorProtocol.method";
/**
* After data has been refreshed.
*/
public final static int POST_REFRESH = 0;
public final static String POST_REFRESH_RECEIVE_ISPENDING
= "DialogCursorProtocol.POST_REFRESH.isPending";
public final static String POST_REFRESH_RECEIVE_DISPLAY_NOTIFY
= "DialogCursorProtocol.POST_REFRESH.displayNotify";
/**
* When a position has been clicked.
*/
public final static int CLICK = 2;
public final static String CLICK_SEND_POSITION
= "DialogCursorProtocol.CLICK.sendPosition";
public final static String CLICK_SEND_MAX_DISPLAY_POS
= "DialogCursorProtocol.CLICK.sendDisplayPosition";
public final static String CLICK_SEND_ACTION_KEY
= "DialogCursorProtocol.CLICK.sendActionKey";
public final static String CLICK_SEND_ACTION_MSG
= "DialogCursorProtocol.CLICK.sendActionMsg";
public final static String CLICK_RECEIVE_SELECTED_POS
= "DialogCursorProtocol.CLICK.receiveSelectedPosition";
/**
* When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed.
*/
public final static int THRESH_HIT = 3;
/**
* When a search is started without using a suggestion.
*/
public final static int SEARCH = 4;
public final static String SEARCH_SEND_MAX_DISPLAY_POS
= "DialogCursorProtocol.SEARCH.sendDisplayPosition";
public final static String SEARCH_SEND_QUERY = "DialogCursorProtocol.SEARCH.query";
}
/**
* Intent extra data key: Use this key with Intent.ACTION_SEARCH and
* {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
@@ -1421,40 +1354,6 @@ public class SearchManager
public final static String SHORTCUT_MIME_TYPE =
"vnd.android.cursor.item/vnd.android.search.suggest";
/**
* The authority of the provider to report clicks to when a click is detected after pivoting
* into a specific app's search from global search.
*
* In addition to the columns below, the suggestion columns are used to pass along the full
* suggestion so it can be shortcutted.
*
* @hide
*/
public final static String SEARCH_CLICK_REPORT_AUTHORITY =
"com.android.globalsearch.stats";
/**
* The path the write goes to.
*
* @hide
*/
public final static String SEARCH_CLICK_REPORT_URI_PATH = "click";
/**
* The column storing the query for the click.
*
* @hide
*/
public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query";
/**
* The column storing the component name of the application that was pivoted into.
*
* @hide
*/
public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component";
/**
* Column name for suggestions cursor. <i>Unused - can be null or column can be omitted.</i>
*/
@@ -1531,10 +1430,7 @@ public class SearchManager
*/
public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
/**
* Column name for suggestions cursor. <i>Optional.</i> This column allows suggestions
* to provide additional arbitrary data which will be included as an extra under the key
* {@link #COMPONENT_NAME_KEY}. For use by the global search system only - if other providers
* attempt to use this column, the value will be overwritten by global search.
* TODO: Remove
*
* @hide
*/
@@ -1593,27 +1489,6 @@ public class SearchManager
*/
public final static String SUGGEST_PARAMETER_LIMIT = "limit";
/**
* If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
* the search dialog will switch to a different suggestion source when the
* suggestion is clicked.
*
* {@link #SUGGEST_COLUMN_INTENT_DATA} must contain
* the flattened {@link ComponentName} of the activity which is to be searched.
*
* TODO: Should {@link #SUGGEST_COLUMN_INTENT_DATA} instead contain a URI in the format
* used by {@link android.provider.Applications}?
*
* TODO: This intent should be protected by the same permission that we use
* for replacing the global search provider.
*
* The query text field will be set to the value of {@link #SUGGEST_COLUMN_QUERY}.
*
* @hide Pending API council approval.
*/
public final static String INTENT_ACTION_CHANGE_SEARCH_SOURCE
= "android.search.action.CHANGE_SEARCH_SOURCE";
/**
* Intent action for starting the global search activity.
* The global search provider should handle this intent.
@@ -1663,7 +1538,7 @@ public class SearchManager
* @hide
*/
public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH";
/**
* Reference to the shared system search service.
*/
@@ -1671,13 +1546,6 @@ public class SearchManager
private final Context mContext;
/**
* compact representation of the activity associated with this search manager so
* we can say who we are when starting search. the search managerservice, in turn,
* uses this to properly handle the back stack.
*/
private int mIdent;
/**
* The package associated with this seach manager.
*/
@@ -1696,21 +1564,6 @@ public class SearchManager
mService = ISearchManager.Stub.asInterface(
ServiceManager.getService(Context.SEARCH_SERVICE));
}
/*package*/ boolean hasIdent() {
return mIdent != 0;
}
/*package*/ void setIdent(int ident, ComponentName component) {
if (mIdent != 0) {
throw new IllegalStateException("mIdent already set");
}
if (component == null) {
throw new IllegalArgumentException("component must be non-null");
}
mIdent = ident;
mAssociatedPackage = component.getPackageName();
}
/**
* Launch search UI.
@@ -1757,15 +1610,14 @@ public class SearchManager
ComponentName launchActivity,
Bundle appSearchData,
boolean globalSearch) {
ensureSearchDialog();
if (globalSearch) {
startGlobalSearch(initialQuery, selectInitialQuery, appSearchData);
return;
}
mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
globalSearch);
ensureSearchDialog();
mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData);
}
private void ensureSearchDialog() {
@@ -1994,17 +1846,6 @@ public class SearchManager
return null;
}
}
/**
* Checks whether the given searchable is the default searchable.
*
* @hide because SearchableInfo is not part of the API.
*/
public boolean isDefaultSearchable(SearchableInfo searchable) {
SearchableInfo defaultSearchable = getSearchableInfo(null, true);
return defaultSearchable != null
&& defaultSearchable.getSearchActivity().equals(searchable.getSearchActivity());
}
/**
* Gets a cursor with search suggestions.
@@ -2091,56 +1932,4 @@ public class SearchManager
}
}
/**
* Returns a list of the searchable activities that handle web searches.
*
* @return a list of all searchable activities that handle
* {@link android.content.Intent#ACTION_WEB_SEARCH}.
*
* @hide because SearchableInfo is not part of the API.
*/
public List<SearchableInfo> getSearchablesForWebSearch() {
try {
return mService.getSearchablesForWebSearch();
} catch (RemoteException e) {
Log.e(TAG, "getSearchablesForWebSearch() failed: " + 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 SearchableInfo getDefaultSearchableForWebSearch() {
try {
return mService.getDefaultSearchableForWebSearch();
} catch (RemoteException e) {
Log.e(TAG, "getDefaultSearchableForWebSearch() failed: " + 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 void setDefaultWebSearch(ComponentName component) {
try {
mService.setDefaultWebSearch(component);
} catch (RemoteException e) {
Log.e(TAG, "setDefaultWebSearch() failed: " + e);
}
}
private static void debug(String msg) {
Thread thread = Thread.currentThread();
Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")");
}
}

View File

@@ -16,7 +16,6 @@
package android.app;
import android.app.SearchManager.DialogCursorProtocol;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,12 +29,10 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
@@ -65,7 +62,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
private Context mProviderContext;
private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
private SparseArray<Drawable.ConstantState> mBackgroundsCache;
private boolean mGlobalSearchMode;
private boolean mClosed = false;
// Cached column indexes, updated when the cursor changes.
@@ -76,29 +72,8 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
private int mIconName2Col;
private int mBackgroundColorCol;
// The extra used to tell a cursor to close itself. This is a hack, see the description by
// its use later in this file.
private static final String EXTRA_CURSOR_RESPOND_CLOSE_CURSOR = "cursor_respond_close_cursor";
// The bundle which contains {EXTRA_CURSOR_RESPOND_CLOSE_CURSOR=true}, just cached once
// so we don't bother recreating it a bunch.
private final Bundle mCursorRespondCloseCursorBundle;
// This value is stored in SuggestionsAdapter by the SearchDialog to indicate whether
// a particular list item should be selected upon the next call to notifyDataSetChanged.
// This is used to indicate the index of the "More results..." list item so that when
// the data set changes after a click of "More results...", we can correctly tell the
// ListView to scroll to the right line item. It gets reset to NONE every time it
// is consumed.
private int mListItemToSelect = NONE;
static final int NONE = -1;
// holds the maximum position that has been displayed to the user
int mMaxDisplayed = NONE;
// holds the position that, when displayed, should result in notifying the cursor
int mDisplayNotifyPos = NONE;
private final Runnable mStartSpinnerRunnable;
private final Runnable mStopSpinnerRunnable;
@@ -110,8 +85,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
public SuggestionsAdapter(Context context, SearchDialog searchDialog,
SearchableInfo searchable,
WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache,
boolean globalSearchMode) {
WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache) {
super(context,
com.android.internal.R.layout.search_dropdown_item_icons_2line,
null, // no initial cursor
@@ -126,7 +100,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
mOutsideDrawablesCache = outsideDrawablesCache;
mBackgroundsCache = new SparseArray<Drawable.ConstantState>();
mGlobalSearchMode = globalSearchMode;
mStartSpinnerRunnable = new Runnable() {
public void run() {
@@ -140,10 +113,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
}
};
// Create this once because we'll reuse it a bunch.
mCursorRespondCloseCursorBundle = new Bundle();
mCursorRespondCloseCursorBundle.putBoolean(EXTRA_CURSOR_RESPOND_CLOSE_CURSOR, true);
// delay 500ms when deleting
getFilter().setDelayer(new Filter.Delayer() {
@@ -177,27 +146,22 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")");
String query = (constraint == null) ? "" : constraint.toString();
if (!mGlobalSearchMode) {
/**
* for in app search we show the progress spinner until the cursor is returned with
* the results. for global search we manage the progress bar using
* {@link DialogCursorProtocol#POST_REFRESH_RECEIVE_ISPENDING}.
*/
mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
}
/**
* for in app search we show the progress spinner until the cursor is returned with
* the results.
*/
mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
try {
final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
// trigger fill window so the spinner stays up until the results are copied over and
// closer to being ready
if (!mGlobalSearchMode && cursor != null) cursor.getCount();
if (cursor != null) cursor.getCount();
return cursor;
} catch (RuntimeException e) {
Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
return null;
} finally {
if (!mGlobalSearchMode) {
mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
}
mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
}
}
@@ -221,21 +185,8 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
}
try {
Cursor oldCursor = getCursor();
super.changeCursor(c);
// We send a special respond to the cursor to tell it to close itself directly because
// it may not happen correctly for some cursors currently. This was originally
// included as a fix to http://b/2036290, in which the search dialog was holding
// on to references to the web search provider unnecessarily. This is being caused by
// the fact that the cursor is not being correctly closed in
// BulkCursorToCursorAdapter#close, which remains unfixed (see http://b/2015069).
//
// TODO: Remove this hack once http://b/2015069 is fixed.
if (oldCursor != null && oldCursor != c) {
oldCursor.respond(mCursorRespondCloseCursorBundle);
}
if (c != null) {
mFormatCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT);
mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
@@ -249,79 +200,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
}
}
@Override
public void notifyDataSetChanged() {
if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
super.notifyDataSetChanged();
callCursorPostRefresh(mCursor);
// look out for the pending item we are supposed to scroll to
if (mListItemToSelect != NONE) {
mSearchDialog.setListSelection(mListItemToSelect);
mListItemToSelect = NONE;
}
}
/**
* Handle sending and receiving information associated with
* {@link DialogCursorProtocol#POST_REFRESH}.
*
* @param cursor The cursor to call.
*/
private void callCursorPostRefresh(Cursor cursor) {
if (!mGlobalSearchMode) return;
final Bundle request = new Bundle();
request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.POST_REFRESH);
final Bundle response = cursor.respond(request);
mSearchDialog.setWorking(
response.getBoolean(DialogCursorProtocol.POST_REFRESH_RECEIVE_ISPENDING, false));
mDisplayNotifyPos =
response.getInt(DialogCursorProtocol.POST_REFRESH_RECEIVE_DISPLAY_NOTIFY, -1);
}
/**
* Tell the cursor which position was clicked, handling sending and receiving information
* associated with {@link DialogCursorProtocol#CLICK}.
*
* @param cursor The cursor
* @param position The position that was clicked.
*/
void callCursorOnClick(Cursor cursor, int position, int actionKey, String actionMsg) {
if (!mGlobalSearchMode) return;
final Bundle request = new Bundle(5);
request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.CLICK);
request.putInt(DialogCursorProtocol.CLICK_SEND_POSITION, position);
request.putInt(DialogCursorProtocol.CLICK_SEND_MAX_DISPLAY_POS, mMaxDisplayed);
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
request.putInt(DialogCursorProtocol.CLICK_SEND_ACTION_KEY, actionKey);
request.putString(DialogCursorProtocol.CLICK_SEND_ACTION_MSG, actionMsg);
}
final Bundle response = cursor.respond(request);
mMaxDisplayed = -1;
mListItemToSelect = response.getInt(
DialogCursorProtocol.CLICK_RECEIVE_SELECTED_POS, SuggestionsAdapter.NONE);
}
/**
* Tell the cursor that a search was started without using a suggestion.
*
* @param query The search query.
*/
void reportSearch(String query) {
if (!mGlobalSearchMode) return;
Cursor cursor = getCursor();
if (cursor == null) return;
final Bundle request = new Bundle(3);
request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.SEARCH);
request.putString(DialogCursorProtocol.SEARCH_SEND_QUERY, query);
request.putInt(DialogCursorProtocol.SEARCH_SEND_MAX_DISPLAY_POS, mMaxDisplayed);
// the response is always empty
cursor.respond(request);
}
/**
* Tags the view with cached child view look-ups.
*/
@@ -353,20 +231,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
@Override
public void bindView(View view, Context context, Cursor cursor) {
ChildViewCache views = (ChildViewCache) view.getTag();
final int pos = cursor.getPosition();
// update the maximum position displayed since last refresh
if (pos > mMaxDisplayed) {
mMaxDisplayed = pos;
}
// if the cursor wishes to be notified about this position, send it
if (mGlobalSearchMode && mDisplayNotifyPos != NONE && pos == mDisplayNotifyPos) {
final Bundle request = new Bundle();
request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.THRESH_HIT);
mCursor.respond(request);
mDisplayNotifyPos = NONE; // only notify the first time
}
int backgroundColor = 0;
if (mBackgroundColorCol != -1) {

View File

@@ -120,23 +120,6 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct
assertSame(searchManager1, searchManager2 );
}
@MediumTest
public void testSearchables() {
SearchManager searchManager = (SearchManager)
mContext.getSystemService(Context.SEARCH_SERVICE);
SearchableInfo si;
si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, false);
assertNotNull(si);
assertFalse(searchManager.isDefaultSearchable(si));
si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, true);
assertNotNull(si);
assertTrue(searchManager.isDefaultSearchable(si));
si = searchManager.getSearchableInfo(null, true);
assertNotNull(si);
assertTrue(searchManager.isDefaultSearchable(si));
}
/**
* Tests that startSearch() can be called multiple times without stopSearch()
* in between.