/* 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.settings.search import android.content.Context import android.content.res.Resources import android.graphics.drawable.BitmapDrawable import android.util.AttributeSet import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.CompoundButton import android.widget.RadioGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import kotlinx.android.synthetic.main.search_engine_radio_button.view.* import mozilla.components.browser.search.SearchEngine import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings abstract class SearchEngineListPreference @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.preferenceStyle ) : Preference(context, attrs, defStyleAttr), CompoundButton.OnCheckedChangeListener { protected var searchEngines: List = emptyList() protected var searchEngineGroup: RadioGroup? = null protected abstract val itemResId: Int init { layoutResource = R.layout.preference_search_engine_chooser } override fun onBindViewHolder(holder: PreferenceViewHolder?) { super.onBindViewHolder(holder) searchEngineGroup = holder!!.itemView.findViewById(R.id.search_engine_group) val context = searchEngineGroup!!.context searchEngines = context.components.search.searchEngineManager.getSearchEngines(context) .sortedBy { it.name } refreshSearchEngineViews(context) } protected abstract fun onSearchEngineSelected(searchEngine: SearchEngine) protected abstract fun updateDefaultItem(defaultButton: CompoundButton) private fun refreshSearchEngineViews(context: Context) { if (searchEngineGroup == null) { // We want to refresh the search engine list of this preference in onResume, // but the first time this preference is created onResume is called before onCreateView // so searchEngineGroup is not set yet. return } // To get the default search engine we have to pass in a name that doesn't exist // https://github.com/mozilla-mobile/android-components/issues/3344 val defaultSearchEngine = context.components.search.searchEngineManager.getDefaultSearchEngine( context, THIS_IS_A_HACK_FIX_ME ) val selectedSearchEngine = context.components.search.searchEngineManager.getDefaultSearchEngine( context, context.settings().defaultSearchEngineName ).identifier searchEngineGroup!!.removeAllViews() val layoutInflater = LayoutInflater.from(context) val layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) val setupSearchEngineItem: (Int, SearchEngine) -> Unit = { index, engine -> val engineId = engine.identifier val engineItem = makeButtonFromSearchEngine(engine, layoutInflater, context.resources) engineItem.id = index engineItem.tag = engineId if (engineId == selectedSearchEngine) { updateDefaultItem(engineItem.radio_button) } searchEngineGroup!!.addView(engineItem, layoutParams) } setupSearchEngineItem(0, defaultSearchEngine) searchEngines .filter { it.identifier != defaultSearchEngine.identifier } .forEachIndexed(setupSearchEngineItem) } private fun makeButtonFromSearchEngine( engine: SearchEngine, layoutInflater: LayoutInflater, res: Resources ): View { val wrapper = layoutInflater.inflate(itemResId, null) as ConstraintLayout wrapper.setOnClickListener { wrapper.radio_button.isChecked = true } wrapper.radio_button.setOnCheckedChangeListener(this) wrapper.engine_text.text = engine.name val iconSize = res.getDimension(R.dimen.preference_icon_drawable_size).toInt() val engineIcon = BitmapDrawable(res, engine.icon) engineIcon.setBounds(0, 0, iconSize, iconSize) wrapper.engine_icon.setImageDrawable(engineIcon) return wrapper } override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { searchEngines.forEach { engine -> val wrapper: ConstraintLayout = searchEngineGroup?.findViewWithTag(engine.identifier) ?: return when (wrapper.radio_button == buttonView) { true -> onSearchEngineSelected(engine) false -> { wrapper.radio_button.setOnCheckedChangeListener(null) wrapper.radio_button.isChecked = false wrapper.radio_button.setOnCheckedChangeListener(this) } } } } companion object { private const val THIS_IS_A_HACK_FIX_ME = "." } }