Use MobileDataEnabledFlow in BillingCyclePreference

To easily collect the mobile data enabled setting changes.

Bug: 308903704
Test: manual - on Mobile Settings
Test: unit test
Change-Id: Ic449ce14fad38513b1e13facc6a192d30318c7b0
This commit is contained in:
Chaohui Wang
2023-11-07 00:15:13 +08:00
parent c292adaf6b
commit 0739b5edd1
6 changed files with 81 additions and 49 deletions

View File

@@ -16,15 +16,21 @@ package com.android.settings.datausage
import android.app.settings.SettingsEnums import android.app.settings.SettingsEnums
import android.content.Context import android.content.Context
import android.content.Intent
import android.net.NetworkTemplate import android.net.NetworkTemplate
import android.os.Bundle import android.os.Bundle
import android.util.AttributeSet import android.util.AttributeSet
import androidx.preference.Preference import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R import com.android.settings.R
import com.android.settings.core.SubSettingLauncher import com.android.settings.core.SubSettingLauncher
import com.android.settings.datausage.lib.BillingCycleRepository import com.android.settings.datausage.lib.BillingCycleRepository
import com.android.settings.network.MobileDataEnabledListener import com.android.settings.network.mobileDataEnabledFlow
import com.android.settings.spa.preference.ComposePreference
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import kotlinx.coroutines.flow.map
/** /**
* Preference which displays billing cycle of subscription * Preference which displays billing cycle of subscription
@@ -36,45 +42,31 @@ class BillingCyclePreference @JvmOverloads constructor(
context: Context, context: Context,
attrs: AttributeSet?, attrs: AttributeSet?,
private val repository: BillingCycleRepository = BillingCycleRepository(context), private val repository: BillingCycleRepository = BillingCycleRepository(context),
) : Preference(context, attrs), TemplatePreference { ) : ComposePreference(context, attrs), TemplatePreference {
private lateinit var template: NetworkTemplate
private var subId = 0
private val listener = MobileDataEnabledListener(context) {
updateEnabled()
}
override fun setTemplate(template: NetworkTemplate, subId: Int) { override fun setTemplate(template: NetworkTemplate, subId: Int) {
this.template = template setContent {
this.subId = subId val isModifiable by remember {
summary = null context.mobileDataEnabledFlow(subId).map { repository.isModifiable(subId) }
updateEnabled() }.collectAsStateWithLifecycle(initialValue = false)
intent = intent
Preference(object : PreferenceModel {
override val title = stringResource(R.string.billing_cycle)
override val enabled = { isModifiable }
override val onClick = { launchBillingCycleSettings(template) }
})
}
} }
override fun onAttached() { private fun launchBillingCycleSettings(template: NetworkTemplate) {
super.onAttached()
listener.start(subId)
}
override fun onDetached() {
listener.stop()
super.onDetached()
}
private fun updateEnabled() {
isEnabled = repository.isModifiable(subId)
}
override fun getIntent(): Intent {
val args = Bundle().apply { val args = Bundle().apply {
putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, template) putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, template)
} }
return SubSettingLauncher(context).apply { SubSettingLauncher(context).apply {
setDestination(BillingCycleSettings::class.java.name) setDestination(BillingCycleSettings::class.java.name)
setArguments(args) setArguments(args)
setTitleRes(R.string.billing_cycle) setTitleRes(R.string.billing_cycle)
setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN) setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN)
}.toIntent() }.launch()
} }
} }

View File

@@ -18,6 +18,7 @@ package com.android.settings.spa.preference
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.ViewCompositionStrategy
@@ -26,13 +27,23 @@ import androidx.preference.PreferenceViewHolder
import com.android.settings.R import com.android.settings.R
import com.android.settingslib.spa.framework.theme.SettingsTheme import com.android.settingslib.spa.framework.theme.SettingsTheme
class ComposePreference @JvmOverloads constructor( open class ComposePreference @JvmOverloads constructor(
context: Context, context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = 0, defStyleAttr: Int = 0,
defStyleRes: Int = 0, defStyleRes: Int = 0,
) : Preference(context, attrs, defStyleAttr, defStyleRes) { ) : Preference(context, attrs, defStyleAttr, defStyleRes) {
var content: @Composable () -> Unit = {} private var content: @Composable () -> Unit = {}
fun setContent(content: @Composable () -> Unit) {
this.content = content
}
@VisibleForTesting
@Composable
fun Content() {
content()
}
init { init {
layoutResource = R.layout.preference_compose layoutResource = R.layout.preference_compose

View File

@@ -29,7 +29,7 @@ abstract class ComposePreferenceController(context: Context, preferenceKey: Stri
override fun displayPreference(screen: PreferenceScreen) { override fun displayPreference(screen: PreferenceScreen) {
super.displayPreference(screen) super.displayPreference(screen)
preference = screen.findPreference(preferenceKey)!! preference = screen.findPreference(preferenceKey)!!
preference.content = { Content() } preference.setContent { Content() }
} }
@Composable @Composable

View File

@@ -18,40 +18,69 @@ package com.android.settings.datausage
import android.content.Context import android.content.Context
import android.net.NetworkTemplate import android.net.NetworkTemplate
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.datausage.lib.BillingCycleRepository import com.android.settings.datausage.lib.BillingCycleRepository
import com.google.common.truth.Truth.assertThat import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class BillingCyclePreferenceTest { class BillingCyclePreferenceTest {
@get:Rule
val composeTestRule = createComposeRule()
private val mockBillingCycleRepository = mock<BillingCycleRepository> { private val mockBillingCycleRepository = mock<BillingCycleRepository>()
on { isModifiable(SUB_ID) } doReturn false
}
private val context: Context = ApplicationProvider.getApplicationContext() private val context: Context = ApplicationProvider.getApplicationContext()
private val preference = BillingCyclePreference(context, null, mockBillingCycleRepository) private val preference = BillingCyclePreference(context, null, mockBillingCycleRepository)
@Test @Test
fun isEnabled_initialState() { fun setTemplate_titleDisplayed() {
val enabled = preference.isEnabled setTemplate()
assertThat(enabled).isTrue() composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle))
.assertIsDisplayed()
} }
@Test @Test
fun isEnabled_afterSetTemplate_updated() { fun setTemplate_modifiable_enabled() {
mockBillingCycleRepository.stub {
on { isModifiable(SUB_ID) } doReturn true
}
setTemplate()
composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle)).assertIsEnabled()
}
@Test
fun setTemplate_notModifiable_notEnabled() {
mockBillingCycleRepository.stub {
on { isModifiable(SUB_ID) } doReturn false
}
setTemplate()
composeTestRule.onNodeWithText(context.getString(R.string.billing_cycle))
.assertIsNotEnabled()
}
private fun setTemplate() {
preference.setTemplate(mock<NetworkTemplate>(), SUB_ID) preference.setTemplate(mock<NetworkTemplate>(), SUB_ID)
composeTestRule.setContent {
val enabled = preference.isEnabled preference.Content()
}
assertThat(enabled).isFalse()
} }
private companion object { private companion object {

View File

@@ -61,7 +61,7 @@ class ComposePreferenceControllerTest {
controller.displayPreference(preferenceScreen) controller.displayPreference(preferenceScreen)
composeTestRule.setContent { composeTestRule.setContent {
preference.content() preference.Content()
} }
composeTestRule.onNodeWithText(TEXT).assertIsDisplayed() composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
} }

View File

@@ -43,7 +43,7 @@ class ComposePreferenceTest {
@Test @Test
fun onBindViewHolder() { fun onBindViewHolder() {
preference.content = { preference.setContent {
Text(TEXT) Text(TEXT)
} }