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