diff options
Diffstat (limited to 'src/com/android/launcher/Search.java')
-rw-r--r-- | src/com/android/launcher/Search.java | 668 |
1 files changed, 0 insertions, 668 deletions
diff --git a/src/com/android/launcher/Search.java b/src/com/android/launcher/Search.java deleted file mode 100644 index d33fd69fa..000000000 --- a/src/com/android/launcher/Search.java +++ /dev/null @@ -1,668 +0,0 @@ -/* - * 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; - -import java.util.List; - -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(); - - /** - * 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); - } - - /** - * 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); - } - - /** - * 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()); - } - } - - /** - * 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; - } - } - - } -} |