/* * 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.quicksearchbox; import com.android.common.SharedPreferencesCompat; import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; /** * Manages user settings. */ public class SearchSettingsImpl implements SearchSettings { private static final boolean DBG = false; private static final String TAG = "QSB.SearchSettingsImpl"; // Name of the preferences file used to store search preference public static final String PREFERENCES_NAME = "SearchSettings"; // Intent action that opens the "Searchable Items" preference private static final String ACTION_SEARCHABLE_ITEMS = "com.android.quicksearchbox.action.SEARCHABLE_ITEMS"; /** * Preference key used for storing the index of the next voice search hint to show. */ private static final String NEXT_VOICE_SEARCH_HINT_INDEX_PREF = "next_voice_search_hint"; /** * Preference key used to store the time at which the first voice search hint was displayed. */ private static final String FIRST_VOICE_HINT_DISPLAY_TIME = "first_voice_search_hint_time"; /** * Preference key for the version of voice search we last got hints from. */ private static final String LAST_SEEN_VOICE_SEARCH_VERSION = "voice_search_version"; /** * Preference key for storing whether searches always go to google.com. Public * so that it can be used by PreferenceControllers. */ public static final String USE_GOOGLE_COM_PREF = "use_google_com"; /** * Preference key for the base search URL. This value is normally set by * a SearchBaseUrlHelper instance. Public so classes can listen to changes * on this key. */ public static final String SEARCH_BASE_DOMAIN_PREF = "search_base_domain"; /** * This is the time at which the base URL was stored, and is set using * @link{System.currentTimeMillis()}. */ private static final String SEARCH_BASE_DOMAIN_APPLY_TIME = "search_base_domain_apply_time"; /** * Prefix of per-corpus enable preference */ private static final String CORPUS_ENABLED_PREF_PREFIX = "enable_corpus_"; private final Context mContext; private final Config mConfig; public SearchSettingsImpl(Context context, Config config) { mContext = context; mConfig = config; } protected Context getContext() { return mContext; } protected Config getConfig() { return mConfig; } public void upgradeSettingsIfNeeded() { } public Intent getSearchableItemsIntent() { Intent intent = new Intent(ACTION_SEARCHABLE_ITEMS); intent.setPackage(getContext().getPackageName()); return intent; } /** * Gets the preference key of the preference for whether the given corpus * is enabled. The preference is stored in the {@link #PREFERENCES_NAME} * preferences file. */ public static String getCorpusEnabledPreference(Corpus corpus) { return CORPUS_ENABLED_PREF_PREFIX + corpus.getName(); } public boolean isCorpusEnabled(Corpus corpus) { boolean defaultEnabled = corpus.isCorpusDefaultEnabled(); String sourceEnabledPref = getCorpusEnabledPreference(corpus); return getSearchPreferences().getBoolean(sourceEnabledPref, defaultEnabled); } public SharedPreferences getSearchPreferences() { return getContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); } protected void storeBoolean(String name, boolean value) { SharedPreferencesCompat.apply(getSearchPreferences().edit().putBoolean(name, value)); } protected void storeInt(String name, int value) { SharedPreferencesCompat.apply(getSearchPreferences().edit().putInt(name, value)); } protected void storeLong(String name, long value) { SharedPreferencesCompat.apply(getSearchPreferences().edit().putLong(name, value)); } protected void storeString(String name, String value) { SharedPreferencesCompat.apply(getSearchPreferences().edit().putString(name, value)); } protected void removePref(String name) { SharedPreferencesCompat.apply(getSearchPreferences().edit().remove(name)); } /** * Informs our listeners about the updated settings data. */ public 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); getContext().sendBroadcast(intent); } public void addMenuItems(Menu menu, boolean showDisabled) { MenuInflater inflater = new MenuInflater(getContext()); inflater.inflate(R.menu.settings, menu); MenuItem item = menu.findItem(R.id.menu_settings); item.setIntent(getSearchSettingsIntent()); } public Intent getSearchSettingsIntent() { Intent settings = new Intent(SearchManager.INTENT_ACTION_SEARCH_SETTINGS); settings.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); settings.setPackage(getContext().getPackageName()); return settings; } public int getNextVoiceSearchHintIndex(int size) { int i = getAndIncrementIntPreference(getSearchPreferences(), NEXT_VOICE_SEARCH_HINT_INDEX_PREF); return i % size; } // TODO: Could this be made atomic to avoid races? private int getAndIncrementIntPreference(SharedPreferences prefs, String name) { int i = prefs.getInt(name, 0); storeInt(name, i + 1); return i; } public void resetVoiceSearchHintFirstSeenTime() { storeLong(FIRST_VOICE_HINT_DISPLAY_TIME, System.currentTimeMillis()); } public boolean haveVoiceSearchHintsExpired(int currentVoiceSearchVersion) { SharedPreferences prefs = getSearchPreferences(); if (currentVoiceSearchVersion != 0) { long currentTime = System.currentTimeMillis(); int lastVoiceSearchVersion = prefs.getInt(LAST_SEEN_VOICE_SEARCH_VERSION, 0); long firstHintTime = prefs.getLong(FIRST_VOICE_HINT_DISPLAY_TIME, 0); if (firstHintTime == 0 || currentVoiceSearchVersion != lastVoiceSearchVersion) { SharedPreferencesCompat.apply(prefs.edit() .putInt(LAST_SEEN_VOICE_SEARCH_VERSION, currentVoiceSearchVersion) .putLong(FIRST_VOICE_HINT_DISPLAY_TIME, currentTime)); firstHintTime = currentTime; } if (currentTime - firstHintTime > getConfig().getVoiceSearchHintActivePeriod()) { if (DBG) Log.d(TAG, "Voice seach hint period expired; not showing hints."); return true; } else { return false; } } else { if (DBG) Log.d(TAG, "Could not determine voice search version; not showing hints."); return true; } } public boolean allowWebSearchShortcuts() { return true; } /** * @return true if user searches should always be based at google.com, false * otherwise. */ @Override public boolean shouldUseGoogleCom() { // Note that this preserves the old behaviour of using google.com // for searches, with the gl= parameter set. return getSearchPreferences().getBoolean(USE_GOOGLE_COM_PREF, true); } @Override public void setUseGoogleCom(boolean useGoogleCom) { storeBoolean(USE_GOOGLE_COM_PREF, useGoogleCom); } @Override public long getSearchBaseDomainApplyTime() { return getSearchPreferences().getLong(SEARCH_BASE_DOMAIN_APPLY_TIME, -1); } @Override public String getSearchBaseDomain() { // Note that the only time this will return null is on the first run // of the app, or when settings have been cleared. Callers should // ideally check that getSearchBaseDomainApplyTime() is not -1 before // calling this function. return getSearchPreferences().getString(SEARCH_BASE_DOMAIN_PREF, null); } @Override public void setSearchBaseDomain(String searchBaseUrl) { Editor sharedPrefEditor = getSearchPreferences().edit(); sharedPrefEditor.putString(SEARCH_BASE_DOMAIN_PREF, searchBaseUrl); sharedPrefEditor.putLong(SEARCH_BASE_DOMAIN_APPLY_TIME, System.currentTimeMillis()); SharedPreferencesCompat.apply(sharedPrefEditor); } }