SearchSettings.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 com.android.quicksearchbox.framework.Searchables;
20
21import android.app.AlertDialog;
22import android.app.Dialog;
23import android.app.SearchManager;
24import android.content.ComponentName;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.pm.PackageManager;
28import android.content.pm.ResolveInfo;
29import android.os.Bundle;
30import android.preference.CheckBoxPreference;
31import android.preference.Preference;
32import android.preference.PreferenceActivity;
33import android.preference.PreferenceGroup;
34import android.preference.PreferenceScreen;
35import android.preference.Preference.OnPreferenceChangeListener;
36import android.preference.Preference.OnPreferenceClickListener;
37import android.util.Log;
38
39import java.util.List;
40
41/**
42 * Activity for setting global search preferences. Changes to search preferences trigger a broadcast
43 * intent that causes all SuggestionSources objects to be updated.
44 */
45public class SearchSettings extends PreferenceActivity
46        implements OnPreferenceClickListener, OnPreferenceChangeListener {
47
48    private static final boolean DBG = false;
49    private static final String TAG = "SearchSettings";
50
51    // Only used to find the preferences after inflating
52    private static final String CLEAR_SHORTCUTS_PREF = "clear_shortcuts";
53    private static final String SEARCH_ENGINE_SETTINGS_PREF = "search_engine_settings";
54    private static final String SEARCH_SOURCES_PREF = "search_sources";
55
56    private SourceLookup mSources;
57    private ShortcutRepository mShortcuts;
58
59    // References to the top-level preference objects
60    private Preference mClearShortcutsPreference;
61    private PreferenceScreen mSearchEngineSettingsPreference;
62    private PreferenceGroup mSourcePreferences;
63
64    // Dialog ids
65    private static final int CLEAR_SHORTCUTS_CONFIRM_DIALOG = 0;
66
67    @Override
68    protected void onCreate(Bundle savedInstanceState) {
69        super.onCreate(savedInstanceState);
70
71        mSources = getSources();
72        mShortcuts = ShortcutRepositoryImplLog.create(this, getConfig(), mSources);
73        getPreferenceManager().setSharedPreferencesName(Sources.PREFERENCES_NAME);
74
75        addPreferencesFromResource(R.xml.preferences);
76
77        PreferenceScreen preferenceScreen = getPreferenceScreen();
78        mClearShortcutsPreference = preferenceScreen.findPreference(CLEAR_SHORTCUTS_PREF);
79        mSearchEngineSettingsPreference = (PreferenceScreen) preferenceScreen.findPreference(
80                SEARCH_ENGINE_SETTINGS_PREF);
81        mSourcePreferences = (PreferenceGroup) getPreferenceScreen().findPreference(
82                SEARCH_SOURCES_PREF);
83
84        mClearShortcutsPreference.setOnPreferenceClickListener(this);
85
86        updateClearShortcutsPreference();
87        populateSourcePreference();
88        populateSearchEnginePreference();
89    }
90
91    @Override
92    protected void onDestroy() {
93        mShortcuts.close();
94        super.onDestroy();
95    }
96
97    private QsbApplication getQSBApplication() {
98        return (QsbApplication) getApplication();
99    }
100
101    private Config getConfig() {
102        return getQSBApplication().getConfig();
103    }
104
105    private SourceLookup getSources() {
106        return getQSBApplication().getSources();
107    }
108
109    /**
110     * Enables/disables the "Clear search shortcuts" preference depending
111     * on whether there is any search history.
112     */
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     */
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     */
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     */
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     */
202    private void populateSourcePreference() {
203        for (Source source : mSources.getSources()) {
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     */
215    private Preference createSourcePreference(Source source) {
216        CheckBoxPreference sourcePref = new CheckBoxPreference(this);
217        sourcePref.setKey(Sources.getSourceEnabledPreference(source));
218        sourcePref.setDefaultValue(mSources.isTrustedSource(source));
219        sourcePref.setOnPreferenceChangeListener(this);
220        CharSequence 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     */
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
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