SearchWidgetProvider.java revision 3e44ff1f2a204db3f479698cf0b3eab3d451dec2
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.quicksearchbox; 18 19import android.app.PendingIntent; 20import android.app.SearchManager; 21import android.appwidget.AppWidgetManager; 22import android.appwidget.AppWidgetProvider; 23import android.content.ComponentName; 24import android.content.Context; 25import android.content.Intent; 26import android.net.Uri; 27import android.os.Bundle; 28import android.speech.RecognizerIntent; 29import android.util.Log; 30import android.view.KeyEvent; 31import android.view.View; 32import android.widget.RemoteViews; 33 34/** 35 * Search widget provider. 36 * 37 */ 38public class SearchWidgetProvider extends AppWidgetProvider { 39 40 private static final boolean DBG = true; 41 private static final String TAG = "QSB.SearchWidgetProvider"; 42 43 private static final String ACTION_UPDATE_SEARCH_WIDGETS = 44 "com.android.quicksearchbox.UPDATE_SEARCH_WIDGETS"; 45 46 private static final String WIDGET_SEARCH_SOURCE = "launcher-search"; 47 private static final String WIDGET_SEARCH_SHORTCUT_SOURCE = "launcher-search-shortcut"; 48 49 // TODO: Expose SearchManager.SOURCE instead. 50 private static final String SOURCE = "source"; 51 52 /** 53 * Updates all search widgets. 54 */ 55 public static void updateSearchWidgets(Context context) { 56 Intent intent = new Intent(ACTION_UPDATE_SEARCH_WIDGETS); 57 intent.setComponent(myComponentName(context)); 58 if (DBG) Log.d(TAG, "Broadcasting " + intent); 59 context.sendBroadcast(intent); 60 } 61 62 /** 63 * Gets the component name for this app widget provider. 64 */ 65 private static ComponentName myComponentName(Context context) { 66 return new ComponentName(context, SearchWidgetProvider.class); 67 } 68 69 @Override 70 public void onReceive(Context context, Intent intent) { 71 String action = intent.getAction(); 72 if (ACTION_UPDATE_SEARCH_WIDGETS.equals(action)) { 73 // We requested the update. Find the widgets and update them. 74 AppWidgetManager manager = AppWidgetManager.getInstance(context); 75 int[] appWidgetIds = manager.getAppWidgetIds(myComponentName(context)); 76 onUpdate(context, manager, appWidgetIds); 77 } else { 78 // Handle actions requested by the widget host. 79 super.onReceive(context, intent); 80 } 81 } 82 83 @Override 84 public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 85 updateSearchWidgets(context, appWidgetManager, appWidgetIds); 86 } 87 88 /** 89 * Updates a set of search widgets. 90 */ 91 private void updateSearchWidgets(Context context, AppWidgetManager appWidgetManager, 92 int[] appWidgetIds) { 93 if (DBG) Log.d(TAG, "updateSearchWidgets()"); 94 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.search_widget); 95 96 Bundle widgetAppData = new Bundle(); 97 widgetAppData.putString(SOURCE, WIDGET_SEARCH_SOURCE); 98 99 // Text field 100 Intent qsbIntent = new Intent(Intent.ACTION_MAIN); 101 qsbIntent.setClass(context, SearchActivity.class); 102 qsbIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 103 qsbIntent.putExtra(SearchManager.APP_DATA, widgetAppData); 104 PendingIntent textPendingIntent = PendingIntent.getActivity(context, 0, qsbIntent, 0); 105 views.setOnClickPendingIntent(R.id.search_widget_text, textPendingIntent); 106 107 // Voice search button. Only shown if voice search is available. 108 Intent voiceSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 109 voiceSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 110 voiceSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, 111 RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); 112 // TODO: Does VoiceSearch actually look at APP_DATA? 113 voiceSearchIntent.putExtra(SearchManager.APP_DATA, widgetAppData); 114 if (voiceSearchIntent.resolveActivity(context.getPackageManager()) != null) { 115 PendingIntent voicePendingIntent = 116 PendingIntent.getActivity(context, 0, voiceSearchIntent, 0); 117 views.setOnClickPendingIntent(R.id.search_widget_voice_btn, voicePendingIntent); 118 views.setViewVisibility(R.id.search_widget_voice_btn, View.VISIBLE); 119 } else { 120 views.setViewVisibility(R.id.search_widget_voice_btn, View.GONE); 121 } 122 123 // Show the top shortcut 124 ShortcutRepository shortcutRepo = getShortcutRepository(context); 125 SuggestionCursor shortcuts = shortcutRepo.getShortcutsForQuery(""); 126 try { 127 if (shortcuts != null && shortcuts.getCount() > 0) { 128 shortcuts.moveTo(0); 129 130 // TODO: Eclair 2.1 adds RemoteViews.addView(), use that instead, 131 // so that we can show multiple suggestions. 132 // RemoteViews shortcutView = new RemoteViews(context.getPackageName(), R.layout.suggestion); 133 // views.addView(R.id.widget_shortcuts, shortcutView); 134 // bindRemoteViewSuggestion(context, shortcutView, shortcuts); 135 136 bindRemoteViewSuggestion(context, views, shortcuts); 137 } else { 138 clearRemoteViewSuggestion(context, views); 139 } 140 } finally { 141 if (shortcuts != null) { 142 shortcuts.close(); 143 } 144 } 145 146 appWidgetManager.updateAppWidget(appWidgetIds, views); 147 } 148 149 private void clearRemoteViewSuggestion(Context context, RemoteViews views) { 150// TODO: These throw exceptions because of http://b/issue?id=2301242 151// setText1(views, null); 152// setIcon1(views, null); 153// setPendingIntent(views, null); 154 } 155 156 private void bindRemoteViewSuggestion(Context context, RemoteViews views, SuggestionCursor suggestion) { 157 CharSequence text1 = suggestion.getSuggestionFormattedText1(); 158 CharSequence text2 = suggestion.getSuggestionFormattedText2(); 159 Uri icon1 = suggestion.getIconUri(suggestion.getSuggestionIcon1()); 160 if (icon1 == null) { 161 icon1 = suggestion.getSourceIconUri(); 162 } 163 Uri icon2 = suggestion.getIconUri(suggestion.getSuggestionIcon2()); 164 PendingIntent pendingIntent = getWidgetSuggestionIntent(context, suggestion); 165 if (DBG) { 166 Log.d(TAG, "Adding shortcut to widget: text1=" + text1 + ",text2=" + text2 167 + ",icon1=" + icon1 + ",icon2=" + icon2); 168 Log.d(TAG, " intent=" + pendingIntent); 169 } 170 setText1(views, text1); 171 setIcon1(views, icon1); 172 setPendingIntent(views, pendingIntent); 173 } 174 175 private PendingIntent getWidgetSuggestionIntent(Context context, SuggestionCursor suggestion) { 176 Bundle widgetAppData = new Bundle(); 177 widgetAppData.putString(SOURCE, WIDGET_SEARCH_SHORTCUT_SOURCE); 178 Intent intent = suggestion.getSuggestionIntent(context, widgetAppData, 179 KeyEvent.KEYCODE_UNKNOWN, null); 180 return PendingIntent.getActivity(context, 0, intent, 0); 181 } 182 183 private void setText1(RemoteViews views, CharSequence text) { 184 views.setCharSequence(R.id.text1, "setText", text); 185 } 186 187 private void setIcon1(RemoteViews views, Uri icon) { 188 views.setImageViewUri(R.id.icon1, icon); 189 } 190 191 private void setPendingIntent(RemoteViews views, PendingIntent pendingIntent) { 192 // TODO: Use R.id.suggestion when we switch to RemoteViews.addView() 193 views.setOnClickPendingIntent(R.id.shortcut_1, pendingIntent); 194 } 195 196 private QsbApplication getQsbApplication(Context context) { 197 return (QsbApplication) context.getApplicationContext(); 198 } 199 200 private ShortcutRepository getShortcutRepository(Context context) { 201 return getQsbApplication(context).getShortcutRepository(); 202 } 203 204} 205