SearchableSource.java revision 62adab88055fd0ef6779242245cdc8c3ae5f999c
13e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/*
23e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Copyright (C) 2009 The Android Open Source Project
33e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
43e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
53e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * you may not use this file except in compliance with the License.
63e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * You may obtain a copy of the License at
73e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
83e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
93e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * See the License for the specific language governing permissions and
143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * limitations under the License.
153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpackage com.android.quicksearchbox;
183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
19fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.app.PendingIntent;
203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
21227180196cf8d92172cbb62f8ddaacf864be28e7Bjorn Bringertimport android.app.SearchableInfo;
223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.ComponentName;
233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.ContentResolver;
243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Context;
25fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.content.Intent;
263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.pm.ActivityInfo;
273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.pm.PackageManager;
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.pm.PackageManager.NameNotFoundException;
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.database.Cursor;
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.graphics.drawable.Drawable;
313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.net.Uri;
32fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.os.Bundle;
33fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringertimport android.speech.RecognizerIntent;
343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
36227180196cf8d92172cbb62f8ddaacf864be28e7Bjorn Bringertimport java.util.Arrays;
37227180196cf8d92172cbb62f8ddaacf864be28e7Bjorn Bringert
383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Represents a single suggestion source, e.g. Contacts.
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic class SearchableSource implements Source {
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private static final boolean DBG = true;
450484fb4d652bfa9d5c7fb238a7cec1a6f2244e44Bjorn Bringert    private static final String TAG = "QSB.SearchableSource";
463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
47fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    // TODO: This should be exposed or moved to android-common, see http://b/issue?id=2440614
48fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    // The extra key used in an intent to the speech recognizer for in-app voice search.
49fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private static final String EXTRA_CALLING_PACKAGE = "calling_package";
50fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final Context mContext;
523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final SearchableInfo mSearchable;
543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5562adab88055fd0ef6779242245cdc8c3ae5f999cBjorn Bringert    private final String mName;
5662adab88055fd0ef6779242245cdc8c3ae5f999cBjorn Bringert
573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final ActivityInfo mActivityInfo;
583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    // Cached label for the activity
603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private CharSequence mLabel = null;
613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    // Cached icon for the activity
633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private Drawable.ConstantState mSourceIcon = null;
643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final IconLoader mIconLoader;
663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public SearchableSource(Context context, SearchableInfo searchable)
683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throws NameNotFoundException {
693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        ComponentName componentName = searchable.getSearchActivity();
703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mContext = context;
713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mSearchable = searchable;
7262adab88055fd0ef6779242245cdc8c3ae5f999cBjorn Bringert        mName = componentName.flattenToShortString();
733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mActivityInfo = context.getPackageManager().getActivityInfo(componentName, 0);
743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
75e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert        mIconLoader = createIconLoader(context, searchable.getSuggestPackage());
76e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert    }
77e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert
78fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected Context getContext() {
79fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return mContext;
80fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
81fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
82fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    protected SearchableInfo getSearchableInfo() {
83fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return mSearchable;
84fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
85fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
86e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert    private IconLoader createIconLoader(Context context, String providerPackage) {
87e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert        if (providerPackage == null) return null;
88e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert        try {
89e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert            return new CachingIconLoader(new PackageIconLoader(context, providerPackage));
90e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert        } catch (PackageManager.NameNotFoundException ex) {
91e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert            Log.e(TAG, "Suggestion provider package not found: " + providerPackage);
92e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert            return null;
93e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert        }
943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public ComponentName getComponentName() {
973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mSearchable.getSearchActivity();
983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
100f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert    public String getName() {
10162adab88055fd0ef6779242245cdc8c3ae5f999cBjorn Bringert        return mName;
102d32d6753347e872aa5d9d6e9015ed7f24db3e362Bjorn Bringert    }
103d32d6753347e872aa5d9d6e9015ed7f24db3e362Bjorn Bringert
1043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public Drawable getIcon(String drawableId) {
105e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert        return mIconLoader == null ? null : mIconLoader.getIcon(drawableId);
1063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public Uri getIconUri(String drawableId) {
109e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert        return mIconLoader == null ? null : mIconLoader.getIconUri(drawableId);
1103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public CharSequence getLabel() {
1133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mLabel == null) {
1143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Load label lazily
1153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mLabel = mActivityInfo.loadLabel(mContext.getPackageManager());
1163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mLabel;
1183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1205691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert    public CharSequence getHint() {
1215691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert        return getText(mSearchable.getHintId());
1225691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert    }
1235691f9062b2a558ba39c700d65bc522d397ebc75Bjorn Bringert
1243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public int getQueryThreshold() {
1253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mSearchable.getSuggestThreshold();
1263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1288d9d845323514c34bb07d6eb91146a975324fcedBjorn Bringert    public CharSequence getSettingsDescription() {
129fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return getText(mSearchable.getSettingsDescriptionId());
1303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public Drawable getSourceIcon() {
1333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mSourceIcon == null) {
1343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Load icon lazily
1353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            int iconRes = getSourceIconResource();
1363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            PackageManager pm = mContext.getPackageManager();
1373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Drawable icon = pm.getDrawable(mActivityInfo.packageName, iconRes,
1383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    mActivityInfo.applicationInfo);
1393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Can't share Drawable instances, save constant state instead.
1403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mSourceIcon = (icon != null) ? icon.getConstantState() : null;
1413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // Optimization, return the Drawable the first time
1423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return icon;
1433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return (mSourceIcon != null) ? mSourceIcon.newDrawable() : null;
1453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public Uri getSourceIconUri() {
148d32d6753347e872aa5d9d6e9015ed7f24db3e362Bjorn Bringert        int resourceId = getSourceIconResource();
149d32d6753347e872aa5d9d6e9015ed7f24db3e362Bjorn Bringert        return new Uri.Builder()
150d32d6753347e872aa5d9d6e9015ed7f24db3e362Bjorn Bringert                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
151d32d6753347e872aa5d9d6e9015ed7f24db3e362Bjorn Bringert                .authority(getComponentName().getPackageName())
152d32d6753347e872aa5d9d6e9015ed7f24db3e362Bjorn Bringert                .appendEncodedPath(String.valueOf(resourceId))
153d32d6753347e872aa5d9d6e9015ed7f24db3e362Bjorn Bringert                .build();
1543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private int getSourceIconResource() {
1573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        int icon = mActivityInfo.getIconResource();
1583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return (icon != 0) ? icon : android.R.drawable.sym_def_app_icon;
1593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
161fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public boolean voiceSearchEnabled() {
162fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return mSearchable.getVoiceSearchEnabled();
163fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
164fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
165fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    // TODO: not all apps handle ACTION_SEARCH properly, e.g. ApplicationsProvider.
166fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    // Maybe we should add a flag to searchable, so that QSB can hide the search button?
167fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public Intent createSearchIntent(String query, Bundle appData) {
168fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Intent intent = new Intent(Intent.ACTION_SEARCH);
169fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        intent.setComponent(getComponentName());
170fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
171fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // We need CLEAR_TOP to avoid reusing an old task that has other activities
172fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // on top of the one we want.
173fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
174fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        intent.putExtra(SearchManager.USER_QUERY, query);
175fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        intent.putExtra(SearchManager.QUERY, query);
176fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (appData != null) {
177fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            intent.putExtra(SearchManager.APP_DATA, appData);
178fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
179fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return intent;
180fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
181fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
182fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public Intent createVoiceSearchIntent(Bundle appData) {
183fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (mSearchable.getVoiceSearchLaunchWebSearch()) {
184fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return WebCorpus.createVoiceWebSearchIntent(appData);
185fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
186fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return createVoiceAppSearchIntent(appData);
187fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
188fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return null;
189fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
190fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
191fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    /**
192fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * Create and return an Intent that can launch the voice search activity, perform a specific
193fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * voice transcription, and forward the results to the searchable activity.
194fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     *
195fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * This code is copied from SearchDialog
196fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     *
197fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     * @return A completely-configured intent ready to send to the voice search activity
198fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert     */
199fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private Intent createVoiceAppSearchIntent(Bundle appData) {
200fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        ComponentName searchActivity = mSearchable.getSearchActivity();
201fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
202fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // create the necessary intent to set up a search-and-forward operation
203fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // in the voice search system.   We have to keep the bundle separate,
204fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // because it becomes immutable once it enters the PendingIntent
205fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
206fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        queryIntent.setComponent(searchActivity);
207fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        PendingIntent pending = PendingIntent.getActivity(
208fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
209fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
210fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // Now set up the bundle that will be inserted into the pending intent
211fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // when it's time to do the search.  We always build it here (even if empty)
212fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // because the voice search activity will always need to insert "QUERY" into
213fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // it anyway.
214fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Bundle queryExtras = new Bundle();
215fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (appData != null) {
216fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            queryExtras.putBundle(SearchManager.APP_DATA, appData);
217fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
218fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
219fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // Now build the intent to launch the voice search.  Add all necessary
220fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // extras to launch the voice recognizer, and then all the necessary extras
221fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // to forward the results to the searchable activity
222fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
223fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        voiceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
224fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
225fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // Add all of the configuration options supplied by the searchable's metadata
226fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String languageModel = getString(mSearchable.getVoiceLanguageModeId());
227fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (languageModel == null) {
228fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
229fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
230fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String prompt = getString(mSearchable.getVoicePromptTextId());
231fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        String language = getString(mSearchable.getVoiceLanguageId());
232fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        int maxResults = mSearchable.getVoiceMaxResults();
233fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (maxResults <= 0) {
234fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            maxResults = 1;
235fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
236fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
237fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
238fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
239fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
240fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
241fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        voiceIntent.putExtra(EXTRA_CALLING_PACKAGE,
242fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                searchActivity == null ? null : searchActivity.toShortString());
243fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
244fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        // Add the values that configure forwarding the results
245fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
246fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
247fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
248fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return voiceIntent;
249fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
250fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
251fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    public SourceResult getSuggestions(String query, int queryLimit) {
2523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        try {
2533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Cursor cursor = getSuggestions(mContext, mSearchable, query, queryLimit);
2543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (DBG) Log.d(TAG, toString() + "[" + query + "] returned.");
255fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return new CursorBackedSourceResult(query, cursor);
2563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } catch (RuntimeException ex) {
2573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.e(TAG, toString() + "[" + query + "] failed", ex);
258fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return new CursorBackedSourceResult(query);
2593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
26294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    public SuggestionCursor refreshShortcut(String shortcutId, String extraData) {
26394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        Cursor cursor = null;
26494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        try {
26594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            cursor = getValidationCursor(mContext, mSearchable, shortcutId, extraData);
26694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            if (DBG) Log.d(TAG, toString() + "[" + shortcutId + "] returned.");
26794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            if (cursor != null && cursor.getCount() > 0) {
26894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney                cursor.moveToFirst();
26994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            }
270fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return new CursorBackedSourceResult(null, cursor);
27194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        } catch (RuntimeException ex) {
27294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            Log.e(TAG, toString() + "[" + shortcutId + "] failed", ex);
27394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            if (cursor != null) {
27494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney                cursor.close();
27594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            }
27694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            // TODO: Should we delete the shortcut even if the failure is temporary?
27794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            return null;
27894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        }
27994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    }
28094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
281fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private class CursorBackedSourceResult extends CursorBackedSuggestionCursor
282fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            implements SourceResult {
283fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
284fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public CursorBackedSourceResult(String userQuery) {
285fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            this(userQuery, null);
286fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
287fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
288fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public CursorBackedSourceResult(String userQuery, Cursor cursor) {
289fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            super(userQuery, cursor);
290fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
291fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
292fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public Source getSource() {
293fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return SearchableSource.this;
294fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
295fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
296fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        @Override
297fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        public Source getSuggestionSource() {
298fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            return SearchableSource.this;
299fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        }
300fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
301883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringert        public boolean isSuggestionShortcut() {
302883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringert            return false;
303883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringert        }
304883c1bf364e38c5b133afb55f8493a14b65f4dd4Bjorn Bringert
305bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert        @Override
306bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert        public String toString() {
307bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert            return SearchableSource.this + "[" + getUserQuery() + "]";
308bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert        }
309bf61e445cbe423cc2554b722b6dd38675015c36dBjorn Bringert
310fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
311fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
3123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * This is a copy of {@link SearchManager#getSuggestions(SearchableInfo, String)}.
3143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private static Cursor getSuggestions(Context context, SearchableInfo searchable, String query,
3163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            int queryLimit) {
3173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (searchable == null) {
3183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return null;
3193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String authority = searchable.getSuggestAuthority();
3223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (authority == null) {
3233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return null;
3243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Uri.Builder uriBuilder = new Uri.Builder()
3273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .scheme(ContentResolver.SCHEME_CONTENT)
3283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .authority(authority);
3293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // if content path provided, insert it now
3313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        final String contentPath = searchable.getSuggestPath();
3323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (contentPath != null) {
3333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            uriBuilder.appendEncodedPath(contentPath);
3343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // append standard suggestion query path
3373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
3383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // get the query selection, may be null
3403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String selection = searchable.getSuggestSelection();
3413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // inject query, either as selection args or inline
3423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String[] selArgs = null;
3433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (selection != null) {    // use selection if provided
3443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            selArgs = new String[] { query };
3453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {                    // no selection, use REST pattern
3463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            uriBuilder.appendPath(query);
3473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        uriBuilder.appendQueryParameter("limit", String.valueOf(queryLimit));
3503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Uri uri = uriBuilder.build();
3523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // finally, make the query
3543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) {
3553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.d(TAG, "query(" + uri + ",null," + selection + ","
3563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    + Arrays.toString(selArgs) + ",null)");
3573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return context.getContentResolver().query(uri, null, selection, selArgs, null);
3593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
36194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    private static Cursor getValidationCursor(Context context, SearchableInfo searchable,
36294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            String shortcutId, String extraData) {
36394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        String authority = searchable.getSuggestAuthority();
36494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        if (authority == null) {
36594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            return null;
36694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        }
36794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
36894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        Uri.Builder uriBuilder = new Uri.Builder()
36994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney                .scheme(ContentResolver.SCHEME_CONTENT)
37094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney                .authority(authority);
37194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
37294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        // if content path provided, insert it now
37394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        final String contentPath = searchable.getSuggestPath();
37494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        if (contentPath != null) {
37594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            uriBuilder.appendEncodedPath(contentPath);
37694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        }
37794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
37894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        // append the shortcut path and id
37994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_SHORTCUT);
38094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        uriBuilder.appendPath(shortcutId);
38194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
38294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        Uri uri = uriBuilder
38394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney                .appendQueryParameter(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA, extraData)
38494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney                .build();
38594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
38694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        if (DBG) Log.d(TAG, "Requesting refresh " + uri);
38794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        // finally, make the query
38894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        return context.getContentResolver().query(uri, null, null, null, null);
38994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    }
39094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
3913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean isWebSuggestionSource() {
392ca78085bb2127559e6f55276a307bfa857018ecaBjorn Bringert        return false;
3933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean queryAfterZeroResults() {
3963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mSearchable.queryAfterZeroResults();
3973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean shouldRewriteQueryFromData() {
4003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mSearchable.shouldRewriteQueryFromData();
4013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean shouldRewriteQueryFromText() {
4043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mSearchable.shouldRewriteQueryFromText();
4053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
408abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert    public boolean equals(Object o) {
409abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert        if (o != null && o.getClass().equals(this.getClass())) {
410abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert            SearchableSource s = (SearchableSource) o;
41162adab88055fd0ef6779242245cdc8c3ae5f999cBjorn Bringert            return s.mName.equals(mName);
412abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert        }
413abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert        return false;
414abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert    }
415abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert
416abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert    @Override
417abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert    public int hashCode() {
41862adab88055fd0ef6779242245cdc8c3ae5f999cBjorn Bringert        return mName.hashCode();
419abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert    }
420abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert
421abd33f7f8d8e56348a8a87bb9e7af491d83ee833Bjorn Bringert    @Override
4223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String toString() {
423f252dc7a25ba08b973ecc1cfbbce58eb78d42167Bjorn Bringert        return "SearchableSource{component=" + getName() + "}";
4243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getDefaultIntentAction() {
4273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mSearchable.getSuggestIntentAction();
4283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
4303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getDefaultIntentData() {
4313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mSearchable.getSuggestIntentData();
4323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
434782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    public String getSuggestActionMsg(int keyCode) {
435782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
436782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (actionKey == null) return null;
437782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return actionKey.getSuggestActionMsg();
438782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
439782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
440782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    public String getSuggestActionMsgColumn(int keyCode) {
441782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
442782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (actionKey == null) return null;
443782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return actionKey.getSuggestActionMsgColumn();
444782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
445782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
446fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private CharSequence getText(int id) {
447fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        if (id == 0) return null;
448fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return mContext.getPackageManager().getText(mActivityInfo.packageName, id,
449fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert                mActivityInfo.applicationInfo);
450fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
451fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert
452fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    private String getString(int id) {
453fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        CharSequence text = getText(id);
454fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert        return text == null ? null : text.toString();
455fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert    }
4563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
457