| /* |
| * Copyright (C) 2022 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 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 com.android.common.SharedPreferencesCompat |
| |
| /** Manages user settings. */ |
| class SearchSettingsImpl(context: Context?, config: Config?) : SearchSettings { |
| private val mContext: Context? |
| protected val config: Config? |
| protected val context: Context? |
| get() = mContext |
| |
| @Override override fun upgradeSettingsIfNeeded() {} |
| |
| val searchPreferences: SharedPreferences |
| get() = context!!.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) |
| |
| protected fun storeBoolean(name: String?, value: Boolean) { |
| SharedPreferencesCompat.apply(searchPreferences.edit().putBoolean(name, value)) |
| } |
| |
| protected fun storeInt(name: String?, value: Int) { |
| SharedPreferencesCompat.apply(searchPreferences.edit().putInt(name, value)) |
| } |
| |
| protected fun storeLong(name: String?, value: Long) { |
| SharedPreferencesCompat.apply(searchPreferences.edit().putLong(name, value)) |
| } |
| |
| protected fun storeString(name: String?, value: String?) { |
| SharedPreferencesCompat.apply(searchPreferences.edit().putString(name, value)) |
| } |
| |
| protected fun removePref(name: String?) { |
| SharedPreferencesCompat.apply(searchPreferences.edit().remove(name)) |
| } |
| |
| /** Informs our listeners about the updated settings data. */ |
| @Override |
| override fun broadcastSettingsChanged() { |
| // We use a message broadcast since the listeners could be in multiple processes. |
| val intent = Intent(SearchManager.INTENT_ACTION_SEARCH_SETTINGS_CHANGED) |
| Log.i(TAG, "Broadcasting: $intent") |
| context?.sendBroadcast(intent) |
| } |
| |
| @Override |
| override fun getNextVoiceSearchHintIndex(size: Int): Int { |
| val i = getAndIncrementIntPreference(searchPreferences, NEXT_VOICE_SEARCH_HINT_INDEX_PREF) |
| return i % size |
| } |
| |
| // TODO: Could this be made atomic to avoid races? |
| private fun getAndIncrementIntPreference(prefs: SharedPreferences, name: String): Int { |
| val i: Int = prefs.getInt(name, 0) |
| storeInt(name, i + 1) |
| return i |
| } |
| |
| @Override |
| override fun resetVoiceSearchHintFirstSeenTime() { |
| storeLong(FIRST_VOICE_HINT_DISPLAY_TIME, System.currentTimeMillis()) |
| } |
| |
| @Override |
| override fun haveVoiceSearchHintsExpired(currentVoiceSearchVersion: Int): Boolean { |
| val prefs: SharedPreferences = searchPreferences |
| return if (currentVoiceSearchVersion != 0) { |
| val currentTime: Long = System.currentTimeMillis() |
| val lastVoiceSearchVersion: Int = prefs.getInt(LAST_SEEN_VOICE_SEARCH_VERSION, 0) |
| var firstHintTime: Long = prefs.getLong(FIRST_VOICE_HINT_DISPLAY_TIME, 0) |
| if (firstHintTime == 0L || 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 > config!!.voiceSearchHintActivePeriod) { |
| if (DBG) Log.d(TAG, "Voice search hint period expired; not showing hints.") |
| return true |
| } else { |
| false |
| } |
| } else { |
| if (DBG) Log.d(TAG, "Could not determine voice search version; not showing hints.") |
| true |
| } |
| } |
| |
| /** @return true if user searches should always be based at google.com, false otherwise. */ |
| @Override |
| override fun shouldUseGoogleCom(): Boolean { |
| // Note that this preserves the old behaviour of using google.com |
| // for searches, with the gl= parameter set. |
| return searchPreferences.getBoolean(USE_GOOGLE_COM_PREF, true) |
| } |
| |
| @Override |
| override fun setUseGoogleCom(useGoogleCom: Boolean) { |
| storeBoolean(USE_GOOGLE_COM_PREF, useGoogleCom) |
| } |
| |
| @get:Override |
| override val searchBaseDomainApplyTime: Long |
| get() = searchPreferences.getLong(SEARCH_BASE_DOMAIN_APPLY_TIME, -1) |
| |
| // 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. |
| @get:Override |
| @set:Override |
| override var searchBaseDomain: String? |
| get() = searchPreferences.getString(SEARCH_BASE_DOMAIN_PREF, null) |
| set(searchBaseUrl) { |
| val sharedPrefEditor: Editor = searchPreferences.edit() |
| sharedPrefEditor.putString(SEARCH_BASE_DOMAIN_PREF, searchBaseUrl) |
| sharedPrefEditor.putLong(SEARCH_BASE_DOMAIN_APPLY_TIME, System.currentTimeMillis()) |
| SharedPreferencesCompat.apply(sharedPrefEditor) |
| } |
| |
| companion object { |
| private const val DBG = false |
| private const val TAG = "QSB.SearchSettingsImpl" |
| |
| // Name of the preferences file used to store search preference |
| const val PREFERENCES_NAME = "SearchSettings" |
| |
| /** Preference key used for storing the index of the next voice search hint to show. */ |
| private const val 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 const val FIRST_VOICE_HINT_DISPLAY_TIME = "first_voice_search_hint_time" |
| |
| /** Preference key for the version of voice search we last got hints from. */ |
| private const val 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. |
| */ |
| const val 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. |
| */ |
| const val 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 const val SEARCH_BASE_DOMAIN_APPLY_TIME = "search_base_domain_apply_time" |
| } |
| |
| init { |
| mContext = context |
| this.config = config |
| } |
| } |