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