diff options
Diffstat (limited to 'src/com/android/launcher/Search.java')
-rw-r--r-- | src/com/android/launcher/Search.java | 823 |
1 files changed, 226 insertions, 597 deletions
diff --git a/src/com/android/launcher/Search.java b/src/com/android/launcher/Search.java index 71ab7ef44..3d0947987 100644 --- a/src/com/android/launcher/Search.java +++ b/src/com/android/launcher/Search.java @@ -16,71 +16,59 @@ 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.content.res.Configuration; 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.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.Interpolator; +import android.view.animation.Transformation; +import android.view.inputmethod.InputMethodManager; 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 { +public class Search extends LinearLayout + implements OnClickListener, OnKeyListener, OnLongClickListener { + + // Speed at which the widget slides up/down, in pixels/ms. + private static final float ANIMATION_VELOCITY = 1.0f; private final String TAG = "SearchWidget"; - private AutoCompleteTextView mSearchText; - private ImageButton mGoButton; + private Launcher mLauncher; + + private TextView mSearchText; 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; - + + /** The animation that morphs the search widget to the search dialog. */ + private Animation mMorphAnimation; + + /** The animation that morphs the search widget back to its normal position. */ + private Animation mUnmorphAnimation; + + // These four are passed to Launcher.startSearch() when the search widget + // has finished morphing. They are instance variables to make it possible to update + // them while the widget is morphing. + private String mInitialQuery; + private boolean mSelectInitialQuery; + private Bundle mAppSearchData; + private boolean mGlobalSearch; + // For voice searching private Intent mVoiceSearchIntent; - private Rect mTempRect = new Rect(); - private boolean mRestoreFocus = false; - /** * Used to inflate the Workspace from XML. * @@ -89,293 +77,235 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen */ public Search(Context context, AttributeSet attrs) { super(context, attrs); + + Interpolator interpolator = new AccelerateDecelerateInterpolator(); + + mMorphAnimation = new ToParentOriginAnimation(); + // no need to apply transformation before the animation starts, + // since the gadget is already in its normal place. + mMorphAnimation.setFillBefore(false); + // stay in the top position after the animation finishes + mMorphAnimation.setFillAfter(true); + mMorphAnimation.setInterpolator(interpolator); + mMorphAnimation.setAnimationListener(new Animation.AnimationListener() { + // The amount of time before the animation ends to show the search dialog. + private static final long TIME_BEFORE_ANIMATION_END = 80; + + // The runnable which we'll pass to our handler to show the search dialog. + private final Runnable mShowSearchDialogRunnable = new Runnable() { + public void run() { + showSearchDialog(); + } + }; + + public void onAnimationEnd(Animation animation) { } + public void onAnimationRepeat(Animation animation) { } + public void onAnimationStart(Animation animation) { + // Make the search dialog show up ideally *just* as the animation reaches + // the top, to aid the illusion that the widget becomes the search dialog. + // Otherwise, there is a short delay when the widget reaches the top before + // the search dialog shows. We do this roughly 80ms before the animation ends. + getHandler().postDelayed( + mShowSearchDialogRunnable, + Math.max(mMorphAnimation.getDuration() - TIME_BEFORE_ANIMATION_END, 0)); + } + }); + + mUnmorphAnimation = new FromParentOriginAnimation(); + // stay in the top position until the animation starts + mUnmorphAnimation.setFillBefore(true); + // no need to apply transformation after the animation finishes, + // since the gadget is now back in its normal place. + mUnmorphAnimation.setFillAfter(false); + mUnmorphAnimation.setInterpolator(interpolator); + mUnmorphAnimation.setAnimationListener(new Animation.AnimationListener(){ + public void onAnimationEnd(Animation animation) { + clearAnimation(); + } + public void onAnimationRepeat(Animation animation) { } + public void onAnimationStart(Animation animation) { } + }); 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) + * Implements OnClickListener. */ 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"); - } + if (v == mVoiceButton) { + startVoiceSearch(); + } else { + mLauncher.onSearchRequested(); } } - private void query() { - String query = mSearchText.getText().toString(); - if (TextUtils.getTrimmedLength(mSearchText.getText()) == 0) { - return; + private void startVoiceSearch() { + 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"); } - 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. + * Sets the query text. The query field is not editable, instead we forward + * the key events to the launcher, which keeps track of the text, + * calls setQuery() to show it, and gives it to the search dialog. */ - 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); + public void setQuery(String query) { + mSearchText.setText(query, TextView.BufferType.NORMAL); } - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - if (!hasWindowFocus && hasFocus()) { - mRestoreFocus = true; + /** + * Morph the search gadget to the search dialog. + * See {@link Activity.startSearch()} for the arguments. + */ + public void startSearch(String initialQuery, boolean selectInitialQuery, + Bundle appSearchData, boolean globalSearch) { + mInitialQuery = initialQuery; + mSelectInitialQuery = selectInitialQuery; + mAppSearchData = appSearchData; + mGlobalSearch = globalSearch; + + // Call up the keyboard before we actually call the search dialog so that it + // (hopefully) animates in at about the same time as the widget animation, and + // so that it becomes available as soon as possible. Only do this if a hard + // keyboard is not currently available. + if (getContext().getResources().getConfiguration().hardKeyboardHidden == + Configuration.HARDKEYBOARDHIDDEN_YES) { + // Make sure the text field is not focusable, so it's not responsible for + // causing the whole view to shift up to accommodate the keyboard. + mSearchText.setFocusable(false); + + InputMethodManager inputManager = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + inputManager.showSoftInputUnchecked(0, null); } - - super.onWindowFocusChanged(hasWindowFocus); - - if (hasWindowFocus && mRestoreFocus) { - if (isInTouchMode()) { - final AutoCompleteTextView searchText = mSearchText; - searchText.setSelectAllOnFocus(false); - searchText.requestFocusFromTouch(); - searchText.setSelectAllOnFocus(true); + + if (isAtTop()) { + showSearchDialog(); + } else { + // Start the animation, unless it has already started. + if (getAnimation() != mMorphAnimation) { + mMorphAnimation.setDuration(getAnimationDuration()); + startAnimation(mMorphAnimation); } - mRestoreFocus = false; } } /** - * Implements TextWatcher (for EditText) + * Shows the system search dialog immediately, without any animation. */ - public void beforeTextChanged(CharSequence s, int start, int before, int after) { + private void showSearchDialog() { + mLauncher.showSearchDialog( + mInitialQuery, mSelectInitialQuery, mAppSearchData, mGlobalSearch); } /** - * Implements TextWatcher (for EditText) + * Restore the search gadget to its normal position. + * + * @param animate Whether to animate the movement of the gadget. */ - 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); + public void stopSearch(boolean animate) { + setQuery(""); + + // Set the search field back to focusable after making it unfocusable in + // startSearch, so that the home screen doesn't try to shift around when the + // keyboard comes up. + mSearchText.setFocusable(true); + // Only restore if we are not already restored. + if (getAnimation() == mMorphAnimation) { + if (animate && !isAtTop()) { + mUnmorphAnimation.setDuration(getAnimationDuration()); + startAnimation(mUnmorphAnimation); + } else { + clearAnimation(); + } + } } - /** - * Implements TextWatcher (for EditText) - */ - public void afterTextChanged(Editable s) { + private boolean isAtTop() { + return getTop() == 0; + } + + private int getAnimationDuration() { + return (int) (getTop() / ANIMATION_VELOCITY); } /** - * 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. + * Modify clearAnimation() to invalidate the parent. This works around + * an issue where the region where the end of the animation placed the view + * was not redrawn after clearing the animation. */ - 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; - } - } + @Override + public void clearAnimation() { + Animation animation = getAnimation(); + if (animation != null) { + super.clearAnimation(); + if (animation.hasEnded() + && animation.getFillAfter() + && animation.willChangeBounds()) { + ((View) getParent()).invalidate(); } else { -// Log.d(TAG, "onKey() ACTION_DOWN isPopupShowing:" + mSearchText.isPopupShowing() + -// " mItemSelected="+ mItemSelected); - if (searchTrigger && mItemSelected < 0) { - query(); - return true; - } + invalidate(); } - } 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); + public boolean onKey(View v, int keyCode, KeyEvent event) { + 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)) { + // Forward key events to Launcher, which will forward text + // to search dialog + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + return mLauncher.onKeyDown(keyCode, event); + case KeyEvent.ACTION_MULTIPLE: + return mLauncher.onKeyMultiple(keyCode, event.getRepeatCount(), event); + case KeyEvent.ACTION_UP: + return mLauncher.onKeyUp(keyCode, event); + } } 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. + * Implements OnLongClickListener to pass long clicks on child views + * to the widget. This makes it possible to pick up the widget by long + * clicking on the text field or a button. */ - public void clearQuery() { - mSearchText.setText(null); + public boolean onLongClick(View v) { + return performLongClick(); } @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 = (TextView) findViewById(R.id.search_src_text); + mVoiceButton = (ImageButton) findViewById(R.id.search_voice_btn); + 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); + mSearchText.setOnClickListener(this); mVoiceButton.setOnClickListener(this); - mGoButton.setOnKeyListener(this); - mVoiceButton.setOnKeyListener(this); - + setOnClickListener(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(); } - - /** - * Cache of popup padding value after read from {@link Resources}. - */ - private static float mPaddingInset = -1; - - /** - * When our size is changed, pass down adjusted width and offset values to - * correctly center the {@link AutoCompleteTextView} popup and include our - * padding. - */ - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (changed) { - if (mPaddingInset == -1) { - mPaddingInset = getResources().getDimension(R.dimen.search_widget_inset); - } - - // Fill entire width of widget, minus padding inset - float paddedWidth = getWidth() - (mPaddingInset * 2); - float paddedOffset = -(mSearchText.getLeft() - mPaddingInset); - - mSearchText.setDropDownWidth((int) paddedWidth); - mSearchText.setDropDownHorizontalOffset((int) paddedOffset); - } - } - - /** - * 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 * @@ -384,346 +314,45 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen * 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; - } - + // 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); + boolean 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) + * Sets the {@link Launcher} that this gadget will call on to display the search dialog. */ - 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; + public void setLauncher(Launcher launcher) { + mLauncher = launcher; } - /** - * 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. + /** + * Moves the view to the top left corner of its parent. */ - 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. - */ + private class ToParentOriginAnimation extends Animation { @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; + protected void applyTransformation(float interpolatedTime, Transformation t) { + float dx = -getLeft() * interpolatedTime; + float dy = -getTop() * interpolatedTime; + t.getMatrix().setTranslate(dx, dy); } + } - /** - * 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 - */ + /** + * Moves the view from the top left corner of its parent. + */ + private class FromParentOriginAnimation extends Animation { @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); + protected void applyTransformation(float interpolatedTime, Transformation t) { + float dx = -getLeft() * (1.0f - interpolatedTime); + float dy = -getTop() * (1.0f - interpolatedTime); + t.getMatrix().setTranslate(dx, dy); } - - /** - * 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; - } - } - } + } |