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 17 package com.android.globalsearch; 18 19 import android.app.AlertDialog; 20 import android.app.Dialog; 21 import android.app.SearchManager; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.pm.ActivityInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.ResolveInfo; 29 import android.content.res.Resources; 30 import android.os.Bundle; 31 import android.preference.CheckBoxPreference; 32 import android.preference.ListPreference; 33 import android.preference.Preference; 34 import android.preference.PreferenceActivity; 35 import android.preference.PreferenceGroup; 36 import android.preference.PreferenceScreen; 37 import android.preference.Preference.OnPreferenceChangeListener; 38 import android.preference.Preference.OnPreferenceClickListener; 39 import android.provider.Settings; 40 import android.provider.Settings.SettingNotFoundException; 41 import android.server.search.SearchableInfo; 42 import android.server.search.Searchables; 43 import android.util.Log; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 48 /** 49 * Activity for setting global search preferences. Changes to search preferences trigger a broadcast 50 * intent that causes all SuggestionSources objects to be updated. 51 */ 52 public class SearchSettings extends PreferenceActivity 53 implements OnPreferenceClickListener, OnPreferenceChangeListener { 54 55 private static final boolean DBG = false; 56 private static final String TAG = "SearchSettings"; 57 58 // Only used to find the preferences after inflating 59 private static final String CLEAR_SHORTCUTS_PREF = "clear_shortcuts"; 60 private static final String SEARCH_ENGINE_SETTINGS_PREF = "search_engine_settings"; 61 private static final String SEARCH_SOURCES_PREF = "search_sources"; 62 63 private SearchManager mSearchManager; 64 65 // These instances are not shared with SuggestionProvider 66 private SuggestionSources mSources; 67 private ShortcutRepository mShortcuts; 68 69 // References to the top-level preference objects 70 private Preference mClearShortcutsPreference; 71 private PreferenceScreen mSearchEngineSettingsPreference; 72 private PreferenceGroup mSourcePreferences; 73 74 // Dialog ids 75 private static final int CLEAR_SHORTCUTS_CONFIRM_DIALOG = 0; 76 77 @Override onCreate(Bundle savedInstanceState)78 protected void onCreate(Bundle savedInstanceState) { 79 super.onCreate(savedInstanceState); 80 81 mSearchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); 82 83 mSources = new SuggestionSources(this); 84 mSources.load(); 85 mShortcuts = ShortcutRepositoryImplLog.create(this); 86 getPreferenceManager().setSharedPreferencesName(SuggestionSources.PREFERENCES_NAME); 87 88 addPreferencesFromResource(R.xml.preferences); 89 90 PreferenceScreen preferenceScreen = getPreferenceScreen(); 91 mClearShortcutsPreference = preferenceScreen.findPreference(CLEAR_SHORTCUTS_PREF); 92 mSearchEngineSettingsPreference = (PreferenceScreen) preferenceScreen.findPreference( 93 SEARCH_ENGINE_SETTINGS_PREF); 94 mSourcePreferences = (PreferenceGroup) getPreferenceScreen().findPreference( 95 SEARCH_SOURCES_PREF); 96 97 mClearShortcutsPreference.setOnPreferenceClickListener(this); 98 99 updateClearShortcutsPreference(); 100 populateSourcePreference(); 101 populateSearchEnginePreference(); 102 } 103 104 @Override onDestroy()105 protected void onDestroy() { 106 mSources.close(); 107 mShortcuts.close(); 108 super.onDestroy(); 109 } 110 111 /** 112 * Enables/disables the "Clear search shortcuts" preference depending 113 * on whether there is any search history. 114 */ updateClearShortcutsPreference()115 private void updateClearShortcutsPreference() { 116 boolean hasHistory = mShortcuts.hasHistory(); 117 if (DBG) Log.d(TAG, "hasHistory()=" + hasHistory); 118 mClearShortcutsPreference.setEnabled(hasHistory); 119 } 120 121 /** 122 * Populates the preference item for the web search engine, which links to further 123 * search settings. 124 */ populateSearchEnginePreference()125 private void populateSearchEnginePreference() { 126 PackageManager pm = getPackageManager(); 127 128 // Try to find EnhancedGoogleSearch if installed. 129 ComponentName webSearchComponent; 130 try { 131 webSearchComponent = ComponentName.unflattenFromString( 132 Searchables.ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME); 133 pm.getActivityInfo(webSearchComponent, 0); 134 } catch (PackageManager.NameNotFoundException e1) { 135 // EnhancedGoogleSearch is not installed. Try to get GoogleSearch. 136 try { 137 webSearchComponent = ComponentName.unflattenFromString( 138 Searchables.GOOGLE_SEARCH_COMPONENT_NAME); 139 pm.getActivityInfo(webSearchComponent, 0); 140 } catch (PackageManager.NameNotFoundException e2) { 141 throw new RuntimeException("could not find a web search provider"); 142 } 143 } 144 145 ResolveInfo matchedInfo = findWebSearchSettingsActivity(webSearchComponent); 146 if (matchedInfo == null) { 147 throw new RuntimeException("could not find settings for web search provider"); 148 } 149 150 Intent intent = createWebSearchSettingsIntent(matchedInfo); 151 String searchEngineSettingsLabel = 152 matchedInfo.activityInfo.loadLabel(pm).toString(); 153 mSearchEngineSettingsPreference.setTitle(searchEngineSettingsLabel); 154 155 mSearchEngineSettingsPreference.setIntent(intent); 156 } 157 158 /** 159 * Returns the activity in the provided package that satisfies the 160 * {@link SearchManager#INTENT_ACTION_WEB_SEARCH_SETTINGS} intent, or null 161 * if none. 162 */ findWebSearchSettingsActivity(ComponentName component)163 private ResolveInfo findWebSearchSettingsActivity(ComponentName component) { 164 // Get all the activities which satisfy the WEB_SEARCH_SETTINGS intent. 165 PackageManager pm = getPackageManager(); 166 Intent intent = new Intent(SearchManager.INTENT_ACTION_WEB_SEARCH_SETTINGS); 167 List<ResolveInfo> activitiesWithWebSearchSettings = pm.queryIntentActivities(intent, 0); 168 169 String packageName = component.getPackageName(); 170 String name = component.getClassName(); 171 172 // Iterate through them and see if any of them are the activity we're looking for. 173 for (ResolveInfo resolveInfo : activitiesWithWebSearchSettings) { 174 if (packageName.equals(resolveInfo.activityInfo.packageName) 175 && name.equals(resolveInfo.activityInfo.name)) { 176 return resolveInfo; 177 } 178 } 179 180 // If there is no exact match, look for one in the right package 181 for (ResolveInfo resolveInfo : activitiesWithWebSearchSettings) { 182 if (packageName.equals(resolveInfo.activityInfo.packageName)) { 183 return resolveInfo; 184 } 185 } 186 187 return null; 188 } 189 190 /** 191 * Creates an intent for accessing the web search settings from the provided ResolveInfo 192 * representing an activity. 193 */ createWebSearchSettingsIntent(ResolveInfo info)194 private Intent createWebSearchSettingsIntent(ResolveInfo info) { 195 Intent intent = new Intent(SearchManager.INTENT_ACTION_WEB_SEARCH_SETTINGS); 196 intent.setComponent( 197 new ComponentName(info.activityInfo.packageName, info.activityInfo.name)); 198 return intent; 199 } 200 201 /** 202 * Fills the suggestion source list. 203 */ populateSourcePreference()204 private void populateSourcePreference() { 205 for (SuggestionSource source : mSources.getSuggestionSources()) { 206 Preference pref = createSourcePreference(source); 207 if (pref != null) { 208 if (DBG) Log.d(TAG, "Adding search source: " + source); 209 mSourcePreferences.addPreference(pref); 210 } 211 } 212 } 213 214 /** 215 * Adds a suggestion source to the list of suggestion source checkbox preferences. 216 */ createSourcePreference(SuggestionSource source)217 private Preference createSourcePreference(SuggestionSource source) { 218 CheckBoxPreference sourcePref = new CheckBoxPreference(this); 219 sourcePref.setKey(mSources.getSourceEnabledPreference(source)); 220 sourcePref.setDefaultValue(mSources.isSourceDefaultEnabled(source)); 221 sourcePref.setOnPreferenceChangeListener(this); 222 String label = source.getLabel(); 223 sourcePref.setTitle(label); 224 sourcePref.setSummaryOn(source.getSettingsDescription()); 225 sourcePref.setSummaryOff(source.getSettingsDescription()); 226 return sourcePref; 227 } 228 229 /** 230 * Handles clicks on the "Clear search shortcuts" preference. 231 */ onPreferenceClick(Preference preference)232 public synchronized boolean onPreferenceClick(Preference preference) { 233 if (preference == mClearShortcutsPreference) { 234 showDialog(CLEAR_SHORTCUTS_CONFIRM_DIALOG); 235 return true; 236 } 237 return false; 238 } 239 240 @Override onCreateDialog(int id)241 protected Dialog onCreateDialog(int id) { 242 switch (id) { 243 case CLEAR_SHORTCUTS_CONFIRM_DIALOG: 244 return new AlertDialog.Builder(this) 245 .setTitle(R.string.clear_shortcuts) 246 .setMessage(R.string.clear_shortcuts_prompt) 247 .setPositiveButton(R.string.agree, new DialogInterface.OnClickListener() { 248 public void onClick(DialogInterface dialog, int whichButton) { 249 if (DBG) Log.d(TAG, "Clearing history..."); 250 mShortcuts.clearHistory(); 251 updateClearShortcutsPreference(); 252 } 253 }) 254 .setNegativeButton(R.string.disagree, null).create(); 255 default: 256 Log.e(TAG, "unknown dialog" + id); 257 return null; 258 } 259 } 260 261 /** 262 * Informs our listeners (SuggestionSources objects) about the updated settings data. 263 */ 264 private void broadcastSettingsChanged() { 265 // We use a message broadcast since the listeners could be in multiple processes. 266 Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCH_SETTINGS_CHANGED); 267 Log.i(TAG, "Broadcasting: " + intent); 268 sendBroadcast(intent); 269 } 270 271 public synchronized boolean onPreferenceChange(Preference preference, Object newValue) { 272 broadcastSettingsChanged(); 273 return true; 274 } 275 276 } 277