diff options
| -rw-r--r-- | api/current.xml | 11 | ||||
| -rw-r--r-- | core/java/android/app/SearchDialog.java | 19 | ||||
| -rw-r--r-- | core/java/android/app/SearchManager.java | 10 | ||||
| -rw-r--r-- | core/java/android/app/SearchSourceSelector.java | 197 | ||||
| -rw-r--r-- | core/res/res/drawable-hdpi/search_source_selector_indicator.png | bin | 0 -> 252 bytes | |||
| -rw-r--r-- | core/res/res/drawable-mdpi/search_source_selector_indicator.png | bin | 0 -> 195 bytes | |||
| -rw-r--r-- | core/res/res/drawable/search_source_selector_background.xml | 21 | ||||
| -rw-r--r-- | core/res/res/layout/search_bar.xml | 11 | ||||
| -rw-r--r-- | core/res/res/layout/search_source_selector.xml | 35 |
9 files changed, 290 insertions, 14 deletions
diff --git a/api/current.xml b/api/current.xml index cbe47ff7af6a..64a7d7a29632 100644 --- a/api/current.xml +++ b/api/current.xml @@ -23917,6 +23917,17 @@ visibility="public" > </field> +<field name="INTENT_ACTION_SELECT_SEARCH_SOURCE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.SELECT_SEARCH_SOURCE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="INTENT_ACTION_WEB_SEARCH_SETTINGS" type="java.lang.String" transient="false" diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 8968553ff07c..b3963968b09a 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -60,7 +60,6 @@ import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.ImageButton; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; @@ -106,7 +105,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // views & widgets private TextView mBadgeLabel; - private ImageView mAppIcon; + private SearchSourceSelector mSourceSelector; private SearchAutoComplete mSearchAutoComplete; private Button mGoButton; private ImageButton mVoiceButton; @@ -209,7 +208,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge); mSearchAutoComplete = (SearchAutoComplete) findViewById(com.android.internal.R.id.search_src_text); - mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon); + mSourceSelector = new SearchSourceSelector( + findViewById(com.android.internal.R.id.search_source_selector)); mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); mSearchPlate = findViewById(com.android.internal.R.id.search_plate); @@ -606,13 +606,16 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } private void updateSearchAppIcon() { + mSourceSelector.setSource(mSearchable.getSearchActivity()); + mSourceSelector.setAppSearchData(mAppSearchData); + // In Donut, we special-case the case of the browser to hide the app icon as if it were // global search, for extra space for url entry. // // TODO: Remove this special case once the issue has been reconciled in Eclair. if (mGlobalSearchMode || isBrowserSearch()) { - mAppIcon.setImageResource(0); - mAppIcon.setVisibility(View.GONE); + mSourceSelector.setSourceIcon(null); + mSourceSelector.setVisibility(View.GONE); mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL, mSearchPlate.getPaddingTop(), mSearchPlate.getPaddingRight(), @@ -628,8 +631,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS icon = pm.getDefaultActivityIcon(); Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon"); } - mAppIcon.setImageDrawable(icon); - mAppIcon.setVisibility(View.VISIBLE); + mSourceSelector.setSourceIcon(icon); + mSourceSelector.setVisibility(View.VISIBLE); mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL, mSearchPlate.getPaddingTop(), mSearchPlate.getPaddingRight(), @@ -812,6 +815,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (!mSearchAutoComplete.isPerformingCompletion()) { // The user changed the query, remember it. mUserQuery = s == null ? "" : s.toString(); + mSourceSelector.setQuery(mUserQuery); } } @@ -1927,6 +1931,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS query = ""; } mUserQuery = query; + mSourceSelector.setQuery(query); mSearchAutoComplete.setText(query); mSearchAutoComplete.setSelection(query.length()); } diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 5d9034b516a8..5961ef55924e 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1346,6 +1346,7 @@ public class SearchManager * @hide Pending API council approval */ public final static String SELECT_INITIAL_QUERY = "select_initial_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)}. @@ -1612,6 +1613,15 @@ public class SearchManager public final static String SUGGEST_PARAMETER_LIMIT = "limit"; /** + * Intent action for opening the search source selection activity. + * The intent may include these extra values: + * {@link #QUERY}, + * {@link #APP_DATA}. + */ + public static final String INTENT_ACTION_SELECT_SEARCH_SOURCE + = "android.intent.action.SELECT_SEARCH_SOURCE"; + + /** * 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. diff --git a/core/java/android/app/SearchSourceSelector.java b/core/java/android/app/SearchSourceSelector.java new file mode 100644 index 000000000000..fabf858a52a3 --- /dev/null +++ b/core/java/android/app/SearchSourceSelector.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2009 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 android.app; + +import com.android.internal.R; + +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Intent; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.ImageButton; + +import java.util.List; + +/** + * Utilities for setting up the search source selector. + * + * This class has two copies: + * android.app.SearchSourceSelector + * com.android.quicksearchbox.ui.SearchSourceSelector + * + * They should keep the same look and feel as much as possible, + * but only the intent details must absolutely stay in sync. + * + * @hide + */ +public class SearchSourceSelector implements View.OnClickListener { + + private static final String TAG = "SearchSourceSelector"; + + // TODO: This should be defined in android.provider.Applications, + // and have a less made-up value. + private static final String APPLICATION_TYPE = "application/vnd.android.application"; + + public static final int ICON_VIEW_ID = R.id.search_source_selector_icon; + + private final View mView; + + private final ImageButton mIconView; + + private ComponentName mSource; + + private Bundle mAppSearchData; + + private String mQuery; + + public SearchSourceSelector(View view) { + mView = view; + mIconView = (ImageButton) view.findViewById(ICON_VIEW_ID); + mIconView.setOnClickListener(this); + } + + /** + * Sets the icon displayed in the search source selector. + */ + public void setSourceIcon(Drawable icon) { + mIconView.setImageDrawable(icon); + } + + /** + * Sets the current search source. + */ + public void setSource(ComponentName source) { + mSource = source; + } + + /** + * Sets the app-specific data that will be passed to the search activity if + * the user opens the source selector and chooses a source. + */ + public void setAppSearchData(Bundle appSearchData) { + mAppSearchData = appSearchData; + } + + /** + * Sets the initial query that will be passed to the search activity if + * the user opens the source selector and chooses a source. + */ + public void setQuery(String query) { + mQuery = query; + } + + public void setVisibility(int visibility) { + mView.setVisibility(visibility); + } + + /** + * Creates an intent for opening the search source selector activity. + * + * @param source The current search source. + * @param query The initial query that will be passed to the search activity if + * the user opens the source selector and chooses a source. + * @param appSearchData The app-specific data that will be passed to the search + * activity if the user opens the source selector and chooses a source. + */ + public static Intent createIntent(ComponentName source, String query, Bundle appSearchData) { + Intent intent = new Intent(SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + Uri sourceUri = componentNameToUri(source); + if (sourceUri != null) { + intent.setDataAndType(sourceUri, APPLICATION_TYPE); + } + if (query != null) { + intent.putExtra(SearchManager.QUERY, query); + } + if (query != null) { + intent.putExtra(SearchManager.APP_DATA, appSearchData); + } + return intent; + } + + /** + * Gets the search source from which the given + * {@link SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE} intent was sent. + */ + public static ComponentName getSource(Intent intent) { + return uriToComponentName(intent.getData()); + } + + private static Uri componentNameToUri(ComponentName name) { + if (name == null) return null; + // TODO: This URI format is specificed in android.provider.Applications which is @hidden + return new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority("applications") + .appendEncodedPath("applications") + .appendPath(name.getPackageName()) + .appendPath(name.getClassName()) + .build(); + } + + private static ComponentName uriToComponentName(Uri uri) { + if (uri == null) return null; + List<String> path = uri.getPathSegments(); + if (path == null || path.size() != 3) return null; + String pkg = path.get(1); + String cls = path.get(2); + if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(cls)) return null; + return new ComponentName(pkg, cls); + } + + public void onClick(View v) { + trigger(); + } + + private void trigger() { + try { + Intent intent = createIntent(mSource, mQuery, mAppSearchData); + intent.setSourceBounds(getOnScreenRect(mIconView)); + mIconView.getContext().startActivity(intent); + } catch (ActivityNotFoundException ex) { + Log.e(TAG, "No source selector activity found", ex); + } + } + + // TODO: This code is replicated in lots of places: + // - android.provider.ContactsContract.QuickContact.showQuickContact() + // - android.widget.RemoteViews.setOnClickPendingIntent() + // - com.android.launcher2.Launcher.onClick() + // - com.android.launcher.Launcher.onClick() + // - com.android.server.status.StatusBarService.Launcher.onClick() + private static Rect getOnScreenRect(View v) { + final float appScale = v.getResources().getCompatibilityInfo().applicationScale; + final int[] pos = new int[2]; + v.getLocationOnScreen(pos); + final Rect rect = new Rect(); + rect.left = (int) (pos[0] * appScale + 0.5f); + rect.top = (int) (pos[1] * appScale + 0.5f); + rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f); + rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f); + return rect; + } + +} diff --git a/core/res/res/drawable-hdpi/search_source_selector_indicator.png b/core/res/res/drawable-hdpi/search_source_selector_indicator.png Binary files differnew file mode 100644 index 000000000000..b93a0c0bbfb3 --- /dev/null +++ b/core/res/res/drawable-hdpi/search_source_selector_indicator.png diff --git a/core/res/res/drawable-mdpi/search_source_selector_indicator.png b/core/res/res/drawable-mdpi/search_source_selector_indicator.png Binary files differnew file mode 100644 index 000000000000..26bf18a68870 --- /dev/null +++ b/core/res/res/drawable-mdpi/search_source_selector_indicator.png diff --git a/core/res/res/drawable/search_source_selector_background.xml b/core/res/res/drawable/search_source_selector_background.xml new file mode 100644 index 000000000000..fcacd892f3b7 --- /dev/null +++ b/core/res/res/drawable/search_source_selector_background.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- TODO: Need focused and pressed backgrounds --> + +</selector> diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index cf246ba3906a..12285fde6cee 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -55,14 +55,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> - - <ImageView - android:id="@+id/search_app_icon" - android:layout_height="36dip" - android:layout_width="36dip" + + <include android:id="@+id/search_source_selector" + layout="@layout/search_source_selector" android:layout_marginRight="7dip" - android:layout_gravity="center_vertical" - /> + android:layout_gravity="center_vertical" /> <view class="android.app.SearchDialog$SearchAutoComplete" android:id="@+id/search_src_text" diff --git a/core/res/res/layout/search_source_selector.xml b/core/res/res/layout/search_source_selector.xml new file mode 100644 index 000000000000..c69dfc0e2159 --- /dev/null +++ b/core/res/res/layout/search_source_selector.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 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. +--> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="48dip" + android:layout_height="match_parent" + android:foreground="@drawable/search_source_selector_indicator" + android:foregroundGravity="bottom|right" + > + + <ImageButton + android:id="@+id/search_source_selector_icon" + android:background="@drawable/search_source_selector_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="centerInside" + android:focusable="true" + android:clickable="true" + /> + +</FrameLayout> |