diff options
| author | 2025-03-21 10:36:02 -0700 | |
|---|---|---|
| committer | 2025-03-21 10:36:02 -0700 | |
| commit | 5af6802fdbbccdd034820e2d36be79a2dafaac75 (patch) | |
| tree | b0db26ad16811434e93c243dab863a50d765a488 | |
| parent | 15c634697e28a88c9c3af308f625a6275d6efce7 (diff) | |
| parent | 1129650ee9cac7ecfe4e70638d7d3b184b46ecf6 (diff) | |
Merge changes I6e5eaa39,I6df2e609 into main
* changes:
Collect the icon tint from the StateFlow
Only show the stacked mobile icon when all individual mobile icons are visible
7 files changed, 75 insertions, 24 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt index e72d0c27e632..8aff622ee772 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt @@ -97,7 +97,7 @@ class MobileIconInteractorKairosAdapterTest : MobileIconInteractorTestBase() { } .asIncremental() .applyLatestSpecForKey(), - isStackable = interactor.isStackable.toState(), + isStackable = interactor.isStackable.toState(false), activeDataConnectionHasDataEnabled = interactor.activeDataConnectionHasDataEnabled.toState(), activeDataIconInteractor = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt index 3d37914b1a7d..7dbcb270190c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt @@ -60,7 +60,6 @@ class MobileIconsViewModelTest : SysuiTestCase() { private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock()) private lateinit var airplaneModeInteractor: AirplaneModeInteractor - @Mock private lateinit var constants: ConnectivityConstants @Mock private lateinit var logger: MobileViewLogger @Mock private lateinit var verboseLogger: VerboseMobileViewLogger @@ -84,7 +83,10 @@ class MobileIconsViewModelTest : SysuiTestCase() { verboseLogger, interactor, airplaneModeInteractor, - constants, + object : ConnectivityConstants { + override val hasDataCapabilities = true + override val shouldShowActivityConfig = false + }, testScope.backgroundScope, ) @@ -349,7 +351,42 @@ class MobileIconsViewModelTest : SysuiTestCase() { // WHEN sub2 becomes last and sub2 has a network type icon interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) - // THEN the flow updates + assertThat(latest).isTrue() + job.cancel() + } + + @Test + fun isStackable_apmEnabled_false() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isStackable.onEach { latest = it }.launchIn(this) + + // Set the interactor to true to test APM + interactor.isStackable.value = true + + // Enable APM + airplaneModeInteractor.setIsAirplaneMode(true) + + interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) + + assertThat(latest).isFalse() + job.cancel() + } + + @Test + fun isStackable_apmDisabled_true() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isStackable.onEach { latest = it }.launchIn(this) + + // Set the interactor to true to test APM + interactor.isStackable.value = true + + // Disable APM + airplaneModeInteractor.setIsAirplaneMode(false) + + interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) + assertThat(latest).isTrue() job.cancel() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt index 10821dffd394..1f4ccd59b063 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt @@ -84,7 +84,7 @@ interface MobileIconsInteractor { val icons: StateFlow<List<MobileIconInteractor>> /** Whether the mobile icons can be stacked vertically. */ - val isStackable: StateFlow<Boolean> + val isStackable: Flow<Boolean> /** * Observable for the subscriptionId of the current mobile data connection. Null if we don't @@ -309,21 +309,20 @@ constructor( override val isStackable = if (NewStatusBarIcons.isEnabled && StatusBarRootModernization.isEnabled) { - icons.flatMapLatest { icons -> - combine(icons.map { it.signalLevelIcon }) { signalLevelIcons -> - // These are only stackable if: - // - They are cellular - // - There's exactly two - // - They have the same number of levels - signalLevelIcons.filterIsInstance<SignalIconModel.Cellular>().let { - it.size == 2 && it[0].numberOfLevels == it[1].numberOfLevels - } + icons.flatMapLatest { icons -> + combine(icons.map { it.signalLevelIcon }) { signalLevelIcons -> + // These are only stackable if: + // - They are cellular + // - There's exactly two + // - They have the same number of levels + signalLevelIcons.filterIsInstance<SignalIconModel.Cellular>().let { + it.size == 2 && it[0].numberOfLevels == it[1].numberOfLevels } } - } else { - flowOf(false) } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + } else { + flowOf(false) + } /** * Copied from the old pipeline. We maintain a 2s period of time where we will keep the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt index 54cd8e3c46e4..72ff3b67c317 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt @@ -18,9 +18,11 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.binder import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.Flags import com.android.systemui.kairos.ExperimentalKairosApi @@ -48,7 +50,7 @@ object StackedMobileIconBinder { return SingleBindableStatusBarComposeIconView.withDefaultBinding( view = view, shouldBeVisible = { mobileIconsViewModel.isStackable.value }, - ) { _, tint -> + ) { _, tintFlow -> view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { view.composeView.apply { @@ -66,8 +68,9 @@ object StackedMobileIconBinder { viewModelFactory.create() } } + val tint by tintFlow.collectAsStateWithLifecycle() if (viewModel.isIconVisible) { - CompositionLocalProvider(LocalContentColor provides Color(tint())) { + CompositionLocalProvider(LocalContentColor provides Color(tint)) { StackedMobileIcon(viewModel) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt index 494d95e7f177..997b185fdee5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt @@ -36,6 +36,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -99,7 +101,17 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), false) - val isStackable: StateFlow<Boolean> = interactor.isStackable + /** Whether all of [mobileSubViewModels] are visible or not. */ + private val iconsAreAllVisible = + mobileSubViewModels.flatMapLatest { viewModels -> + combine(viewModels.map { it.isVisible }) { isVisibleArray -> isVisibleArray.all { it } } + } + + val isStackable: StateFlow<Boolean> = + combine(iconsAreAllVisible, interactor.isStackable) { isVisible, isStackable -> + isVisible && isStackable + } + .stateIn(scope, SharingStarted.WhileSubscribed(), false) init { scope.launch { subscriptionIdsFlow.collect { invalidateCaches(it) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarComposeIconView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarComposeIconView.kt index 8076040564fb..9d1df8967fc0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarComposeIconView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/SingleBindableStatusBarComposeIconView.kt @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarV import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewVisibilityHelper import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow /** Compose view that is bound to bindable_status_bar_compose_icon.xml */ class SingleBindableStatusBarComposeIconView(context: Context, attrs: AttributeSet?) : @@ -78,7 +79,7 @@ class SingleBindableStatusBarComposeIconView(context: Context, attrs: AttributeS fun withDefaultBinding( view: SingleBindableStatusBarComposeIconView, shouldBeVisible: () -> Boolean, - block: suspend LifecycleOwner.(View, () -> Int) -> Unit, + block: suspend LifecycleOwner.(View, StateFlow<Int>) -> Unit, ): ModernStatusBarViewBinding { @StatusBarIconView.VisibleState val visibilityState: MutableStateFlow<Int> = MutableStateFlow(STATE_HIDDEN) @@ -90,7 +91,7 @@ class SingleBindableStatusBarComposeIconView(context: Context, attrs: AttributeS view.repeatWhenAttached { // Child binding - block(view) { iconTint.value } + block(view, iconTint) lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt index 8fa82cad5c32..72f9d550eb4d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt @@ -26,7 +26,6 @@ import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow class FakeMobileIconsInteractor( mobileMappings: MobileMappingsProxy, @@ -76,7 +75,7 @@ class FakeMobileIconsInteractor( override val icons: MutableStateFlow<List<MobileIconInteractor>> = MutableStateFlow(emptyList()) - override val isStackable: StateFlow<Boolean> = MutableStateFlow(false) + override val isStackable: MutableStateFlow<Boolean> = MutableStateFlow(false) private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING) override val defaultMobileIconMapping = _defaultMobileIconMapping |