[Sb refactor] Proper support for carrierNetworkChangeMode

Testing via demo mode exposed that `carrierNetworkChangeMode` was not
suppored in the SignalIconModel, only in calculating `roaming`. This
change adds support to:

1. Draw the "marching dots" UI properly when in carrier network change
   mode
2. Ignore any network data type icon when the mode is active

Test: SignalIconModelParameterizedTest
Test: MobileIconViewModelTest
Bug: 284967021
Change-Id: Ieb740ceb0bfc1a8dd6c853d048a15c48775085c7
This commit is contained in:
Evan Laird
2023-05-17 14:06:21 -04:00
parent 0af35e71de
commit 975b9dbfb9
7 changed files with 209 additions and 19 deletions

View File

@@ -110,6 +110,9 @@ interface MobileIconInteractor {
/** See [MobileIconsInteractor.isForceHidden]. */
val isForceHidden: Flow<Boolean>
/** True when in carrier network change mode */
val carrierNetworkChangeActive: StateFlow<Boolean>
}
/** Interactor for a single mobile connection. This connection _should_ have one subscription ID */
@@ -135,6 +138,9 @@ class MobileIconInteractorImpl(
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
override val carrierNetworkChangeActive: StateFlow<Boolean> =
connectionRepository.carrierNetworkChangeActive
// True if there exists _any_ icon override for this carrierId. Note that overrides can include
// any or none of the icon groups defined in MobileMappings, so we still need to check on a
// per-network-type basis whether or not the given icon group is overridden

View File

@@ -109,12 +109,7 @@ object MobileIconBinder {
viewModel.subscriptionId,
icon,
)
mobileDrawable.level =
SignalDrawable.getState(
icon.level,
icon.numberOfLevels,
icon.showExclamationMark,
)
mobileDrawable.level = icon.toSignalDrawableState()
}
}

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.model
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
@@ -24,6 +25,7 @@ data class SignalIconModel(
val level: Int,
val numberOfLevels: Int,
val showExclamationMark: Boolean,
val carrierNetworkChange: Boolean,
) : Diffable<SignalIconModel> {
// TODO(b/267767715): Can we implement [logDiffs] and [logFull] generically for data classes?
override fun logDiffs(prevVal: SignalIconModel, row: TableRowLogger) {
@@ -36,17 +38,30 @@ data class SignalIconModel(
if (prevVal.showExclamationMark != showExclamationMark) {
row.logChange(COL_SHOW_EXCLAMATION, showExclamationMark)
}
if (prevVal.carrierNetworkChange != carrierNetworkChange) {
row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChange)
}
}
override fun logFull(row: TableRowLogger) {
row.logChange(COL_LEVEL, level)
row.logChange(COL_NUM_LEVELS, numberOfLevels)
row.logChange(COL_SHOW_EXCLAMATION, showExclamationMark)
row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChange)
}
/** Convert this model to an [Int] consumable by [SignalDrawable]. */
fun toSignalDrawableState(): Int =
if (carrierNetworkChange) {
SignalDrawable.getCarrierChangeState(numberOfLevels)
} else {
SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
}
companion object {
private const val COL_LEVEL = "level"
private const val COL_NUM_LEVELS = "numLevels"
private const val COL_SHOW_EXCLAMATION = "showExclamation"
private const val COL_CARRIER_NETWORK_CHANGE = "carrierNetworkChange"
}
}

View File

@@ -122,13 +122,20 @@ constructor(
level = shownLevel.value,
numberOfLevels = iconInteractor.numberOfLevels.value,
showExclamationMark = showExclamationMark.value,
carrierNetworkChange = iconInteractor.carrierNetworkChangeActive.value,
)
combine(
shownLevel,
iconInteractor.numberOfLevels,
showExclamationMark,
) { shownLevel, numberOfLevels, showExclamationMark ->
SignalIconModel(shownLevel, numberOfLevels, showExclamationMark)
iconInteractor.carrierNetworkChangeActive,
) { shownLevel, numberOfLevels, showExclamationMark, carrierNetworkChange ->
SignalIconModel(
shownLevel,
numberOfLevels,
showExclamationMark,
carrierNetworkChange,
)
}
.distinctUntilChanged()
.logDiffsForTable(
@@ -152,8 +159,10 @@ constructor(
iconInteractor.isDataEnabled,
iconInteractor.alwaysShowDataRatIcon,
iconInteractor.mobileIsDefault,
) { dataConnected, dataEnabled, alwaysShow, mobileIsDefault ->
alwaysShow || (dataEnabled && dataConnected && mobileIsDefault)
iconInteractor.carrierNetworkChangeActive,
) { dataConnected, dataEnabled, alwaysShow, mobileIsDefault, carrierNetworkChange ->
alwaysShow ||
(!carrierNetworkChange && (dataEnabled && dataConnected && mobileIsDefault))
}
.distinctUntilChanged()
.logDiffsForTable(

View File

@@ -40,6 +40,8 @@ class FakeMobileIconInteractor(
)
)
override val carrierNetworkChangeActive = MutableStateFlow(false)
override val mobileIsDefault = MutableStateFlow(true)
override val networkTypeIconGroup =

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.pipeline.mobile.ui.model
import androidx.test.filters.SmallTest
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters
@SmallTest
@RunWith(Parameterized::class)
internal class SignalIconModelParameterizedTest(private val testCase: TestCase) : SysuiTestCase() {
@Test
fun drawableFromModel_level0_numLevels4_noExclamation_notCarrierNetworkChange() {
val model =
SignalIconModel(
level = 0,
numberOfLevels = 4,
showExclamationMark = false,
carrierNetworkChange = false
)
val expected =
SignalDrawable.getState(/* level = */ 0, /* numLevels = */ 4, /* cutOut = */ false)
assertThat(model.toSignalDrawableState()).isEqualTo(expected)
}
@Test
fun runTest() {
val model = testCase.toSignalIconModel()
assertThat(model.toSignalDrawableState()).isEqualTo(testCase.expected)
}
internal data class TestCase(
val level: Int,
val numberOfLevels: Int,
val showExclamation: Boolean,
val carrierNetworkChange: Boolean,
val expected: Int,
) {
fun toSignalIconModel() =
SignalIconModel(
level = level,
numberOfLevels = numberOfLevels,
showExclamationMark = showExclamation,
carrierNetworkChange = carrierNetworkChange,
)
override fun toString(): String =
"INPUT(level=$level," +
"numberOfLevels=$numberOfLevels," +
"showExclamation=$showExclamation," +
"carrierNetworkChange=$carrierNetworkChange)"
}
companion object {
@Parameters(name = "{0}") @JvmStatic fun data() = testData()
private fun testData(): Collection<TestCase> =
listOf(
TestCase(
level = 0,
numberOfLevels = 4,
showExclamation = false,
carrierNetworkChange = false,
expected = SignalDrawable.getState(0, 4, false)
),
TestCase(
level = 0,
numberOfLevels = 4,
showExclamation = false,
carrierNetworkChange = true,
expected = SignalDrawable.getCarrierChangeState(4)
),
TestCase(
level = 2,
numberOfLevels = 5,
showExclamation = false,
carrierNetworkChange = false,
expected = SignalDrawable.getState(2, 5, false)
),
TestCase(
level = 2,
numberOfLevels = 5,
showExclamation = true,
carrierNetworkChange = false,
expected = SignalDrawable.getState(2, 5, true)
),
TestCase(
level = 2,
numberOfLevels = 5,
showExclamation = true,
carrierNetworkChange = true,
expected = SignalDrawable.getCarrierChangeState(5)
),
)
}
}

View File

@@ -272,6 +272,27 @@ class MobileIconViewModelTest : SysuiTestCase() {
job.cancel()
}
@Test
fun icon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() =
testScope.runTest {
var latest: SignalIconModel? = null
val job = underTest.icon.onEach { latest = it }.launchIn(this)
interactor.carrierNetworkChangeActive.value = true
interactor.level.value = 1
assertThat(latest!!.level).isEqualTo(1)
assertThat(latest!!.carrierNetworkChange).isTrue()
// SignalIconModel respects the current level
interactor.level.value = 2
assertThat(latest!!.level).isEqualTo(2)
assertThat(latest!!.carrierNetworkChange).isTrue()
job.cancel()
}
@Test
fun contentDescription_notInService_usesNoPhone() =
testScope.runTest {
@@ -337,6 +358,20 @@ class MobileIconViewModelTest : SysuiTestCase() {
job.cancel()
}
@Test
fun networkType_null_whenCarrierNetworkChangeActive() =
testScope.runTest {
interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.carrierNetworkChangeActive.value = true
interactor.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
assertThat(latest).isNull()
job.cancel()
}
@Test
fun networkTypeIcon_notNull_whenEnabled() =
testScope.runTest {
@@ -617,13 +652,14 @@ class MobileIconViewModelTest : SysuiTestCase() {
}
private fun createAndSetViewModel() {
underTest = MobileIconViewModel(
SUB_1_ID,
interactor,
airplaneModeInteractor,
constants,
testScope.backgroundScope,
)
underTest =
MobileIconViewModel(
SUB_1_ID,
interactor,
airplaneModeInteractor,
constants,
testScope.backgroundScope,
)
}
companion object {
@@ -632,10 +668,20 @@ class MobileIconViewModelTest : SysuiTestCase() {
/** Convenience constructor for these tests */
fun defaultSignal(level: Int = 1): SignalIconModel {
return SignalIconModel(level, NUM_LEVELS, showExclamationMark = false)
return SignalIconModel(
level,
NUM_LEVELS,
showExclamationMark = false,
carrierNetworkChange = false,
)
}
fun emptySignal(): SignalIconModel =
SignalIconModel(level = 0, numberOfLevels = NUM_LEVELS, showExclamationMark = true)
SignalIconModel(
level = 0,
numberOfLevels = NUM_LEVELS,
showExclamationMark = true,
carrierNetworkChange = false,
)
}
}