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.quicksearchbox; 18 19 import com.android.common.SharedPreferencesCompat; 20 21 import android.app.SearchManager; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.SharedPreferences; 25 import android.content.SharedPreferences.Editor; 26 import android.util.Log; 27 import android.view.Menu; 28 import android.view.MenuInflater; 29 import android.view.MenuItem; 30 31 /** 32 * Manages user settings. 33 */ 34 public class SearchSettingsImpl implements SearchSettings { 35 36 private static final boolean DBG = false; 37 private static final String TAG = "QSB.SearchSettingsImpl"; 38 39 // Name of the preferences file used to store search preference 40 public static final String PREFERENCES_NAME = "SearchSettings"; 41 42 // Intent action that opens the "Searchable Items" preference 43 private static final String ACTION_SEARCHABLE_ITEMS = 44 "com.android.quicksearchbox.action.SEARCHABLE_ITEMS"; 45 46 /** 47 * Preference key used for storing the index of the next voice search hint to show. 48 */ 49 private static final String NEXT_VOICE_SEARCH_HINT_INDEX_PREF = "next_voice_search_hint"; 50 51 /** 52 * Preference key used to store the time at which the first voice search hint was displayed. 53 */ 54 private static final String FIRST_VOICE_HINT_DISPLAY_TIME = "first_voice_search_hint_time"; 55 56 /** 57 * Preference key for the version of voice search we last got hints from. 58 */ 59 private static final String LAST_SEEN_VOICE_SEARCH_VERSION = "voice_search_version"; 60 61 /** 62 * Preference key for storing whether searches always go to google.com. Public 63 * so that it can be used by PreferenceControllers. 64 */ 65 public static final String USE_GOOGLE_COM_PREF = "use_google_com"; 66 67 /** 68 * Preference key for the base search URL. This value is normally set by 69 * a SearchBaseUrlHelper instance. Public so classes can listen to changes 70 * on this key. 71 */ 72 public static final String SEARCH_BASE_DOMAIN_PREF = "search_base_domain"; 73 74 /** 75 * This is the time at which the base URL was stored, and is set using 76 * @link{System.currentTimeMillis()}. 77 */ 78 private static final String SEARCH_BASE_DOMAIN_APPLY_TIME = "search_base_domain_apply_time"; 79 80 /** 81 * Prefix of per-corpus enable preference 82 */ 83 private static final String CORPUS_ENABLED_PREF_PREFIX = "enable_corpus_"; 84 85 private final Context mContext; 86 87 private final Config mConfig; 88 89 public SearchSettingsImpl(Context context, Config config) { 90 mContext = context; 91 mConfig = config; 92 } 93 94 protected Context getContext() { 95 return mContext; 96 } 97 98 protected Config getConfig() { 99 return mConfig; 100 } 101 102 public void upgradeSettingsIfNeeded() { 103 } 104 105 public Intent getSearchableItemsIntent() { 106 Intent intent = new Intent(ACTION_SEARCHABLE_ITEMS); 107 intent.setPackage(getContext().getPackageName()); 108 return intent; 109 } 110 111 /** 112 * Gets the preference key of the preference for whether the given corpus 113 * is enabled. The preference is stored in the {@link #PREFERENCES_NAME} 114 * preferences file. 115 */ 116 public static String getCorpusEnabledPreference(Corpus corpus) { 117 return CORPUS_ENABLED_PREF_PREFIX + corpus.getName(); 118 } 119 120 public boolean isCorpusEnabled(Corpus corpus) { 121 boolean defaultEnabled = corpus.isCorpusDefaultEnabled(); 122 String sourceEnabledPref = getCorpusEnabledPreference(corpus); 123 return getSearchPreferences().getBoolean(sourceEnabledPref, defaultEnabled); 124 } 125 126 public SharedPreferences getSearchPreferences() { 127 return getContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); 128 } 129 130 protected void storeBoolean(String name, boolean value) { 131 SharedPreferencesCompat.apply(getSearchPreferences().edit().putBoolean(name, value)); 132 } 133 134 protected void storeInt(String name, int value) { 135 SharedPreferencesCompat.apply(getSearchPreferences().edit().putInt(name, value)); 136 } 137 138 protected void storeLong(String name, long value) { 139 SharedPreferencesCompat.apply(getSearchPreferences().edit().putLong(name, value)); 140 } 141 142 protected void storeString(String name, String value) { 143 SharedPreferencesCompat.apply(getSearchPreferences().edit().putString(name, value)); 144 } 145 146 protected void removePref(String name) { 147 SharedPreferencesCompat.apply(getSearchPreferences().edit().remove(name)); 148 } 149 150 /** 151 * Informs our listeners about the updated settings data. 152 */ 153 public void broadcastSettingsChanged() { 154 // We use a message broadcast since the listeners could be in multiple processes. 155 Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCH_SETTINGS_CHANGED); 156 Log.i(TAG, "Broadcasting: " + intent); 157 getContext().sendBroadcast(intent); 158 } 159 160 public void addMenuItems(Menu menu, boolean showDisabled) { 161 MenuInflater inflater = new MenuInflater(getContext()); 162 inflater.inflate(R.menu.settings, menu); 163 MenuItem item = menu.findItem(R.id.menu_settings); 164 item.setIntent(getSearchSettingsIntent()); 165 } 166 167 public Intent getSearchSettingsIntent() { 168 Intent settings = new Intent(SearchManager.INTENT_ACTION_SEARCH_SETTINGS); 169 settings.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 170 settings.setPackage(getContext().getPackageName()); 171 return settings; 172 } 173 174 public int getNextVoiceSearchHintIndex(int size) { 175 int i = getAndIncrementIntPreference(getSearchPreferences(), 176 NEXT_VOICE_SEARCH_HINT_INDEX_PREF); 177 return i % size; 178 } 179 180 // TODO: Could this be made atomic to avoid races? 181 private int getAndIncrementIntPreference(SharedPreferences prefs, String name) { 182 int i = prefs.getInt(name, 0); 183 storeInt(name, i + 1); 184 return i; 185 } 186 187 public void resetVoiceSearchHintFirstSeenTime() { 188 storeLong(FIRST_VOICE_HINT_DISPLAY_TIME, System.currentTimeMillis()); 189 } 190 191 public boolean haveVoiceSearchHintsExpired(int currentVoiceSearchVersion) { 192 SharedPreferences prefs = getSearchPreferences(); 193 194 if (currentVoiceSearchVersion != 0) { 195 long currentTime = System.currentTimeMillis(); 196 int lastVoiceSearchVersion = prefs.getInt(LAST_SEEN_VOICE_SEARCH_VERSION, 0); 197 long firstHintTime = prefs.getLong(FIRST_VOICE_HINT_DISPLAY_TIME, 0); 198 if (firstHintTime == 0 || currentVoiceSearchVersion != lastVoiceSearchVersion) { 199 SharedPreferencesCompat.apply(prefs.edit() 200 .putInt(LAST_SEEN_VOICE_SEARCH_VERSION, currentVoiceSearchVersion) 201 .putLong(FIRST_VOICE_HINT_DISPLAY_TIME, currentTime)); 202 firstHintTime = currentTime; 203 } 204 if (currentTime - firstHintTime > getConfig().getVoiceSearchHintActivePeriod()) { 205 if (DBG) Log.d(TAG, "Voice seach hint period expired; not showing hints."); 206 return true; 207 } else { 208 return false; 209 } 210 } else { 211 if (DBG) Log.d(TAG, "Could not determine voice search version; not showing hints."); 212 return true; 213 } 214 } 215 216 public boolean allowWebSearchShortcuts() { 217 return true; 218 } 219 220 /** 221 * @return true if user searches should always be based at google.com, false 222 * otherwise. 223 */ 224 @Override 225 public boolean shouldUseGoogleCom() { 226 // Note that this preserves the old behaviour of using google.com 227 // for searches, with the gl= parameter set. 228 return getSearchPreferences().getBoolean(USE_GOOGLE_COM_PREF, true); 229 } 230 231 @Override 232 public void setUseGoogleCom(boolean useGoogleCom) { 233 storeBoolean(USE_GOOGLE_COM_PREF, useGoogleCom); 234 } 235 236 @Override 237 public long getSearchBaseDomainApplyTime() { 238 return getSearchPreferences().getLong(SEARCH_BASE_DOMAIN_APPLY_TIME, -1); 239 } 240 241 @Override 242 public String getSearchBaseDomain() { 243 // Note that the only time this will return null is on the first run 244 // of the app, or when settings have been cleared. Callers should 245 // ideally check that getSearchBaseDomainApplyTime() is not -1 before 246 // calling this function. 247 return getSearchPreferences().getString(SEARCH_BASE_DOMAIN_PREF, null); 248 } 249 250 @Override 251 public void setSearchBaseDomain(String searchBaseUrl) { 252 Editor sharedPrefEditor = getSearchPreferences().edit(); 253 sharedPrefEditor.putString(SEARCH_BASE_DOMAIN_PREF, searchBaseUrl); 254 sharedPrefEditor.putLong(SEARCH_BASE_DOMAIN_APPLY_TIME, System.currentTimeMillis()); 255 256 SharedPreferencesCompat.apply(sharedPrefEditor); 257 } 258 } 259