Drop our fixed version of the old removed search engine provider
parent
6d4a47921c
commit
3d8b00f840
@ -1,306 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.components.searchengine
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.search.SearchEngine
|
||||
import mozilla.components.browser.search.provider.AssetsSearchEngineProvider
|
||||
import mozilla.components.browser.search.provider.SearchEngineList
|
||||
import mozilla.components.browser.search.provider.SearchEngineProvider
|
||||
import mozilla.components.browser.search.provider.filter.SearchEngineFilter
|
||||
import mozilla.components.browser.search.provider.localization.LocaleSearchLocalizationProvider
|
||||
import mozilla.components.browser.search.provider.localization.SearchLocalizationProvider
|
||||
import mozilla.components.service.location.LocationService
|
||||
import mozilla.components.service.location.MozillaLocationService
|
||||
import mozilla.components.service.location.search.RegionSearchLocalizationProvider
|
||||
import org.mozilla.fenix.BuildConfig
|
||||
import org.mozilla.fenix.Config
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.perf.runBlockingIncrement
|
||||
import java.util.Locale
|
||||
|
||||
@SuppressWarnings("TooManyFunctions")
|
||||
open class FenixSearchEngineProvider(
|
||||
private val context: Context
|
||||
) : SearchEngineProvider, CoroutineScope by CoroutineScope(Job() + Dispatchers.IO) {
|
||||
private val shouldMockMLS = Config.channel.isDebug || BuildConfig.MLS_TOKEN.isEmpty()
|
||||
private val locationService: LocationService = if (shouldMockMLS) {
|
||||
LocationService.dummy()
|
||||
} else {
|
||||
MozillaLocationService(
|
||||
context,
|
||||
context.components.core.client,
|
||||
BuildConfig.MLS_TOKEN
|
||||
)
|
||||
}
|
||||
|
||||
// We have two search engine types: one based on MLS reported region, one based only on Locale.
|
||||
// There are multiple steps involved in returning the default search engine for example.
|
||||
// Simplest and most effective way to make sure the MLS engines do not mix with Locale based engines
|
||||
// is to use the same type of engines for the entire duration of the app's run.
|
||||
// See fenix/issues/11875
|
||||
private val isRegionCachedByLocationService = locationService.hasRegionCached()
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
open val localizationProvider: SearchLocalizationProvider =
|
||||
RegionSearchLocalizationProvider(locationService)
|
||||
|
||||
/**
|
||||
* Unfiltered list of search engines based on locale.
|
||||
*/
|
||||
open var baseSearchEngines = async {
|
||||
AssetsSearchEngineProvider(localizationProvider)
|
||||
.loadSearchEngines(context)
|
||||
}
|
||||
|
||||
private val loadedRegion = async { localizationProvider.determineRegion() }
|
||||
|
||||
// https://github.com/mozilla-mobile/fenix/issues/9935
|
||||
// Adds a Locale search engine provider as a fallback in case the MLS lookup takes longer
|
||||
// than the time it takes for a user to try to search.
|
||||
private val fallbackLocationService: SearchLocalizationProvider = LocaleSearchLocalizationProvider()
|
||||
private val fallBackProvider =
|
||||
AssetsSearchEngineProvider(fallbackLocationService)
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
open val fallbackEngines = async { fallBackProvider.loadSearchEngines(context) }
|
||||
private val fallbackRegion = async { fallbackLocationService.determineRegion() }
|
||||
|
||||
/**
|
||||
* Default bundled search engines based on locale.
|
||||
*/
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
open val bundledSearchEngines = async {
|
||||
val defaultEngineIdentifiers =
|
||||
baseSearchEngines.await().list.map { it.identifier }.toSet()
|
||||
AssetsSearchEngineProvider(
|
||||
localizationProvider,
|
||||
filters = listOf(object : SearchEngineFilter {
|
||||
override fun filter(context: Context, searchEngine: SearchEngine): Boolean {
|
||||
return BUNDLED_SEARCH_ENGINES.contains(searchEngine.identifier) &&
|
||||
!defaultEngineIdentifiers.contains(searchEngine.identifier)
|
||||
}
|
||||
}),
|
||||
additionalIdentifiers = BUNDLED_SEARCH_ENGINES
|
||||
).loadSearchEngines(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Search engines that have been manually added by a user.
|
||||
*/
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
open var customSearchEngines = async {
|
||||
CustomSearchEngineProvider().loadSearchEngines(context)
|
||||
}
|
||||
|
||||
private var loadedSearchEngines = refreshInstalledEngineListAsync(baseSearchEngines)
|
||||
|
||||
// https://github.com/mozilla-mobile/fenix/issues/9935
|
||||
// Create new getter that will return the fallback SearchEngineList if
|
||||
// the main one hasn't completed yet
|
||||
private val searchEngines: Deferred<SearchEngineList>
|
||||
get() =
|
||||
if (isRegionCachedByLocationService || shouldMockMLS) {
|
||||
loadedSearchEngines
|
||||
} else {
|
||||
refreshInstalledEngineListAsync(fallbackEngines)
|
||||
}
|
||||
|
||||
fun getDefaultEngine(context: Context): SearchEngine {
|
||||
val engines = installedSearchEngines(context)
|
||||
val selectedName = context.settings().defaultSearchEngineName
|
||||
|
||||
return engines.list.find { it.name == selectedName }
|
||||
?: engines.default
|
||||
?: engines.list.first()
|
||||
}
|
||||
|
||||
// We should only be setting the default search engine here
|
||||
fun setDefaultEngine(context: Context, id: String) {
|
||||
val engines = installedSearchEngines(context)
|
||||
val newDefault = engines.list.find { it.name == id }
|
||||
?: engines.default
|
||||
?: engines.list.first()
|
||||
|
||||
context.settings().defaultSearchEngineName = newDefault.name
|
||||
context.components.search.searchEngineManager.defaultSearchEngine = newDefault
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of all SearchEngines that are currently active. These are the engines that
|
||||
* are readily available throughout the app. Includes all installed engines, both
|
||||
* default and custom
|
||||
*/
|
||||
fun installedSearchEngines(context: Context): SearchEngineList = runBlockingIncrement {
|
||||
val installedIdentifiers = installedSearchEngineIdentifiers(context)
|
||||
val defaultList = searchEngines.await()
|
||||
|
||||
defaultList.copy(
|
||||
list = defaultList.list.filter {
|
||||
installedIdentifiers.contains(it.identifier)
|
||||
}.sortedBy {
|
||||
it.name.toLowerCase(Locale.getDefault())
|
||||
},
|
||||
default = defaultList.default?.let {
|
||||
if (installedIdentifiers.contains(it.identifier)) {
|
||||
it
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun allSearchEngineIdentifiers() = runBlockingIncrement {
|
||||
loadedSearchEngines.await().list.map { it.identifier }
|
||||
}
|
||||
|
||||
fun uninstalledSearchEngines(context: Context): SearchEngineList = runBlockingIncrement {
|
||||
val installedIdentifiers = installedSearchEngineIdentifiers(context)
|
||||
val engineList = loadedSearchEngines.await()
|
||||
|
||||
return@runBlockingIncrement engineList.copy(
|
||||
list = engineList.list.filterNot { installedIdentifiers.contains(it.identifier) }
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun loadSearchEngines(context: Context): SearchEngineList {
|
||||
return installedSearchEngines(context)
|
||||
}
|
||||
|
||||
fun installSearchEngine(
|
||||
context: Context,
|
||||
searchEngine: SearchEngine,
|
||||
isCustom: Boolean = false
|
||||
) = runBlockingIncrement {
|
||||
if (isCustom) {
|
||||
val searchUrl = searchEngine.getSearchTemplate()
|
||||
CustomSearchEngineStore.addSearchEngine(context, searchEngine.name, searchUrl)
|
||||
reload()
|
||||
} else {
|
||||
val installedIdentifiers = installedSearchEngineIdentifiers(context).toMutableSet()
|
||||
installedIdentifiers.add(searchEngine.identifier)
|
||||
prefs(context).edit()
|
||||
.putStringSet(
|
||||
localeAwareInstalledEnginesKey(), installedIdentifiers
|
||||
).apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun uninstallSearchEngine(
|
||||
context: Context,
|
||||
searchEngine: SearchEngine,
|
||||
isCustom: Boolean = false
|
||||
) = runBlockingIncrement {
|
||||
if (isCustom) {
|
||||
CustomSearchEngineStore.removeSearchEngine(context, searchEngine.identifier)
|
||||
reload()
|
||||
} else {
|
||||
val installedIdentifiers = installedSearchEngineIdentifiers(context).toMutableSet()
|
||||
installedIdentifiers.remove(searchEngine.identifier)
|
||||
prefs(context).edit().putStringSet(
|
||||
localeAwareInstalledEnginesKey(),
|
||||
installedIdentifiers
|
||||
).apply()
|
||||
}
|
||||
}
|
||||
|
||||
fun reload() {
|
||||
launch {
|
||||
customSearchEngines = async { CustomSearchEngineProvider().loadSearchEngines(context) }
|
||||
loadedSearchEngines = refreshInstalledEngineListAsync(baseSearchEngines)
|
||||
}
|
||||
}
|
||||
|
||||
// When we change the locale we need to update the baseSearchEngines list
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
open fun updateBaseSearchEngines() {
|
||||
baseSearchEngines = async {
|
||||
AssetsSearchEngineProvider(localizationProvider)
|
||||
.loadSearchEngines(context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshInstalledEngineListAsync(
|
||||
engines: Deferred<SearchEngineList>
|
||||
): Deferred<SearchEngineList> = async {
|
||||
val engineList = engines.await()
|
||||
val bundledList = bundledSearchEngines.await().list
|
||||
val customList = customSearchEngines.await().list
|
||||
|
||||
return@async engineList.copy(list = engineList.list + bundledList + customList)
|
||||
}
|
||||
|
||||
private fun prefs(context: Context) = context.getSharedPreferences(
|
||||
PREF_FILE_SEARCH_ENGINES,
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
suspend fun installedSearchEngineIdentifiers(context: Context): Set<String> {
|
||||
val prefs = prefs(context)
|
||||
val installedEnginesKey = localeAwareInstalledEnginesKey()
|
||||
|
||||
if (!prefs.contains(installedEnginesKey)) {
|
||||
val searchEngines =
|
||||
if (isRegionCachedByLocationService) {
|
||||
baseSearchEngines
|
||||
} else {
|
||||
fallbackEngines
|
||||
}
|
||||
|
||||
val defaultSet = searchEngines.await()
|
||||
.list
|
||||
.map { it.identifier }
|
||||
.toSet()
|
||||
|
||||
prefs.edit().putStringSet(installedEnginesKey, defaultSet).apply()
|
||||
}
|
||||
|
||||
val installedIdentifiers: Set<String> =
|
||||
prefs(context).getStringSet(installedEnginesKey, setOf()) ?: setOf()
|
||||
|
||||
val customEngineIdentifiers =
|
||||
customSearchEngines.await().list.map { it.identifier }.toSet()
|
||||
|
||||
return installedIdentifiers + customEngineIdentifiers
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
suspend fun localeAwareInstalledEnginesKey(): String {
|
||||
val tag = if (isRegionCachedByLocationService) {
|
||||
val localization = loadedRegion.await()
|
||||
val region = localization.region?.let {
|
||||
if (it.isEmpty()) "" else "-$it"
|
||||
}
|
||||
|
||||
"${localization.languageTag}$region"
|
||||
} else {
|
||||
val localization = fallbackRegion.await()
|
||||
val region = localization.region?.let {
|
||||
if (it.isEmpty()) "" else "-$it"
|
||||
}
|
||||
|
||||
"${localization.languageTag}$region-fallback"
|
||||
}
|
||||
|
||||
return "$INSTALLED_ENGINES_KEY-$tag"
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
companion object {
|
||||
val BUNDLED_SEARCH_ENGINES = listOf("reddit", "youtube")
|
||||
const val PREF_FILE_SEARCH_ENGINES = "fenix-search-engine-provider"
|
||||
const val INSTALLED_ENGINES_KEY = "fenix-installed-search-engines"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue