blob: 5748a0990de72d2c44010d4cd8245465421c77a1 [file] [log] [blame]
/*
* 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 com.android.globalsearch;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.server.search.SearchableInfo;
import android.server.search.Searchables;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Activity for setting global search preferences. Changes to search preferences trigger a broadcast
* intent that causes all SuggestionSources objects to be updated.
*/
public class SearchSettings extends PreferenceActivity
implements OnPreferenceClickListener, OnPreferenceChangeListener {
private static final boolean DBG = false;
private static final String TAG = "SearchSettings";
// Only used to find the preferences after inflating
private static final String CLEAR_SHORTCUTS_PREF = "clear_shortcuts";
private static final String SEARCH_ENGINE_SETTINGS_PREF = "search_engine_settings";
private static final String SEARCH_SOURCES_PREF = "search_sources";
private SearchManager mSearchManager;
// These instances are not shared with SuggestionProvider
private SuggestionSources mSources;
private ShortcutRepository mShortcuts;
// References to the top-level preference objects
private Preference mClearShortcutsPreference;
private ListPreference mWebSourcePreference;
private PreferenceScreen mSearchEngineSettingsPreference;
private PreferenceGroup mSourcePreferences;
// Dialog ids
private static final int CLEAR_SHORTCUTS_CONFIRM_DIALOG = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSearchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
mSources = new SuggestionSources(this);
mSources.load();
mShortcuts = ShortcutRepositoryImplLog.create(this);
getPreferenceManager().setSharedPreferencesName(SuggestionSources.PREFERENCES_NAME);
addPreferencesFromResource(R.xml.preferences);
PreferenceScreen preferenceScreen = getPreferenceScreen();
mClearShortcutsPreference = preferenceScreen.findPreference(CLEAR_SHORTCUTS_PREF);
mWebSourcePreference = (ListPreference) preferenceScreen.findPreference(
SuggestionSources.WEB_SEARCH_SOURCE_PREF);
mSearchEngineSettingsPreference = (PreferenceScreen) preferenceScreen.findPreference(
SEARCH_ENGINE_SETTINGS_PREF);
mSourcePreferences = (PreferenceGroup) getPreferenceScreen().findPreference(
SEARCH_SOURCES_PREF);
mClearShortcutsPreference.setOnPreferenceClickListener(this);
// Note: If a new preference was added to this UI, please set the onchange listener on the
// new preference here.
mWebSourcePreference.setOnPreferenceChangeListener(this);
updateClearShortcutsPreference();
populateWebSourcePreference();
populateSourcePreference();
updateSearchEnginePreferences(mWebSourcePreference.getValue());
}
@Override
protected void onDestroy() {
mSources.close();
mShortcuts.close();
super.onDestroy();
}
/**
* Fills in the web search source pop-up list.
*/
private void populateWebSourcePreference() {
SuggestionSource defWebSearch = mSources.getSelectedWebSearchSource();
ComponentName defComponentName = null;
if (defWebSearch != null) {
defComponentName = defWebSearch.getComponentName();
}
// Get the list of all packages handling intent action web search, these are the providers
// that we display in the selection list.
List<SearchableInfo> webSearchActivities = mSearchManager.getSearchablesForWebSearch();
PackageManager pm = getPackageManager();
ArrayList<String> labels = new ArrayList<String>();
ArrayList<String> values = new ArrayList<String>();
final int count = webSearchActivities.size();
Log.i(TAG, "Number of web search activities = " + count);
for (int i = 0; i < count; ++i) {
SearchableInfo searchable = webSearchActivities.get(i);
if (searchable == null) continue;
ComponentName component = searchable.getSearchActivity();
// If both GoogleSearch and EnhancedGoogleSearch are present, the former is hidden from
// our list because the latter is a superset of the former.
if (component.flattenToShortString().equals(Searchables.GOOGLE_SEARCH_COMPONENT_NAME)) {
try {
ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString(
Searchables.ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME);
pm.getActivityInfo(enhancedGoogleSearch, 0);
// Control comes here if EnhancedGoogleSearch is installed, in which case it
// overrides the GoogleSearch package in the web source list.
continue;
} catch (PackageManager.NameNotFoundException e) {
// Nothing to do as EnhancedGoogleSearch is not installed. Continue below and
// add this source to the list.
}
}
try {
// Add the localised display name and index of the activity within our array as the
// label and value for each item. The index will be used to identify which item was
// selected by the user.
ActivityInfo activityInfo = pm.getActivityInfo(component, 0);
Resources res = pm.getResourcesForApplication(activityInfo.applicationInfo);
int labelRes = (activityInfo.labelRes != 0)
? activityInfo.labelRes : activityInfo.applicationInfo.labelRes;
String name = res.getString(labelRes);
String value = component.flattenToShortString();
labels.add(name);
values.add(value);
if (DBG) Log.d(TAG, "Listing web search source: " + name);
if (defComponentName != null
&& defComponentName.getClassName().equals(activityInfo.name)) {
if (DBG) Log.d(TAG, "Default web search source: " + name);
mWebSourcePreference.setValue(value);
}
} catch (PackageManager.NameNotFoundException exception) {
// Skip this entry and continue to list other activities.
Log.w(TAG, "Web search source not found: " + component);
} catch (Resources.NotFoundException exception) {
Log.w(TAG, "No name for web search source: " + component);
}
}
// Check if EnhancedGoogleSearch or GoogleSearch are available, and if so insert it at the
// first position.
for (int i = 1; i < values.size(); ++i) {
String value = values.get(i);
if (value.equals(Searchables.GOOGLE_SEARCH_COMPONENT_NAME) ||
value.equals(Searchables.ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME)) {
values.add(0, values.remove(i));
labels.add(0, labels.remove(i));
break;
}
}
try {
String[] labelsArray = new String[labels.size()];
String[] valuesArray = new String[values.size()];
labels.toArray(labelsArray);
values.toArray(valuesArray);
mWebSourcePreference.setEntries(labelsArray);
mWebSourcePreference.setEntryValues(valuesArray);
} catch (ArrayStoreException exception) {
// In this case we will end up displaying an empty list.
Log.e(TAG, "Error loading web search sources", exception);
}
}
/**
* Enables/disables the "Clear search shortcuts" preference depending
* on whether there is any search history.
*/
private void updateClearShortcutsPreference() {
boolean hasHistory = mShortcuts.hasHistory();
if (DBG) Log.d(TAG, "hasHistory()=" + hasHistory);
mClearShortcutsPreference.setEnabled(hasHistory);
}
/**
* Updates the search engine preferences depending on the name of the currently
* selected search engine and whether it has settings to expose or not.
*
* @param webSourcePreferenceValue the web source preference value to use
*/
private void updateSearchEnginePreferences(String webSourcePreferenceValue) {
if (webSourcePreferenceValue == null) return;
// Get the package name of the current activity chosen for web search.
ComponentName component = ComponentName.unflattenFromString(webSourcePreferenceValue);
// Now find out if this package contains an activity which satisfies the
// WEB_SEARCH_SETTINGS intent, and if so, point the "search engine settings"
// item there.
ResolveInfo matchedInfo = findWebSearchSettingsActivity(component);
// If we found a match, that means this web search source provides some settings,
// so enable the link to its settings. If not, then disable this preference.
mSearchEngineSettingsPreference.setEnabled(matchedInfo != null);
PackageManager pm = getPackageManager();
String engineName = getWebSourceLabel(pm, component);
// Set the correct summary and intent information for the matched activity, if any.
int summaryStringRes;
Intent intent;
if (matchedInfo != null) {
summaryStringRes = R.string.search_engine_settings_summary_enabled;
intent = createWebSearchSettingsIntent(matchedInfo);
} else {
summaryStringRes = R.string.search_engine_settings_summary_disabled;
intent = null;
}
// If for some reason the engine name could not be found, just don't set a summary.
if (engineName != null) {
mWebSourcePreference.setSummary(
getResources().getString(R.string.web_search_source_summary, engineName));
mSearchEngineSettingsPreference.setSummary(
getResources().getString(summaryStringRes, engineName));
} else {
mWebSourcePreference.setSummary(null);
mSearchEngineSettingsPreference.setSummary(null);
}
mSearchEngineSettingsPreference.setIntent(intent);
}
/**
* Gets the name of the web source represented in the provided ResolveInfo.
*/
private String getWebSourceLabel(PackageManager pm, ComponentName component) {
try {
ActivityInfo activityInfo = pm.getActivityInfo(component, 0);
Resources res = pm.getResourcesForApplication(activityInfo.applicationInfo);
int labelRes = (activityInfo.labelRes != 0) ?
activityInfo.labelRes : activityInfo.applicationInfo.labelRes;
return res.getString(labelRes);
} catch (PackageManager.NameNotFoundException exception) {
Log.e(TAG, "Error loading web search source from activity "
+ component, exception);
return null;
}
}
/**
* Returns the activity in the provided package that satisfies the
* {@link SearchManager#INTENT_ACTION_WEB_SEARCH_SETTINGS} intent, or null
* if none.
*/
private ResolveInfo findWebSearchSettingsActivity(ComponentName component) {
// Get all the activities which satisfy the WEB_SEARCH_SETTINGS intent.
PackageManager pm = getPackageManager();
Intent intent = new Intent(SearchManager.INTENT_ACTION_WEB_SEARCH_SETTINGS);
List<ResolveInfo> activitiesWithWebSearchSettings = pm.queryIntentActivities(intent, 0);
String packageName = component.getPackageName();
String name = component.getClassName();
// Iterate through them and see if any of them are the activity we're looking for.
for (ResolveInfo resolveInfo : activitiesWithWebSearchSettings) {
if (packageName.equals(resolveInfo.activityInfo.packageName)
&& name.equals(resolveInfo.activityInfo.name)) {
return resolveInfo;
}
}
// If there is no exact match, look for one in the right package
for (ResolveInfo resolveInfo : activitiesWithWebSearchSettings) {
if (packageName.equals(resolveInfo.activityInfo.packageName)) {
return resolveInfo;
}
}
return null;
}
/**
* Creates an intent for accessing the web search settings from the provided ResolveInfo
* representing an activity.
*/
private Intent createWebSearchSettingsIntent(ResolveInfo info) {
Intent intent = new Intent(SearchManager.INTENT_ACTION_WEB_SEARCH_SETTINGS);
intent.setComponent(
new ComponentName(info.activityInfo.packageName, info.activityInfo.name));
return intent;
}
/**
* Fills the suggestion source list.
*/
private void populateSourcePreference() {
for (SuggestionSource source : mSources.getSuggestionSources()) {
Preference pref = createSourcePreference(source);
if (pref != null) {
if (DBG) Log.d(TAG, "Adding search source: " + source);
mSourcePreferences.addPreference(pref);
}
}
}
/**
* Adds a suggestion source to the list of suggestion source checkbox preferences.
*/
private Preference createSourcePreference(SuggestionSource source) {
CheckBoxPreference sourcePref = new CheckBoxPreference(this);
sourcePref.setKey(mSources.getSourceEnabledPreference(source));
sourcePref.setDefaultValue(mSources.isSourceDefaultEnabled(source));
sourcePref.setOnPreferenceChangeListener(this);
String label = source.getLabel();
sourcePref.setTitle(label);
sourcePref.setSummaryOn(source.getSettingsDescription());
sourcePref.setSummaryOff(source.getSettingsDescription());
return sourcePref;
}
/**
* Handles clicks on the "Clear search shortcuts" preference.
*/
public synchronized boolean onPreferenceClick(Preference preference) {
if (preference == mClearShortcutsPreference) {
showDialog(CLEAR_SHORTCUTS_CONFIRM_DIALOG);
return true;
}
return false;
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case CLEAR_SHORTCUTS_CONFIRM_DIALOG:
return new AlertDialog.Builder(this)
.setTitle(R.string.clear_shortcuts)
.setMessage(R.string.clear_shortcuts_prompt)
.setPositiveButton(R.string.agree, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
if (DBG) Log.d(TAG, "Clearing history...");
mShortcuts.clearHistory();
updateClearShortcutsPreference();
}
})
.setNegativeButton(R.string.disagree, null).create();
default:
Log.e(TAG, "unknown dialog" + id);
return null;
}
}
/**
* Inform our listeners (SuggestionSources objects) about the updated settings data.
*/
private void broadcastSettingsChanged() {
// We use a message broadcast since the listeners could be in multiple processes.
Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCH_SETTINGS_CHANGED);
Log.i(TAG, "Broadcasting: " + intent);
sendBroadcast(intent);
}
public synchronized boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mWebSourcePreference) {
String valueStr = (String)newValue;
ComponentName activity = ComponentName.unflattenFromString(valueStr);
if (DBG) Log.i(TAG, "Setting default web search source as " + valueStr);
mSearchManager.setDefaultWebSearch(activity);
updateSearchEnginePreferences(valueStr);
}
broadcastSettingsChanged();
return true; // to update the selection in the list if the user brings it up again.
}
}