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.SearchManager;
20import android.content.Context;
21import android.content.Intent;
22import android.content.SharedPreferences;
23import android.content.SharedPreferences.Editor;
24import android.util.Log;
25
26import com.android.common.SharedPreferencesCompat;
27
28/**
29 * Manages user settings.
30 */
31public 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
77    public SearchSettingsImpl(Context context, Config config) {
78        mContext = context;
79        mConfig = config;
80    }
81
82    protected Context getContext() {
83        return mContext;
84    }
85
86    protected Config getConfig() {
87        return mConfig;
88    }
89
90    @Override
91    public void upgradeSettingsIfNeeded() {
92    }
93
94    public SharedPreferences getSearchPreferences() {
95        return getContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
96    }
97
98    protected void storeBoolean(String name, boolean value) {
99        SharedPreferencesCompat.apply(getSearchPreferences().edit().putBoolean(name, value));
100    }
101
102    protected void storeInt(String name, int value) {
103        SharedPreferencesCompat.apply(getSearchPreferences().edit().putInt(name, value));
104    }
105
106    protected void storeLong(String name, long value) {
107        SharedPreferencesCompat.apply(getSearchPreferences().edit().putLong(name, value));
108    }
109
110    protected void storeString(String name, String value) {
111        SharedPreferencesCompat.apply(getSearchPreferences().edit().putString(name, value));
112    }
113
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
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
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?
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
144    public void resetVoiceSearchHintFirstSeenTime() {
145        storeLong(FIRST_VOICE_HINT_DISPLAY_TIME, System.currentTimeMillis());
146    }
147
148    @Override
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
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
186    public void setUseGoogleCom(boolean useGoogleCom) {
187        storeBoolean(USE_GOOGLE_COM_PREF, useGoogleCom);
188    }
189
190    @Override
191    public long getSearchBaseDomainApplyTime() {
192        return getSearchPreferences().getLong(SEARCH_BASE_DOMAIN_APPLY_TIME, -1);
193    }
194
195    @Override
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
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