1
0
Fork 0

Extract radio group logic into helper (#11493)

master
Tiger Oakes 2020-07-06 17:09:42 -07:00 committed by GitHub
parent 01338274e0
commit 8e8e5aea36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 149 additions and 86 deletions

View File

@ -14,7 +14,7 @@ import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.ViewHolder
import org.mozilla.fenix.utils.view.ViewHolder
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getIconColor
import org.mozilla.fenix.ext.increaseTapArea

View File

@ -6,9 +6,9 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.View
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.no_collections_message.view.*
import kotlinx.android.synthetic.main.no_collections_message.*
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.ViewHolder
import org.mozilla.fenix.utils.view.ViewHolder
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
open class NoCollectionsMessageViewHolder(
@ -18,11 +18,12 @@ open class NoCollectionsMessageViewHolder(
) : ViewHolder(view) {
init {
view.add_tabs_to_collections_button.setOnClickListener {
add_tabs_to_collections_button.setOnClickListener {
interactor.onAddTabsToCollectionTapped()
}
view.add_tabs_to_collections_button.isVisible = hasNormalTabsOpened
add_tabs_to_collections_button.isVisible = hasNormalTabsOpened
}
companion object {
const val LAYOUT_ID = R.layout.no_collections_message
}

View File

@ -13,7 +13,7 @@ import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.android.util.dpToFloat
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.ViewHolder
import org.mozilla.fenix.utils.view.ViewHolder
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.loadIntoView

View File

@ -9,18 +9,14 @@ import android.os.Build.VERSION.SDK_INT
import android.view.View
import androidx.appcompat.app.AppCompatDelegate
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.clickable_region_automatic
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_automatic_radio_button
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_dark_image
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_dark_radio_button
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_light_image
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_light_radio_button
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.Event.OnboardingThemePicker.Theme
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.onboarding.OnboardingRadioButton
import org.mozilla.fenix.utils.view.addToRadioGroup
class OnboardingThemePickerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
@ -35,17 +31,14 @@ class OnboardingThemePickerViewHolder(view: View) : RecyclerView.ViewHolder(view
R.string.pref_key_auto_battery_theme
}
radioLightTheme.addToRadioGroup(radioDarkTheme)
radioLightTheme.addToRadioGroup(radioFollowDeviceTheme)
addToRadioGroup(
radioLightTheme,
radioDarkTheme,
radioFollowDeviceTheme
)
radioLightTheme.addIllustration(view.theme_light_image)
radioDarkTheme.addToRadioGroup(radioLightTheme)
radioDarkTheme.addToRadioGroup(radioFollowDeviceTheme)
radioDarkTheme.addIllustration(view.theme_dark_image)
radioFollowDeviceTheme.addToRadioGroup(radioDarkTheme)
radioFollowDeviceTheme.addToRadioGroup(radioLightTheme)
view.theme_dark_image.setOnClickListener {
it.context.components.analytics.metrics.track(Event.OnboardingThemePicker(Theme.DARK))
radioDarkTheme.performClick()

View File

@ -6,10 +6,7 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.toolbar_bottom_image
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.toolbar_bottom_radio_button
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.toolbar_top_image
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.toolbar_top_radio_button
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.Event.OnboardingToolbarPosition.Position
@ -17,6 +14,7 @@ import org.mozilla.fenix.ext.asActivity
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.onboarding.OnboardingRadioButton
import org.mozilla.fenix.utils.view.addToRadioGroup
class OnboardingToolbarPositionPickerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
@ -25,10 +23,8 @@ class OnboardingToolbarPositionPickerViewHolder(view: View) : RecyclerView.ViewH
val radioBottomToolbar = view.toolbar_bottom_radio_button
val radio: OnboardingRadioButton
radioTopToolbar.addToRadioGroup(radioBottomToolbar)
addToRadioGroup(radioTopToolbar, radioBottomToolbar)
radioTopToolbar.addIllustration(view.toolbar_top_image)
radioBottomToolbar.addToRadioGroup(radioTopToolbar)
radioBottomToolbar.addIllustration(view.toolbar_bottom_image)
radio = if (view.context.settings().shouldUseBottomToolbar) {

View File

@ -14,6 +14,7 @@ import org.mozilla.fenix.components.metrics.Event.OnboardingTrackingProtection.S
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.onboarding.OnboardingRadioButton
import org.mozilla.fenix.utils.view.addToRadioGroup
class OnboardingTrackingProtectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
@ -48,8 +49,7 @@ class OnboardingTrackingProtectionViewHolder(view: View) : RecyclerView.ViewHold
updateRadioGroupState(view, isChecked)
standardTrackingProtection.addToRadioGroup(strictTrackingProtection)
strictTrackingProtection.addToRadioGroup(standardTrackingProtection)
addToRadioGroup(standardTrackingProtection, strictTrackingProtection)
strictTrackingProtection.isChecked =
itemView.context.settings().useStrictTrackingProtection

View File

@ -15,7 +15,7 @@ import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.ViewHolder
import org.mozilla.fenix.utils.view.ViewHolder
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.loadIntoView
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor

View File

@ -12,9 +12,14 @@ import androidx.core.content.edit
import androidx.core.content.withStyledAttributes
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.utils.view.GroupableRadioButton
import org.mozilla.fenix.utils.view.uncheckAll
class OnboardingRadioButton(context: Context, attrs: AttributeSet) : AppCompatRadioButton(context, attrs) {
private val radioGroups = mutableListOf<OnboardingRadioButton>()
class OnboardingRadioButton(
context: Context,
attrs: AttributeSet
) : AppCompatRadioButton(context, attrs), GroupableRadioButton {
private val radioGroups = mutableListOf<GroupableRadioButton>()
private var illustration: ImageView? = null
private var clickListener: (() -> Unit)? = null
var key: Int = 0
@ -29,7 +34,7 @@ class OnboardingRadioButton(context: Context, attrs: AttributeSet) : AppCompatRa
}
}
fun addToRadioGroup(radioButton: OnboardingRadioButton) {
override fun addToRadioGroup(radioButton: GroupableRadioButton) {
radioGroups.add(radioButton)
}
@ -49,7 +54,7 @@ class OnboardingRadioButton(context: Context, attrs: AttributeSet) : AppCompatRa
}
}
fun updateRadioValue(isChecked: Boolean) {
override fun updateRadioValue(isChecked: Boolean) {
this.isChecked = isChecked
illustration?.let {
it.isSelected = isChecked
@ -61,7 +66,7 @@ class OnboardingRadioButton(context: Context, attrs: AttributeSet) : AppCompatRa
private fun toggleRadioGroups() {
if (isChecked) {
radioGroups.forEach { it.updateRadioValue(false) }
radioGroups.uncheckAll()
}
}
}

View File

@ -16,6 +16,7 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.utils.view.addToRadioGroup
/**
* Lets the user customize the UI.
@ -48,23 +49,15 @@ class CustomizationFragment : PreferenceFragmentCompat() {
}
private fun setupRadioGroups() {
radioLightTheme.addToRadioGroup(radioDarkTheme)
radioDarkTheme.addToRadioGroup(radioLightTheme)
if (SDK_INT >= Build.VERSION_CODES.P) {
radioLightTheme.addToRadioGroup(radioFollowDeviceTheme)
radioDarkTheme.addToRadioGroup(radioFollowDeviceTheme)
radioFollowDeviceTheme.addToRadioGroup(radioDarkTheme)
radioFollowDeviceTheme.addToRadioGroup(radioLightTheme)
} else {
radioLightTheme.addToRadioGroup(radioAutoBatteryTheme)
radioDarkTheme.addToRadioGroup(radioAutoBatteryTheme)
radioAutoBatteryTheme.addToRadioGroup(radioLightTheme)
radioAutoBatteryTheme.addToRadioGroup(radioDarkTheme)
}
addToRadioGroup(
radioLightTheme,
radioDarkTheme,
if (SDK_INT >= Build.VERSION_CODES.P) {
radioFollowDeviceTheme
} else {
radioAutoBatteryTheme
}
)
}
private fun bindLightTheme() {
@ -132,7 +125,6 @@ class CustomizationFragment : PreferenceFragmentCompat() {
topPreference.setCheckedWithoutClickListener(!requireContext().settings().shouldUseBottomToolbar)
bottomPreference.setCheckedWithoutClickListener(requireContext().settings().shouldUseBottomToolbar)
topPreference.addToRadioGroup(bottomPreference)
bottomPreference.addToRadioGroup(topPreference)
addToRadioGroup(topPreference, bottomPreference)
}
}

View File

@ -16,12 +16,15 @@ import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.utils.view.GroupableRadioButton
import org.mozilla.fenix.utils.view.uncheckAll
@Suppress("RestrictedApi", "PrivateResource")
open class RadioButtonPreference @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : Preference(context, attrs) {
private val radioGroups = mutableListOf<RadioButtonPreference>()
) : Preference(context, attrs), GroupableRadioButton {
private val radioGroups = mutableListOf<GroupableRadioButton>()
private var summaryView: TextView? = null
private var titleView: TextView? = null
private var radioButton: RadioButton? = null
@ -55,8 +58,8 @@ open class RadioButtonPreference @JvmOverloads constructor(
}
}
fun addToRadioGroup(radioPreference: RadioButtonPreference) {
radioGroups.add(radioPreference)
override fun addToRadioGroup(radioButton: GroupableRadioButton) {
radioGroups.add(radioButton)
}
fun onClickListener(listener: (() -> Unit)) {
@ -97,7 +100,7 @@ open class RadioButtonPreference @JvmOverloads constructor(
toggleRadioGroups()
}
private fun updateRadioValue(isChecked: Boolean) {
override fun updateRadioValue(isChecked: Boolean) {
persistBoolean(isChecked)
radioButton?.isChecked = isChecked
context.settings().preferences.edit().putBoolean(key, isChecked)
@ -113,7 +116,7 @@ open class RadioButtonPreference @JvmOverloads constructor(
private fun toggleRadioGroups() {
if (radioButton?.isChecked == true) {
radioGroups.forEach { it.updateRadioValue(false) }
radioGroups.uncheckAll()
}
}

View File

@ -21,6 +21,7 @@ import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.trackingprotection.TrackingProtectionMode
import org.mozilla.fenix.utils.view.addToRadioGroup
/**
* Displays the toggle for tracking protection, options for tracking protection policy and a button
@ -46,7 +47,7 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
val radioStrict = bindTrackingProtectionRadio(TrackingProtectionMode.STRICT)
val radioStandard = bindTrackingProtectionRadio(TrackingProtectionMode.STANDARD)
val radioCustom = bindCustom()
setupRadioGroups(radioStrict, radioStandard, radioCustom)
addToRadioGroup(radioStrict, radioStandard, radioCustom)
updateCustomOptionsVisibility()
}
@ -208,21 +209,6 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
}
}
private fun setupRadioGroups(
radioStrict: RadioButtonInfoPreference,
radioStandard: RadioButtonInfoPreference,
radioCustom: RadioButtonInfoPreference
) {
radioStandard.addToRadioGroup(radioStrict)
radioStrict.addToRadioGroup(radioStandard)
radioStandard.addToRadioGroup(radioCustom)
radioCustom.addToRadioGroup(radioStandard)
radioStrict.addToRadioGroup(radioCustom)
radioCustom.addToRadioGroup(radioStrict)
}
private fun updateCustomOptionsVisibility() {
val isCustomSelected = requireContext().settings().useCustomTrackingProtection
customCookies.isVisible = isCustomSelected

View File

@ -15,6 +15,7 @@ import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.settings.RadioButtonPreference
import org.mozilla.fenix.settings.SharedPreferenceUpdater
import org.mozilla.fenix.settings.requirePreference
import org.mozilla.fenix.utils.view.addToRadioGroup
class SavedLoginsSettingFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@ -26,7 +27,7 @@ class SavedLoginsSettingFragment : PreferenceFragmentCompat() {
showToolbar(getString(R.string.preferences_passwords_save_logins))
val save = bindSave()
val neverSave = bindNeverSave()
setupRadioGroups(save, neverSave)
addToRadioGroup(save, neverSave)
}
private fun bindSave(): RadioButtonPreference {
@ -66,12 +67,4 @@ class SavedLoginsSettingFragment : PreferenceFragmentCompat() {
}
return preferenceNeverSave
}
private fun setupRadioGroups(
radioNeverSave: RadioButtonPreference,
radioSave: RadioButtonPreference
) {
radioNeverSave.addToRadioGroup(radioSave)
radioSave.addToRadioGroup(radioNeverSave)
}
}

View File

@ -0,0 +1,28 @@
/* 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.utils.view
interface GroupableRadioButton {
fun updateRadioValue(isChecked: Boolean)
fun addToRadioGroup(radioButton: GroupableRadioButton)
}
/**
* Connect all the given radio buttons into a group,
* so that when one radio is checked the others are unchecked.
*/
fun addToRadioGroup(vararg radios: GroupableRadioButton) {
for (i in 0..radios.lastIndex) {
for (j in (i + 1)..radios.lastIndex) {
radios[i].addToRadioGroup(radios[j])
radios[j].addToRadioGroup(radios[i])
}
}
}
fun Iterable<GroupableRadioButton>.uncheckAll() {
forEach { it.updateRadioValue(isChecked = false) }
}

View File

@ -1,4 +1,4 @@
package org.mozilla.fenix.ext
package org.mozilla.fenix.utils.view
import android.view.View
import androidx.recyclerview.widget.RecyclerView

View File

@ -0,0 +1,66 @@
/* 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.utils.view
import io.mockk.Called
import io.mockk.mockk
import io.mockk.verify
import io.mockk.verifySequence
import org.junit.Test
class GroupableRadioButtonTest {
@Test
fun `test add 1 radio to group`() {
val radio = mockk<GroupableRadioButton>(relaxed = true)
addToRadioGroup(radio)
verify { radio wasNot Called }
}
@Test
fun `test add 2 radios to group`() {
val radio1 = mockk<GroupableRadioButton>(relaxed = true)
val radio2 = mockk<GroupableRadioButton>(relaxed = true)
addToRadioGroup(radio1, radio2)
verifySequence {
radio1.addToRadioGroup(radio2)
radio2.addToRadioGroup(radio1)
}
}
@Test
fun `test add 3 radios to group`() {
val radio1 = mockk<GroupableRadioButton>(relaxed = true)
val radio2 = mockk<GroupableRadioButton>(relaxed = true)
val radio3 = mockk<GroupableRadioButton>(relaxed = true)
addToRadioGroup(radio1, radio2, radio3)
verifySequence {
radio1.addToRadioGroup(radio2)
radio2.addToRadioGroup(radio1)
radio1.addToRadioGroup(radio3)
radio3.addToRadioGroup(radio1)
radio2.addToRadioGroup(radio3)
radio3.addToRadioGroup(radio2)
}
}
@Test
fun `test uncheck all`() {
val radio1 = mockk<GroupableRadioButton>(relaxed = true)
val radio2 = mockk<GroupableRadioButton>(relaxed = true)
val radio3 = mockk<GroupableRadioButton>(relaxed = true)
listOf(radio1, radio2, radio3).uncheckAll()
verifySequence {
radio1.updateRadioValue(false)
radio2.updateRadioValue(false)
radio3.updateRadioValue(false)
}
}
}