blob: 8168f78d7116eee9c913cd57ad1fab2ad4187491 [file] [log] [blame]
/*
* 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
}
}