Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt +36 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel import android.content.Context import android.platform.test.annotations.EnableFlags import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -28,6 +29,7 @@ import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.lifecycle.activateIn import com.android.systemui.statusbar.connectivity.ui.mobileContextProvider import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel Loading @@ -38,6 +40,10 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -53,6 +59,7 @@ class StackedMobileIconViewModelTest : SysuiTestCase() { kosmos.run { // Set prerequisites for the stacked icon fakeMobileIconsInteractor.isStackable.value = true whenever(mobileContextProvider.getMobileContextForSub(any(), any())).thenReturn(context) underTest.activateIn(testScope) } Loading Loading @@ -177,6 +184,35 @@ class StackedMobileIconViewModelTest : SysuiTestCase() { assertThat(underTest.contentDescription).isNull() } @Test @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun mobileContext_tracksConnections() = kosmos.runTest { fakeMobileIconsInteractor.filteredSubscriptions.value = listOf() assertThat(underTest.mobileContext).isNull() fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) assertThat(underTest.mobileContext).isNotNull() } @Test @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun mobileContext_tracksPrimaryConnection() = kosmos.runTest { val contextSub1 = mock(Context::class.java) val contextSub2 = mock(Context::class.java) whenever(mobileContextProvider.getMobileContextForSub(eq(SUB_1.subscriptionId), any())) .thenReturn(contextSub1) whenever(mobileContextProvider.getMobileContextForSub(eq(SUB_2.subscriptionId), any())) .thenReturn(contextSub2) fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) assertThat(underTest.mobileContext).isEqualTo(contextSub1) fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_2, SUB_1) assertThat(underTest.mobileContext).isEqualTo(contextSub2) } private fun setIconLevel(subId: Int, level: Int) { with(kosmos.fakeMobileIconsInteractor.getInteractorForSubId(subId)!!) { signalLevelIcon.value = Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt +19 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import com.android.systemui.lifecycle.Hydrator import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider import com.android.systemui.statusbar.pipeline.dagger.StackedMobileIconTableLog import com.android.systemui.statusbar.pipeline.mobile.ui.model.DualSim import com.android.systemui.statusbar.pipeline.mobile.ui.model.logDualSimDiff Loading @@ -42,6 +43,8 @@ interface StackedMobileIconViewModel { val dualSim: DualSim? val contentDescription: String? val networkTypeIcon: Icon.Resource? /** [Context] to use when loading the [networkTypeIcon] */ val mobileContext: Context? val isIconVisible: Boolean } Loading @@ -52,6 +55,7 @@ constructor( mobileIconsViewModel: MobileIconsViewModel, @StackedMobileIconTableLog private val tableLogger: TableLogBuffer, @ShadeDisplayAware private val context: Context, private val mobileContextProvider: MobileContextProvider, ) : ExclusiveActivatable(), StackedMobileIconViewModel { private val hydrator = Hydrator("StackedMobileIconViewModel") Loading Loading @@ -128,6 +132,21 @@ constructor( initialValue = null, ) override val mobileContext: Context? by hydrator.hydratedStateOf( traceName = "mobileContext", source = flowIfIconIsVisible( iconViewModelFlow.map { viewModels -> // Get mobile context of primary connection viewModels.firstOrNull()?.let { mobileContextProvider.getMobileContextForSub(it.subscriptionId, context) } } ), initialValue = null, ) override val isIconVisible: Boolean by hydrator.hydratedStateOf( traceName = "isIconVisible", Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt +13 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.kairos.map import com.android.systemui.kairos.stateOf import com.android.systemui.kairosBuilder import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider import com.android.systemui.statusbar.pipeline.mobile.ui.model.DualSim import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription import com.android.systemui.statusbar.pipeline.mobile.ui.model.tryParseDualSim Loading @@ -41,6 +42,7 @@ class StackedMobileIconViewModelKairos constructor( mobileIcons: MobileIconsViewModelKairos, @ShadeDisplayAware private val context: Context, private val mobileContextProvider: MobileContextProvider, ) : KairosBuilder by kairosBuilder(), StackedMobileIconViewModel { private val isStackable: Boolean by Loading Loading @@ -89,6 +91,17 @@ constructor( initialValue = null, ) override val mobileContext: Context? by hydratedComposeStateOf( "StackedMobileIconViewModelKairos.mobileContext", iconList.map { icons -> icons.firstOrNull()?.let { mobileContextProvider.getMobileContextForSub(it.subscriptionId, context) } }, initialValue = null, ) override val isIconVisible: Boolean get() = isStackable && dualSim != null Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt +8 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.CornerRadius Loading @@ -33,6 +34,7 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics Loading Loading @@ -78,9 +80,13 @@ fun StackedMobileIcon(viewModel: StackedMobileIconViewModel, modifier: Modifier modifier = modifier.padding(horizontal = padding), ) { viewModel.networkTypeIcon?.let { // Provide the RAT context needed for the resource overlays val ratContext = viewModel.mobileContext ?: LocalContext.current CompositionLocalProvider(LocalContext provides ratContext) { val height = with(LocalDensity.current) { IconHeightSp.toDp() } Icon(it, tint = contentColor, modifier = Modifier.height(height).wrapContentWidth()) } } StackedMobileIcon( viewModel = dualSim, Loading packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt +6 −1 Original line number Diff line number Diff line Loading @@ -20,8 +20,13 @@ import android.content.testableContext import com.android.systemui.kairos.ActivatedKairosFixture import com.android.systemui.kairos.ExperimentalKairosApi import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.connectivity.ui.mobileContextProvider @ExperimentalKairosApi val Kosmos.stackedMobileIconViewModelKairos by ActivatedKairosFixture { StackedMobileIconViewModelKairos(mobileIconsViewModelKairos, testableContext) StackedMobileIconViewModelKairos( mobileIconsViewModelKairos, testableContext, mobileContextProvider, ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt +36 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel import android.content.Context import android.platform.test.annotations.EnableFlags import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -28,6 +29,7 @@ import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.lifecycle.activateIn import com.android.systemui.statusbar.connectivity.ui.mobileContextProvider import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel Loading @@ -38,6 +40,10 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -53,6 +59,7 @@ class StackedMobileIconViewModelTest : SysuiTestCase() { kosmos.run { // Set prerequisites for the stacked icon fakeMobileIconsInteractor.isStackable.value = true whenever(mobileContextProvider.getMobileContextForSub(any(), any())).thenReturn(context) underTest.activateIn(testScope) } Loading Loading @@ -177,6 +184,35 @@ class StackedMobileIconViewModelTest : SysuiTestCase() { assertThat(underTest.contentDescription).isNull() } @Test @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun mobileContext_tracksConnections() = kosmos.runTest { fakeMobileIconsInteractor.filteredSubscriptions.value = listOf() assertThat(underTest.mobileContext).isNull() fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) assertThat(underTest.mobileContext).isNotNull() } @Test @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun mobileContext_tracksPrimaryConnection() = kosmos.runTest { val contextSub1 = mock(Context::class.java) val contextSub2 = mock(Context::class.java) whenever(mobileContextProvider.getMobileContextForSub(eq(SUB_1.subscriptionId), any())) .thenReturn(contextSub1) whenever(mobileContextProvider.getMobileContextForSub(eq(SUB_2.subscriptionId), any())) .thenReturn(contextSub2) fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) assertThat(underTest.mobileContext).isEqualTo(contextSub1) fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_2, SUB_1) assertThat(underTest.mobileContext).isEqualTo(contextSub2) } private fun setIconLevel(subId: Int, level: Int) { with(kosmos.fakeMobileIconsInteractor.getInteractorForSubId(subId)!!) { signalLevelIcon.value = Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt +19 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import com.android.systemui.lifecycle.Hydrator import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider import com.android.systemui.statusbar.pipeline.dagger.StackedMobileIconTableLog import com.android.systemui.statusbar.pipeline.mobile.ui.model.DualSim import com.android.systemui.statusbar.pipeline.mobile.ui.model.logDualSimDiff Loading @@ -42,6 +43,8 @@ interface StackedMobileIconViewModel { val dualSim: DualSim? val contentDescription: String? val networkTypeIcon: Icon.Resource? /** [Context] to use when loading the [networkTypeIcon] */ val mobileContext: Context? val isIconVisible: Boolean } Loading @@ -52,6 +55,7 @@ constructor( mobileIconsViewModel: MobileIconsViewModel, @StackedMobileIconTableLog private val tableLogger: TableLogBuffer, @ShadeDisplayAware private val context: Context, private val mobileContextProvider: MobileContextProvider, ) : ExclusiveActivatable(), StackedMobileIconViewModel { private val hydrator = Hydrator("StackedMobileIconViewModel") Loading Loading @@ -128,6 +132,21 @@ constructor( initialValue = null, ) override val mobileContext: Context? by hydrator.hydratedStateOf( traceName = "mobileContext", source = flowIfIconIsVisible( iconViewModelFlow.map { viewModels -> // Get mobile context of primary connection viewModels.firstOrNull()?.let { mobileContextProvider.getMobileContextForSub(it.subscriptionId, context) } } ), initialValue = null, ) override val isIconVisible: Boolean by hydrator.hydratedStateOf( traceName = "isIconVisible", Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt +13 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.kairos.map import com.android.systemui.kairos.stateOf import com.android.systemui.kairosBuilder import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider import com.android.systemui.statusbar.pipeline.mobile.ui.model.DualSim import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription import com.android.systemui.statusbar.pipeline.mobile.ui.model.tryParseDualSim Loading @@ -41,6 +42,7 @@ class StackedMobileIconViewModelKairos constructor( mobileIcons: MobileIconsViewModelKairos, @ShadeDisplayAware private val context: Context, private val mobileContextProvider: MobileContextProvider, ) : KairosBuilder by kairosBuilder(), StackedMobileIconViewModel { private val isStackable: Boolean by Loading Loading @@ -89,6 +91,17 @@ constructor( initialValue = null, ) override val mobileContext: Context? by hydratedComposeStateOf( "StackedMobileIconViewModelKairos.mobileContext", iconList.map { icons -> icons.firstOrNull()?.let { mobileContextProvider.getMobileContextForSub(it.subscriptionId, context) } }, initialValue = null, ) override val isIconVisible: Boolean get() = isStackable && dualSim != null Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt +8 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.CornerRadius Loading @@ -33,6 +34,7 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics Loading Loading @@ -78,9 +80,13 @@ fun StackedMobileIcon(viewModel: StackedMobileIconViewModel, modifier: Modifier modifier = modifier.padding(horizontal = padding), ) { viewModel.networkTypeIcon?.let { // Provide the RAT context needed for the resource overlays val ratContext = viewModel.mobileContext ?: LocalContext.current CompositionLocalProvider(LocalContext provides ratContext) { val height = with(LocalDensity.current) { IconHeightSp.toDp() } Icon(it, tint = contentColor, modifier = Modifier.height(height).wrapContentWidth()) } } StackedMobileIcon( viewModel = dualSim, Loading
packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt +6 −1 Original line number Diff line number Diff line Loading @@ -20,8 +20,13 @@ import android.content.testableContext import com.android.systemui.kairos.ActivatedKairosFixture import com.android.systemui.kairos.ExperimentalKairosApi import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.connectivity.ui.mobileContextProvider @ExperimentalKairosApi val Kosmos.stackedMobileIconViewModelKairos by ActivatedKairosFixture { StackedMobileIconViewModelKairos(mobileIconsViewModelKairos, testableContext) StackedMobileIconViewModelKairos( mobileIconsViewModelKairos, testableContext, mobileContextProvider, ) }