1
0
Fork 0

For #3278 - Fixes search engine selector layout

master
Jeff Boek 2019-06-10 12:48:53 -07:00
parent 811fbfc5fd
commit a86ec60f8f
3 changed files with 70 additions and 38 deletions

View File

@ -7,15 +7,13 @@ package org.mozilla.fenix.settings
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.RadioGroup
import androidx.preference.PreferenceViewHolder import androidx.preference.PreferenceViewHolder
import mozilla.components.browser.search.SearchEngine
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.Settings
class RadioSearchEngineListPreference : SearchEngineListPreference, class RadioSearchEngineListPreference : SearchEngineListPreference {
RadioGroup.OnCheckedChangeListener {
override val itemResId: Int override val itemResId: Int
get() = R.layout.search_engine_radio_button get() = R.layout.search_engine_radio_button
@ -29,24 +27,14 @@ class RadioSearchEngineListPreference : SearchEngineListPreference,
override fun onBindViewHolder(holder: PreferenceViewHolder?) { override fun onBindViewHolder(holder: PreferenceViewHolder?) {
super.onBindViewHolder(holder) super.onBindViewHolder(holder)
searchEngineGroup!!.setOnCheckedChangeListener(this)
} }
override fun updateDefaultItem(defaultButton: CompoundButton) { override fun updateDefaultItem(defaultButton: CompoundButton) {
defaultButton.isChecked = true defaultButton.isChecked = true
} }
override fun onCheckedChanged(group: RadioGroup, checkedId: Int) { override fun onSearchEngineSelected(searchEngine: SearchEngine) {
/* onCheckedChanged is called intermittently before the search engine table is full, so we context.components.search.searchEngineManager.defaultSearchEngine = searchEngine
must check these conditions to prevent crashes and inconsistent states. */ Settings.getInstance(context).setDefaultSearchEngineByName(searchEngine.name)
if (group.childCount != searchEngines.count() || group.getChildAt(checkedId) == null ||
!group.getChildAt(checkedId).isPressed
) {
return
}
val newDefaultEngine = searchEngines[checkedId]
context.components.search.searchEngineManager.defaultSearchEngine = newDefaultEngine
Settings.getInstance(group.context).setDefaultSearchEngineByName(newDefaultEngine.name)
} }
} }

View File

@ -9,12 +9,15 @@ import android.content.res.Resources
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.RadioGroup import android.widget.RadioGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder import androidx.preference.PreferenceViewHolder
import kotlinx.android.synthetic.main.search_engine_radio_button.view.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -25,7 +28,7 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.Settings
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
abstract class SearchEngineListPreference : Preference, CoroutineScope { abstract class SearchEngineListPreference : Preference, CompoundButton.OnCheckedChangeListener, CoroutineScope {
private val job = Job() private val job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main get() = job + Dispatchers.Main
@ -62,6 +65,7 @@ abstract class SearchEngineListPreference : Preference, CoroutineScope {
super.onDetached() super.onDetached()
} }
protected abstract fun onSearchEngineSelected(searchEngine: SearchEngine)
protected abstract fun updateDefaultItem(defaultButton: CompoundButton) protected abstract fun updateDefaultItem(defaultButton: CompoundButton)
private fun refreshSearchEngineViews(context: Context) { private fun refreshSearchEngineViews(context: Context) {
@ -93,7 +97,7 @@ abstract class SearchEngineListPreference : Preference, CoroutineScope {
engineItem.id = i engineItem.id = i
engineItem.tag = engineId engineItem.tag = engineId
if (engineId == defaultSearchEngine) { if (engineId == defaultSearchEngine) {
updateDefaultItem(engineItem) updateDefaultItem(engineItem.radio_button)
} }
searchEngineGroup!!.addView(engineItem, layoutParams) searchEngineGroup!!.addView(engineItem, layoutParams)
} }
@ -103,19 +107,38 @@ abstract class SearchEngineListPreference : Preference, CoroutineScope {
engine: SearchEngine, engine: SearchEngine,
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater,
res: Resources res: Resources
): CompoundButton { ): View {
val buttonItem = layoutInflater.inflate(itemResId, null) as CompoundButton val wrapper = layoutInflater.inflate(itemResId, null) as ConstraintLayout
buttonItem.text = engine.name wrapper.setOnClickListener { wrapper.radio_button.isChecked = true }
wrapper.radio_button.setOnCheckedChangeListener(this)
val buttonItem = wrapper.radio_button
wrapper.engine_text.text = engine.name
val iconSize = res.getDimension(R.dimen.preference_icon_drawable_size).toInt() val iconSize = res.getDimension(R.dimen.preference_icon_drawable_size).toInt()
val engineIcon = BitmapDrawable(res, engine.icon) val engineIcon = BitmapDrawable(res, engine.icon)
engineIcon.setBounds(0, 0, iconSize, iconSize) engineIcon.setBounds(0, 0, iconSize, iconSize)
wrapper.engine_icon.setImageDrawable(engineIcon)
val attr = val attr =
ThemeManager.resolveAttribute(android.R.attr.listChoiceIndicatorSingle, context) ThemeManager.resolveAttribute(android.R.attr.listChoiceIndicatorSingle, context)
val buttonDrawable = ContextCompat.getDrawable(context, attr) val buttonDrawable = ContextCompat.getDrawable(context, attr)
buttonDrawable.apply { buttonDrawable.apply {
this?.setBounds(0, 0, this.intrinsicWidth, this.intrinsicHeight) this?.setBounds(0, 0, this.intrinsicWidth, this.intrinsicHeight)
} }
buttonItem.setCompoundDrawables(engineIcon, null, buttonDrawable, null) buttonItem.setCompoundDrawablesRelative(buttonDrawable, null, null, null)
return buttonItem 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)
}
}
}
} }
} }

View File

@ -2,19 +2,40 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this - 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/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/radio_button" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/search_engine_radio_button_height" android:layout_height="@dimen/search_engine_radio_button_height"
android:layout_gravity="start" android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?android:selectableItemBackground" android:background="?android:selectableItemBackground"
android:button="@null" android:clickable="true"
android:drawableStart="@drawable/ic_close" android:focusable="true">
android:drawableEnd="?android:attr/listChoiceIndicatorSingle" <RadioButton
android:drawablePadding="16dp" android:id="@+id/radio_button"
android:paddingStart="@dimen/radio_button_padding_horizontal" android:layout_width="@dimen/search_engine_radio_button_height"
android:paddingTop="@dimen/radio_button_padding_vertical" android:layout_height="@dimen/search_engine_radio_button_height"
android:paddingEnd="@dimen/radio_button_padding_horizontal" android:button="@null"
android:paddingBottom="@dimen/radio_button_padding_vertical" android:textAlignment="textStart"
android:textAlignment="textStart" android:textAppearance="?android:attr/textAppearanceListItem"
android:textAppearance="?android:attr/textAppearanceListItem" /> android:layout_marginStart="@dimen/radio_button_padding_horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<ImageView
android:id="@+id/engine_icon"
android:importantForAccessibility="no"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintStart_toEndOf="@id/radio_button"
app:layout_constraintTop_toTopOf="@id/radio_button"
app:layout_constraintBottom_toBottomOf="@id/radio_button" />
<TextView
android:id="@+id/engine_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/radio_button_padding_horizontal"
android:layout_marginEnd="@dimen/radio_button_padding_horizontal"
app:layout_constraintStart_toEndOf="@id/engine_icon"
app:layout_constraintTop_toTopOf="@id/engine_icon"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>