summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher/Search.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:32:27 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:32:27 -0800
commit31dd503c6aa69018e694d91724d46db49ea09327 (patch)
tree9948995382d55906a8f4d06eaa4a320d700c4234 /src/com/android/launcher/Search.java
parent31896793701b36714f040d4fe1b32426c68d5427 (diff)
downloadandroid_packages_apps_Trebuchet-31dd503c6aa69018e694d91724d46db49ea09327.tar.gz
android_packages_apps_Trebuchet-31dd503c6aa69018e694d91724d46db49ea09327.tar.bz2
android_packages_apps_Trebuchet-31dd503c6aa69018e694d91724d46db49ea09327.zip
auto import from //depot/cupcake/@135843
Diffstat (limited to 'src/com/android/launcher/Search.java')
-rw-r--r--src/com/android/launcher/Search.java702
1 files changed, 702 insertions, 0 deletions
diff --git a/src/com/android/launcher/Search.java b/src/com/android/launcher/Search.java
new file mode 100644
index 000000000..296b902ef
--- /dev/null
+++ b/src/com/android/launcher/Search.java
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher;
+
+import android.app.ISearchManager;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.server.search.SearchableInfo;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
+import android.view.View.OnLongClickListener;
+import android.widget.AdapterView;
+import android.widget.AutoCompleteTextView;
+import android.widget.CursorAdapter;
+import android.widget.Filter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+public class Search extends LinearLayout implements OnClickListener, OnKeyListener,
+ OnLongClickListener, TextWatcher, OnItemClickListener, OnItemSelectedListener {
+
+ private final String TAG = "SearchGadget";
+
+ private AutoCompleteTextView mSearchText;
+ private ImageButton mGoButton;
+ private ImageButton mVoiceButton;
+ private OnLongClickListener mLongClickListener;
+
+ // Support for suggestions
+ private SuggestionsAdapter mSuggestionsAdapter;
+ private SearchableInfo mSearchable;
+ private String mSuggestionAction = null;
+ private Uri mSuggestionData = null;
+ private String mSuggestionQuery = null;
+ private int mItemSelected = -1;
+
+ // For voice searching
+ private Intent mVoiceSearchIntent;
+
+ private Rect mTempRect = new Rect();
+ private boolean mRestoreFocus = false;
+
+ /**
+ * Used to inflate the Workspace from XML.
+ *
+ * @param context The application's context.
+ * @param attrs The attributes set containing the Workspace's customization values.
+ */
+ public Search(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mVoiceSearchIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+ mVoiceSearchIntent.putExtra(android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ android.speech.RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
+ }
+
+ /**
+ * Implements OnClickListener (for button)
+ */
+ public void onClick(View v) {
+ if (v == mGoButton) {
+ query();
+ } else if (v == mVoiceButton) {
+ try {
+ getContext().startActivity(mVoiceSearchIntent);
+ } catch (ActivityNotFoundException ex) {
+ // Should not happen, since we check the availability of
+ // voice search before showing the button. But just in case...
+ Log.w(TAG, "Could not find voice search activity");
+ }
+ }
+ }
+
+ private void query() {
+ String query = mSearchText.getText().toString();
+ if (TextUtils.getTrimmedLength(mSearchText.getText()) == 0) {
+ return;
+ }
+ Bundle appData = new Bundle();
+ appData.putString(SearchManager.SOURCE, "launcher-widget");
+ sendLaunchIntent(Intent.ACTION_SEARCH, null, query, appData, 0, null, mSearchable);
+ clearQuery();
+ }
+
+ /**
+ * Assemble a search intent and send it.
+ *
+ * This is copied from SearchDialog.
+ *
+ * @param action The intent to send, typically Intent.ACTION_SEARCH
+ * @param data The data for the intent
+ * @param query The user text entered (so far)
+ * @param appData The app data bundle (if supplied)
+ * @param actionKey If the intent was triggered by an action key, e.g. KEYCODE_CALL, it will
+ * be sent here. Pass KeyEvent.KEYCODE_UNKNOWN for no actionKey code.
+ * @param actionMsg If the intent was triggered by an action key, e.g. KEYCODE_CALL, the
+ * corresponding tag message will be sent here. Pass null for no actionKey message.
+ * @param si Reference to the current SearchableInfo. Passed here so it can be used even after
+ * we've called dismiss(), which attempts to null mSearchable.
+ */
+ private void sendLaunchIntent(final String action, final Uri data, final String query,
+ final Bundle appData, int actionKey, final String actionMsg, final SearchableInfo si) {
+ Intent launcher = new Intent(action);
+ launcher.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ if (query != null) {
+ launcher.putExtra(SearchManager.QUERY, query);
+ }
+
+ if (data != null) {
+ launcher.setData(data);
+ }
+
+ if (appData != null) {
+ launcher.putExtra(SearchManager.APP_DATA, appData);
+ }
+
+ // add launch info (action key, etc.)
+ if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
+ launcher.putExtra(SearchManager.ACTION_KEY, actionKey);
+ launcher.putExtra(SearchManager.ACTION_MSG, actionMsg);
+ }
+
+ // attempt to enforce security requirement (no 3rd-party intents)
+ if (si != null) {
+ launcher.setComponent(si.mSearchActivity);
+ }
+
+ getContext().startActivity(launcher);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ if (!hasWindowFocus && hasFocus()) {
+ mRestoreFocus = true;
+ }
+
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ if (hasWindowFocus && mRestoreFocus) {
+ if (isInTouchMode()) {
+ final AutoCompleteTextView searchText = mSearchText;
+ searchText.setSelectAllOnFocus(false);
+ searchText.requestFocusFromTouch();
+ searchText.setSelectAllOnFocus(true);
+ }
+ mRestoreFocus = false;
+ }
+ }
+
+ /**
+ * Implements TextWatcher (for EditText)
+ */
+ public void beforeTextChanged(CharSequence s, int start, int before, int after) {
+ }
+
+ /**
+ * Implements TextWatcher (for EditText)
+ */
+ public void onTextChanged(CharSequence s, int start, int before, int after) {
+ // enable the button if we have one or more non-space characters
+ boolean enabled = TextUtils.getTrimmedLength(mSearchText.getText()) != 0;
+ mGoButton.setEnabled(enabled);
+ mGoButton.setFocusable(enabled);
+ }
+
+ /**
+ * Implements TextWatcher (for EditText)
+ */
+ public void afterTextChanged(Editable s) {
+ }
+
+ /**
+ * Implements OnKeyListener (for EditText and for button)
+ *
+ * This plays some games with state in order to "soften" the strength of suggestions
+ * presented. Suggestions should not be used unless the user specifically navigates to them
+ * (or clicks them, in which case it's obvious). This is not the way that AutoCompleteTextBox
+ * normally works.
+ */
+ public final boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (v == mSearchText) {
+ boolean searchTrigger = (keyCode == KeyEvent.KEYCODE_ENTER ||
+ keyCode == KeyEvent.KEYCODE_SEARCH ||
+ keyCode == KeyEvent.KEYCODE_DPAD_CENTER);
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+// Log.d(TAG, "onKey() ACTION_UP isPopupShowing:" + mSearchText.isPopupShowing());
+ if (!mSearchText.isPopupShowing()) {
+ if (searchTrigger) {
+ query();
+ return true;
+ }
+ }
+ } else {
+// Log.d(TAG, "onKey() ACTION_DOWN isPopupShowing:" + mSearchText.isPopupShowing() +
+// " mItemSelected="+ mItemSelected);
+ if (searchTrigger && mItemSelected < 0) {
+ query();
+ return true;
+ }
+ }
+ } else if (v == mGoButton || v == mVoiceButton) {
+ boolean handled = false;
+ if (!event.isSystem() &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_DOWN) &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_LEFT) &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_RIGHT) &&
+ (keyCode != KeyEvent.KEYCODE_DPAD_CENTER)) {
+ if (mSearchText.requestFocus()) {
+ handled = mSearchText.dispatchKeyEvent(event);
+ }
+ }
+ return handled;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setOnLongClickListener(OnLongClickListener l) {
+ super.setOnLongClickListener(l);
+ mLongClickListener = l;
+ }
+
+ /**
+ * Implements OnLongClickListener (for button)
+ */
+ public boolean onLongClick(View v) {
+ // Pretend that a long press on a child view is a long press on the search widget
+ if (mLongClickListener != null) {
+ return mLongClickListener.onLongClick(this);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // Request focus unless the user tapped on the voice search button
+ final int x = (int) ev.getX();
+ final int y = (int) ev.getY();
+ final Rect frame = mTempRect;
+ mVoiceButton.getHitRect(frame);
+ if (!frame.contains(x, y)) {
+ requestFocusFromTouch();
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ /**
+ * In order to keep things simple, the external trigger will clear the query just before
+ * focusing, so as to give you a fresh query. This way we eliminate any sources of
+ * accidental query launching.
+ */
+ public void clearQuery() {
+ mSearchText.setText(null);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mSearchText = (AutoCompleteTextView) findViewById(R.id.input);
+ // TODO: This can be confusing when the user taps the text field to give the focus
+ // (it is not necessary but I ran into this issue several times myself)
+ // mTitleInput.setOnClickListener(this);
+ mSearchText.setOnKeyListener(this);
+ mSearchText.addTextChangedListener(this);
+
+ mGoButton = (ImageButton) findViewById(R.id.search_go_btn);
+ mVoiceButton = (ImageButton) findViewById(R.id.search_voice_btn);
+ mGoButton.setOnClickListener(this);
+ mVoiceButton.setOnClickListener(this);
+ mGoButton.setOnKeyListener(this);
+ mVoiceButton.setOnKeyListener(this);
+
+ mSearchText.setOnLongClickListener(this);
+ mGoButton.setOnLongClickListener(this);
+ mVoiceButton.setOnLongClickListener(this);
+
+ // disable the button since we start out w/empty input
+ mGoButton.setEnabled(false);
+ mGoButton.setFocusable(false);
+
+ configureSearchableInfo();
+ configureSuggestions();
+ configureVoiceSearchButton();
+ }
+
+ /**
+ * Read the searchable info from the search manager
+ */
+ private void configureSearchableInfo() {
+ ISearchManager sms;
+ SearchableInfo searchable;
+ sms = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
+ try {
+ // TODO null isn't the published use of this API, but it works when global=true
+ // TODO better implementation: defer all of this, let Home set it up
+ searchable = sms.getSearchableInfo(null, true);
+ } catch (RemoteException e) {
+ searchable = null;
+ }
+ if (searchable == null) {
+ // no suggestions so just get out (no need to continue)
+ return;
+ }
+ mSearchable = searchable;
+ }
+
+ /**
+ * If appropriate & available, configure voice search
+ *
+ * Note: Because the home screen search widget is always web search, we only check for
+ * getVoiceSearchLaunchWebSearch() modes. We don't support the alternate form of app-specific
+ * voice search.
+ */
+ private void configureVoiceSearchButton() {
+ boolean voiceSearchVisible = false;
+ if (mSearchable.getVoiceSearchEnabled() && mSearchable.getVoiceSearchLaunchWebSearch()) {
+ // Enable the voice search button if there is an activity that can handle it
+ PackageManager pm = getContext().getPackageManager();
+ ResolveInfo ri = pm.resolveActivity(mVoiceSearchIntent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ voiceSearchVisible = ri != null;
+ }
+
+ // finally, set visible state of voice search button, as appropriate
+ mVoiceButton.setVisibility(voiceSearchVisible ? View.VISIBLE : View.GONE);
+ }
+
+ /** The rest of the class deals with providing search suggestions */
+
+ /**
+ * Set up the suggestions provider mechanism
+ */
+ private void configureSuggestions() {
+ // get SearchableInfo
+
+ mSearchText.setOnItemClickListener(this);
+ mSearchText.setOnItemSelectedListener(this);
+
+ // attach the suggestions adapter
+ mSuggestionsAdapter = new SuggestionsAdapter(mContext,
+ com.android.internal.R.layout.search_dropdown_item_2line, null,
+ SuggestionsAdapter.TWO_LINE_FROM, SuggestionsAdapter.TWO_LINE_TO, mSearchable);
+ mSearchText.setAdapter(mSuggestionsAdapter);
+ }
+
+ /**
+ * Remove internal cursor references when detaching from window which
+ * prevents {@link Context} leaks.
+ */
+ @Override
+ public void onDetachedFromWindow() {
+ if (mSuggestionsAdapter != null) {
+ mSuggestionsAdapter.changeCursor(null);
+ mSuggestionsAdapter = null;
+ }
+ }
+
+ /**
+ * Implements OnItemClickListener
+ */
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+// Log.d(TAG, "onItemClick() position " + position);
+ launchSuggestion(mSuggestionsAdapter, position);
+ }
+
+ /**
+ * Implements OnItemSelectedListener
+ */
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+// Log.d(TAG, "onItemSelected() position " + position);
+ mItemSelected = position;
+ }
+
+ /**
+ * Implements OnItemSelectedListener
+ */
+ public void onNothingSelected(AdapterView<?> parent) {
+// Log.d(TAG, "onNothingSelected()");
+ mItemSelected = -1;
+ }
+
+ /**
+ * Code to launch a suggestion query.
+ *
+ * This is copied from SearchDialog.
+ *
+ * @param ca The CursorAdapter containing the suggestions
+ * @param position The suggestion we'll be launching from
+ *
+ * @return Returns true if a successful launch, false if could not (e.g. bad position)
+ */
+ private boolean launchSuggestion(CursorAdapter ca, int position) {
+ if (ca != null) {
+ Cursor c = ca.getCursor();
+ if ((c != null) && c.moveToPosition(position)) {
+ setupSuggestionIntent(c, mSearchable);
+
+ SearchableInfo si = mSearchable;
+ String suggestionAction = mSuggestionAction;
+ Uri suggestionData = mSuggestionData;
+ String suggestionQuery = mSuggestionQuery;
+ sendLaunchIntent(suggestionAction, suggestionData, suggestionQuery, null,
+ KeyEvent.KEYCODE_UNKNOWN, null, si);
+ clearQuery();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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,
+ * and/or falling back to the XML for defaults; It also creates REST style Uri data when
+ * the suggestion includes a data id.
+ *
+ * NOTE: Return values are in member variables mSuggestionAction, mSuggestionData and
+ * mSuggestionQuery.
+ *
+ * This is copied from SearchDialog.
+ *
+ * @param c The suggestions cursor, moved to the row of the user's selection
+ * @param si The searchable activity's info record
+ */
+ void setupSuggestionIntent(Cursor c, SearchableInfo si) {
+ try {
+ // use specific action if supplied, or default action if supplied, or fixed default
+ mSuggestionAction = null;
+ int column = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
+ if (column >= 0) {
+ final String action = c.getString(column);
+ if (action != null) {
+ mSuggestionAction = action;
+ }
+ }
+ if (mSuggestionAction == null) {
+ mSuggestionAction = si.getSuggestIntentAction();
+ }
+ if (mSuggestionAction == null) {
+ mSuggestionAction = Intent.ACTION_SEARCH;
+ }
+
+ // use specific data if supplied, or default data if supplied
+ String data = null;
+ column = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA);
+ if (column >= 0) {
+ final String rowData = c.getString(column);
+ if (rowData != null) {
+ data = rowData;
+ }
+ }
+ if (data == null) {
+ data = si.getSuggestIntentData();
+ }
+
+ // then, if an ID was provided, append it.
+ if (data != null) {
+ column = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
+ if (column >= 0) {
+ final String id = c.getString(column);
+ if (id != null) {
+ data = data + "/" + Uri.encode(id);
+ }
+ }
+ }
+ mSuggestionData = (data == null) ? null : Uri.parse(data);
+
+ mSuggestionQuery = null;
+ column = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
+ if (column >= 0) {
+ final String query = c.getString(column);
+ if (query != null) {
+ mSuggestionQuery = query;
+ }
+ }
+ } catch (RuntimeException e ) {
+ int rowNum;
+ try { // be really paranoid now
+ rowNum = c.getPosition();
+ } catch (RuntimeException e2 ) {
+ rowNum = -1;
+ }
+ Log.w(TAG, "Search Suggestions cursor at row " + rowNum +
+ " returned exception" + e.toString());
+ }
+ }
+
+ SearchAutoCompleteTextView getSearchInputField() {
+ return (SearchAutoCompleteTextView) mSearchText;
+ }
+
+ /**
+ * This class provides the filtering-based interface to suggestions providers.
+ * It is hardwired in a couple of places to support GoogleSearch - for example, it supports
+ * two-line suggestions, but it does not support icons.
+ */
+ private static class SuggestionsAdapter extends SimpleCursorAdapter {
+ public final static String[] TWO_LINE_FROM = {SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2 };
+ public final static int[] TWO_LINE_TO = {com.android.internal.R.id.text1,
+ com.android.internal.R.id.text2};
+
+ private final String TAG = "SuggestionsAdapter";
+
+ Filter mFilter;
+ SearchableInfo mSearchable;
+ private Resources mProviderResources;
+ String[] mFromStrings;
+
+ public SuggestionsAdapter(Context context, int layout, Cursor c,
+ String[] from, int[] to, SearchableInfo searchable) {
+ super(context, layout, c, from, to);
+ mFromStrings = from;
+ mSearchable = searchable;
+
+ // set up provider resources (gives us icons, etc.)
+ Context activityContext = mSearchable.getActivityContext(mContext);
+ Context providerContext = mSearchable.getProviderContext(mContext, activityContext);
+ mProviderResources = providerContext.getResources();
+ }
+
+ /**
+ * Use the search suggestions provider to obtain a live cursor. This will be called
+ * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions).
+ * The results will be processed in the UI thread and changeCursor() will be called.
+ */
+ @Override
+ public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
+ String query = (constraint == null) ? "" : constraint.toString();
+ return getSuggestions(mSearchable, query);
+ }
+
+ /**
+ * Overriding this allows us to write the selected query back into the box.
+ * NOTE: This is a vastly simplified version of SearchDialog.jamQuery() and does
+ * not universally support the search API. But it is sufficient for Google Search.
+ */
+ @Override
+ public CharSequence convertToString(Cursor cursor) {
+ CharSequence result = null;
+ if (cursor != null) {
+ int column = cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY);
+ if (column >= 0) {
+ final String query = cursor.getString(column);
+ if (query != null) {
+ result = query;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get the query cursor for the search suggestions.
+ *
+ * TODO this is functionally identical to the version in SearchDialog.java. Perhaps it
+ * could be hoisted into SearchableInfo or some other shared spot.
+ *
+ * @param query The search text entered (so far)
+ * @return Returns a cursor with suggestions, or null if no suggestions
+ */
+ private Cursor getSuggestions(final SearchableInfo searchable, final String query) {
+ Cursor cursor = null;
+ if (searchable.getSuggestAuthority() != null) {
+ try {
+ StringBuilder uriStr = new StringBuilder("content://");
+ uriStr.append(searchable.getSuggestAuthority());
+
+ // if content path provided, insert it now
+ final String contentPath = searchable.getSuggestPath();
+ if (contentPath != null) {
+ uriStr.append('/');
+ uriStr.append(contentPath);
+ }
+
+ // append standard suggestion query path
+ uriStr.append('/' + SearchManager.SUGGEST_URI_PATH_QUERY);
+
+ // inject query, either as selection args or inline
+ String[] selArgs = null;
+ if (searchable.getSuggestSelection() != null) { // use selection if provided
+ selArgs = new String[] {query};
+ } else {
+ uriStr.append('/'); // no sel, use REST pattern
+ uriStr.append(Uri.encode(query));
+ }
+
+ // finally, make the query
+ cursor = mContext.getContentResolver().query(
+ Uri.parse(uriStr.toString()), null,
+ searchable.getSuggestSelection(), selArgs,
+ null);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Search Suggestions query returned exception " + e.toString());
+ cursor = null;
+ }
+ }
+
+ return cursor;
+ }
+
+ /**
+ * Overriding this allows us to affect the way that an icon is loaded. Specifically,
+ * we can be more controlling about the resource path (and allow icons to come from other
+ * packages).
+ *
+ * TODO: This is 100% identical to the version in SearchDialog.java
+ *
+ * @param v ImageView to receive an image
+ * @param value the value retrieved from the cursor
+ */
+ @Override
+ public void setViewImage(ImageView v, String value) {
+ int resID;
+ Drawable img = null;
+
+ try {
+ resID = Integer.parseInt(value);
+ if (resID != 0) {
+ img = mProviderResources.getDrawable(resID);
+ }
+ } catch (NumberFormatException nfe) {
+ // img = null;
+ } catch (NotFoundException e2) {
+ // img = null;
+ }
+
+ // finally, set the image to whatever we've gotten
+ v.setImageDrawable(img);
+ }
+
+ /**
+ * This method is overridden purely to provide a bit of protection against
+ * flaky content providers.
+ *
+ * TODO: This is 100% identical to the version in SearchDialog.java
+ *
+ * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ try {
+ return super.getView(position, convertView, parent);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Search Suggestions cursor returned exception " + e.toString());
+ // what can I return here?
+ View v = newView(mContext, mCursor, parent);
+ if (v != null) {
+ TextView tv = (TextView) v.findViewById(com.android.internal.R.id.text1);
+ tv.setText(e.toString());
+ }
+ return v;
+ }
+ }
+
+ }
+}