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