Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 32d580c3 authored by Bjorn Bringert's avatar Bjorn Bringert
Browse files

Copy SearchSourceSelector from QuickSearchBox

The widget shows the icon for a search source, and when clicked
fires an intent that shows a search source selection activity.
That intent is handled by the QuickSearchBox app.

This change also adds the source selector to the in-app
search dialog.

An upcoming change to QuickSearchBox will add the search source
selector to the home screen search widget and to the Quick Search Box
activity.

TODO: Add assets for selected and pressed states to the search selector.
TODO: The SearchDialog hides when the soruce selection activity
appears. This will be fixed when SearchDialog is changed from a
system window to a normal app window.

Change-Id: I91eadd60682577614e274ecf5b995b927c70a48a
parent 71de7851
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -23917,6 +23917,17 @@
 visibility="public"
>
</field>
<field name="INTENT_ACTION_SELECT_SEARCH_SOURCE"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;android.intent.action.SELECT_SEARCH_SOURCE&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="INTENT_ACTION_WEB_SEARCH_SETTINGS"
 type="java.lang.String"
 transient="false"
+12 −7
Original line number Diff line number Diff line
@@ -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());
     }
+10 −0
Original line number Diff line number Diff line
@@ -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)}.
@@ -1611,6 +1612,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
+197 −0
Original line number Diff line number Diff line
/*
 * 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;
    }

}
+252 B
Loading image diff...
Loading