diff options
| author | 2023-02-06 20:42:11 +0000 | |
|---|---|---|
| committer | 2023-02-07 21:26:29 +0000 | |
| commit | 2def6f9adf10b04e4e7216cf261d9b840658b68c (patch) | |
| tree | 97d6f4e10f8d3e42d8e88231e5b997b305b54c73 | |
| parent | 83e7763535c136d2239b40b112e9b0fe63fe00f0 (diff) | |
[SB Refactor] Migrate mobile's `state.visible` to the new pipeline.
StatusBarMobileView uses `MobileIconState.visible` to determine its
visibility status. Following all the inputs to `visible`, then
`visible = hasMobileData && !airplaneMode && !forceHiddenByTuning`. This
CL implements that boolean logic.
Bug: 238425913
Fixes: 267786582
Test: manual: turn on airplane mode and verify mobile triangle is hidden
every where. Turn off airplane mode and verify triangle re-appears.
Test: `adb shell pm enable com.android.systemui/.tuner.TunerActivity`
then Settings > System > System UI Tuner > Status bar > Toggle the
mobile
on/off and see the mobile icon toggle as well
Test: atest MobileIconViewModelTest
Change-Id: I5986ba477ba5172480ab4a5f7b9900259a8466ea
13 files changed, 319 insertions, 30 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt index 636d2cf91459..9b7614c2ad08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt @@ -111,6 +111,9 @@ interface MobileIconInteractor { /** Based on [CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL], either 4 or 5 */ val numberOfLevels: StateFlow<Int> + + /** See [MobileIconsInteractor.isForceHidden]. */ + val isForceHidden: Flow<Boolean> } /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */ @@ -126,6 +129,7 @@ class MobileIconInteractorImpl( defaultMobileIconGroup: StateFlow<MobileIconGroup>, defaultDataSubId: StateFlow<Int>, override val isDefaultConnectionFailed: StateFlow<Boolean>, + override val isForceHidden: Flow<Boolean>, connectionRepository: MobileConnectionRepository, ) : MobileIconInteractor { private val connectionInfo = connectionRepository.connectionInfo 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 bbb60f919730..94f7af42b54a 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 @@ -32,6 +32,8 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConn import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger +import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot +import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository import com.android.systemui.util.CarrierConfigTracker import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -45,8 +47,8 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest @@ -91,6 +93,10 @@ interface MobileIconsInteractor { val isDefaultConnectionFailed: StateFlow<Boolean> /** True once the user has been set up */ val isUserSetup: StateFlow<Boolean> + + /** True if we're configured to force-hide the mobile icons and false otherwise. */ + val isForceHidden: Flow<Boolean> + /** * Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given * subId. Will throw if the ID is invalid @@ -108,6 +114,7 @@ constructor( private val carrierConfigTracker: CarrierConfigTracker, private val logger: ConnectivityPipelineLogger, @MobileSummaryLog private val tableLogger: TableLogBuffer, + connectivityRepository: ConnectivityRepository, userSetupRepo: UserSetupRepository, @Application private val scope: CoroutineScope, ) : MobileIconsInteractor { @@ -291,6 +298,11 @@ constructor( override val isUserSetup: StateFlow<Boolean> = userSetupRepo.isUserSetupFlow + override val isForceHidden: Flow<Boolean> = + connectivityRepository.forceHiddenSlots + .map { it.contains(ConnectivitySlot.MOBILE) } + .stateIn(scope, SharingStarted.WhileSubscribed(), false) + /** Vends out new [MobileIconInteractor] for a particular subId */ override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor = MobileIconInteractorImpl( @@ -303,6 +315,7 @@ constructor( defaultMobileIconGroup, defaultDataSubId, isDefaultConnectionFailed, + isForceHidden, mobileConnectionsRepo.getRepoForSubId(subId), ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt index 890de70f4959..db585e68d185 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt @@ -91,6 +91,8 @@ object MobileIconBinder { } } + launch { viewModel.isVisible.collect { isVisible -> view.isVisible = isVisible } } + // Set the icon for the triangle launch { viewModel.icon.distinctUntilChanged().collect { icon -> @@ -153,8 +155,7 @@ object MobileIconBinder { return object : ModernStatusBarViewBinding { override fun getShouldIconBeVisible(): Boolean { - // If this view model exists, then the icon should be visible. - return true + return viewModel.isVisible.value } override fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt index b584398b10dc..049627899eff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt @@ -22,6 +22,7 @@ import com.android.settingslib.graph.SignalDrawable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.log.table.logDiffsForTable +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel @@ -42,6 +43,8 @@ import kotlinx.coroutines.flow.stateIn /** Common interface for all of the location-based mobile icon view models. */ interface MobileIconViewModelCommon { val subscriptionId: Int + /** True if this view should be visible at all. */ + val isVisible: StateFlow<Boolean> val icon: Flow<SignalIconModel> val contentDescription: Flow<ContentDescription> val roaming: Flow<Boolean> @@ -71,6 +74,7 @@ class MobileIconViewModel constructor( override val subscriptionId: Int, iconInteractor: MobileIconInteractor, + airplaneModeInteractor: AirplaneModeInteractor, constants: ConnectivityConstants, scope: CoroutineScope, ) : MobileIconViewModelCommon { @@ -78,6 +82,26 @@ constructor( private val showExclamationMark: Flow<Boolean> = iconInteractor.isDefaultDataEnabled.mapLatest { !it } + override val isVisible: StateFlow<Boolean> = + if (!constants.hasDataCapabilities) { + flowOf(false) + } else { + combine( + airplaneModeInteractor.isAirplaneMode, + iconInteractor.isForceHidden, + ) { isAirplaneMode, isForceHidden -> + !isAirplaneMode && !isForceHidden + } + } + .distinctUntilChanged() + .logDiffsForTable( + iconInteractor.tableLogBuffer, + columnPrefix = "", + columnName = "visible", + initialValue = false, + ) + .stateIn(scope, SharingStarted.WhileSubscribed(), false) + override val icon: Flow<SignalIconModel> = run { val initial = SignalIconModel.createEmptyState(iconInteractor.numberOfLevels.value) combine( 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 fa95e1fb5181..185b6685ba0e 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 @@ -17,9 +17,11 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel import androidx.annotation.VisibleForTesting +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants @@ -39,6 +41,7 @@ class MobileIconsViewModel constructor( val subscriptionIdsFlow: StateFlow<List<Int>>, private val interactor: MobileIconsInteractor, + private val airplaneModeInteractor: AirplaneModeInteractor, private val logger: ConnectivityPipelineLogger, private val constants: ConnectivityConstants, @Application private val scope: CoroutineScope, @@ -56,6 +59,7 @@ constructor( ?: MobileIconViewModel( subId, interactor.createMobileConnectionInteractorForSubId(subId), + airplaneModeInteractor, constants, scope, ) @@ -73,10 +77,12 @@ constructor( subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) } } + @SysUISingleton class Factory @Inject constructor( private val interactor: MobileIconsInteractor, + private val airplaneModeInteractor: AirplaneModeInteractor, private val logger: ConnectivityPipelineLogger, private val constants: ConnectivityConstants, @Application private val scope: CoroutineScope, @@ -86,6 +92,7 @@ constructor( return MobileIconsViewModel( subscriptionIdsFlow, interactor, + airplaneModeInteractor, logger, constants, scope, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt index 7aeaa48165aa..b9eda717dc1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt @@ -71,6 +71,8 @@ class FakeMobileIconInteractor( private val _numberOfLevels = MutableStateFlow(DEFAULT_NUM_LEVELS) override val numberOfLevels = _numberOfLevels + override val isForceHidden = MutableStateFlow(false) + fun setIconGroup(group: SignalIcon.MobileIconGroup) { _iconGroup.value = group } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt index 172755cb8d61..2699316d1b0b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt @@ -73,6 +73,8 @@ class FakeMobileIconsInteractor( private val _isUserSetup = MutableStateFlow(true) override val isUserSetup = _isUserSetup + override val isForceHidden = MutableStateFlow(false) + /** Always returns a new fake interactor */ override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor { return FakeMobileIconInteractor(tableLogBuffer) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt index c42aba5a7dd9..f87f651a2480 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt @@ -66,6 +66,7 @@ class MobileIconInteractorTest : SysuiTestCase() { mobileIconsInteractor.defaultMobileIconGroup, mobileIconsInteractor.defaultDataSubId, mobileIconsInteractor.isDefaultConnectionFailed, + mobileIconsInteractor.isForceHidden, connectionRepository, ) } @@ -550,6 +551,21 @@ class MobileIconInteractorTest : SysuiTestCase() { job.cancel() } + @Test + fun isForceHidden_matchesParent() = + runBlocking(IMMEDIATE) { + var latest: Boolean? = null + val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this) + + mobileIconsInteractor.isForceHidden.value = true + assertThat(latest).isTrue() + + mobileIconsInteractor.isForceHidden.value = false + assertThat(latest).isFalse() + + job.cancel() + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt index 23b634c7d2b8..f8a978300dd3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt @@ -27,6 +27,8 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobile import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy +import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository import com.android.systemui.util.CarrierConfigTracker import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -50,6 +52,7 @@ import org.mockito.MockitoAnnotations @SmallTest class MobileIconsInteractorTest : SysuiTestCase() { private lateinit var underTest: MobileIconsInteractor + private lateinit var connectivityRepository: FakeConnectivityRepository private lateinit var connectionsRepository: FakeMobileConnectionsRepository private val userSetupRepository = FakeUserSetupRepository() private val mobileMappingsProxy = FakeMobileMappingsProxy() @@ -63,6 +66,8 @@ class MobileIconsInteractorTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) + connectivityRepository = FakeConnectivityRepository() + connectionsRepository = FakeMobileConnectionsRepository(mobileMappingsProxy, tableLogBuffer) connectionsRepository.setMobileConnectionRepositoryMap( mapOf( @@ -80,6 +85,7 @@ class MobileIconsInteractorTest : SysuiTestCase() { carrierConfigTracker, logger = mock(), tableLogger = mock(), + connectivityRepository, userSetupRepository, testScope.backgroundScope, ) @@ -610,6 +616,32 @@ class MobileIconsInteractorTest : SysuiTestCase() { job.cancel() } + @Test + fun isForceHidden_repoHasMobileHidden_true() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this) + + connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE)) + + assertThat(latest).isTrue() + + job.cancel() + } + + @Test + fun isForceHidden_repoDoesNotHaveMobileHidden_false() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this) + + connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI)) + + assertThat(latest).isFalse() + + job.cancel() + } + companion object { private val tableLogBuffer = TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt index 39f77c9c7528..e68a3970ae93 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt @@ -29,11 +29,14 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -58,28 +61,36 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() { @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags @Mock private lateinit var tableLogBuffer: TableLogBuffer @Mock private lateinit var constants: ConnectivityConstants + private lateinit var interactor: FakeMobileIconInteractor + private lateinit var airplaneModeRepository: FakeAirplaneModeRepository + private lateinit var airplaneModeInteractor: AirplaneModeInteractor + private lateinit var viewModelCommon: MobileIconViewModel private lateinit var viewModel: LocationBasedMobileViewModel @Before fun setUp() { MockitoAnnotations.initMocks(this) - testableLooper = TestableLooper.get(this) + // This line was necessary to make the onDarkChanged and setStaticDrawableColor tests pass. + // But, it maybe *shouldn't* be necessary. + whenever(constants.hasDataCapabilities).thenReturn(true) - val interactor = FakeMobileIconInteractor(tableLogBuffer) + testableLooper = TestableLooper.get(this) - val viewModelCommon = - MobileIconViewModel( - subscriptionId = 1, - interactor, - constants, - testScope.backgroundScope, + airplaneModeRepository = FakeAirplaneModeRepository() + airplaneModeInteractor = + AirplaneModeInteractor( + airplaneModeRepository, + FakeConnectivityRepository(), ) - viewModel = QsMobileIconViewModel(viewModelCommon, statusBarPipelineFlags) + + interactor = FakeMobileIconInteractor(tableLogBuffer) + createViewModel() } // Note: The following tests are more like integration tests, since they stand up a full - // [WifiViewModel] and test the interactions between the view, view-binder, and view-model. + // [MobileIconViewModel] and test the interactions between the view, view-binder, and + // view-model. @Test fun setVisibleState_icon_iconShownDotHidden() { @@ -127,7 +138,25 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() { } @Test - fun isIconVisible_alwaysTrue() { + fun isIconVisible_noData_outputsFalse() { + whenever(constants.hasDataCapabilities).thenReturn(false) + createViewModel() + + val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel) + + ViewUtils.attachView(view) + testableLooper.processAllMessages() + + assertThat(view.isIconVisible).isFalse() + + ViewUtils.detachView(view) + } + + @Test + fun isIconVisible_hasData_outputsTrue() { + whenever(constants.hasDataCapabilities).thenReturn(true) + createViewModel() + val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel) ViewUtils.attachView(view) @@ -139,6 +168,34 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() { } @Test + fun isIconVisible_notAirplaneMode_outputsTrue() { + airplaneModeRepository.setIsAirplaneMode(false) + + val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel) + + ViewUtils.attachView(view) + testableLooper.processAllMessages() + + assertThat(view.isIconVisible).isTrue() + + ViewUtils.detachView(view) + } + + @Test + fun isIconVisible_airplaneMode_outputsTrue() { + airplaneModeRepository.setIsAirplaneMode(true) + + val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel) + + ViewUtils.attachView(view) + testableLooper.processAllMessages() + + assertThat(view.isIconVisible).isFalse() + + ViewUtils.detachView(view) + } + + @Test fun onDarkChanged_iconHasNewColor() { whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false) val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel) @@ -181,6 +238,18 @@ class ModernStatusBarMobileViewTest : SysuiTestCase() { private fun View.getDotView(): View { return this.requireViewById(R.id.status_bar_dot) } + + private fun createViewModel() { + viewModelCommon = + MobileIconViewModel( + subscriptionId = 1, + interactor, + airplaneModeInteractor, + constants, + testScope.backgroundScope, + ) + viewModel = QsMobileIconViewModel(viewModelCommon, statusBarPipelineFlags) + } } private const val SLOT_NAME = "TestSlotName" diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt index 4f36d6396fe0..f9830309252d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt @@ -21,10 +21,13 @@ import com.android.settingslib.mobile.TelephonyIcons import com.android.systemui.SysuiTestCase import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn @@ -46,6 +49,7 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() { private lateinit var qsIcon: QsMobileIconViewModel private lateinit var keyguardIcon: KeyguardMobileIconViewModel private lateinit var interactor: FakeMobileIconInteractor + private lateinit var airplaneModeInteractor: AirplaneModeInteractor @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags @Mock private lateinit var constants: ConnectivityConstants @Mock private lateinit var tableLogBuffer: TableLogBuffer @@ -56,6 +60,11 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) + airplaneModeInteractor = + AirplaneModeInteractor( + FakeAirplaneModeRepository(), + FakeConnectivityRepository(), + ) interactor = FakeMobileIconInteractor(tableLogBuffer) interactor.apply { setLevel(1) @@ -66,7 +75,14 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() { setNumberOfLevels(4) isDataConnected.value = true } - commonImpl = MobileIconViewModel(SUB_1_ID, interactor, constants, testScope.backgroundScope) + commonImpl = + MobileIconViewModel( + SUB_1_ID, + interactor, + airplaneModeInteractor, + constants, + testScope.backgroundScope, + ) homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags) qsIcon = QsMobileIconViewModel(commonImpl, statusBarPipelineFlags) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt index 83c902f031ac..bec276a9c68f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt @@ -24,10 +24,13 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -48,6 +51,8 @@ import org.mockito.MockitoAnnotations class MobileIconViewModelTest : SysuiTestCase() { private lateinit var underTest: MobileIconViewModel private lateinit var interactor: FakeMobileIconInteractor + private lateinit var airplaneModeRepository: FakeAirplaneModeRepository + private lateinit var airplaneModeInteractor: AirplaneModeInteractor @Mock private lateinit var constants: ConnectivityConstants @Mock private lateinit var tableLogBuffer: TableLogBuffer @@ -57,6 +62,15 @@ class MobileIconViewModelTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) + whenever(constants.hasDataCapabilities).thenReturn(true) + + airplaneModeRepository = FakeAirplaneModeRepository() + airplaneModeInteractor = + AirplaneModeInteractor( + airplaneModeRepository, + FakeConnectivityRepository(), + ) + interactor = FakeMobileIconInteractor(tableLogBuffer) interactor.apply { setLevel(1) @@ -67,10 +81,90 @@ class MobileIconViewModelTest : SysuiTestCase() { setNumberOfLevels(4) isDataConnected.value = true } - underTest = MobileIconViewModel(SUB_1_ID, interactor, constants, testScope.backgroundScope) + createAndSetViewModel() } @Test + fun isVisible_notDataCapable_alwaysFalse() = + testScope.runTest { + // Create a new view model here so the constants are properly read + whenever(constants.hasDataCapabilities).thenReturn(false) + createAndSetViewModel() + + var latest: Boolean? = null + val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + + assertThat(latest).isFalse() + + job.cancel() + } + + @Test + fun isVisible_notAirplane_notForceHidden_true() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + + airplaneModeRepository.setIsAirplaneMode(false) + interactor.isForceHidden.value = false + + assertThat(latest).isTrue() + + job.cancel() + } + + @Test + fun isVisible_airplane_false() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + + airplaneModeRepository.setIsAirplaneMode(true) + interactor.isForceHidden.value = false + + assertThat(latest).isFalse() + + job.cancel() + } + + @Test + fun isVisible_forceHidden_false() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + + airplaneModeRepository.setIsAirplaneMode(false) + interactor.isForceHidden.value = true + + assertThat(latest).isFalse() + + job.cancel() + } + + @Test + fun isVisible_respondsToUpdates() = + testScope.runTest { + var latest: Boolean? = null + val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + + airplaneModeRepository.setIsAirplaneMode(false) + interactor.isForceHidden.value = false + + assertThat(latest).isTrue() + + airplaneModeRepository.setIsAirplaneMode(true) + assertThat(latest).isFalse() + + airplaneModeRepository.setIsAirplaneMode(false) + assertThat(latest).isTrue() + + interactor.isForceHidden.value = true + assertThat(latest).isFalse() + + job.cancel() + } + + @Test fun iconId_correctLevel_notCutout() = testScope.runTest { var latest: SignalIconModel? = null @@ -361,13 +455,7 @@ class MobileIconViewModelTest : SysuiTestCase() { testScope.runTest { // Create a new view model here so the constants are properly read whenever(constants.shouldShowActivityConfig).thenReturn(false) - underTest = - MobileIconViewModel( - SUB_1_ID, - interactor, - constants, - testScope.backgroundScope, - ) + createAndSetViewModel() var inVisible: Boolean? = null val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this) @@ -399,13 +487,7 @@ class MobileIconViewModelTest : SysuiTestCase() { testScope.runTest { // Create a new view model here so the constants are properly read whenever(constants.shouldShowActivityConfig).thenReturn(true) - underTest = - MobileIconViewModel( - SUB_1_ID, - interactor, - constants, - testScope.backgroundScope, - ) + createAndSetViewModel() var inVisible: Boolean? = null val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this) @@ -454,6 +536,16 @@ class MobileIconViewModelTest : SysuiTestCase() { containerJob.cancel() } + private fun createAndSetViewModel() { + underTest = MobileIconViewModel( + SUB_1_ID, + interactor, + airplaneModeInteractor, + constants, + testScope.backgroundScope, + ) + } + companion object { private const val SUB_1_ID = 1 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt index 58b50c7e7e6d..d9268a2c3b94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt @@ -20,11 +20,14 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -46,6 +49,7 @@ class MobileIconsViewModelTest : SysuiTestCase() { private lateinit var underTest: MobileIconsViewModel private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock()) + private lateinit var airplaneModeInteractor: AirplaneModeInteractor @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags @Mock private lateinit var logger: ConnectivityPipelineLogger @Mock private lateinit var constants: ConnectivityConstants @@ -57,6 +61,12 @@ class MobileIconsViewModelTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) + airplaneModeInteractor = + AirplaneModeInteractor( + FakeAirplaneModeRepository(), + FakeConnectivityRepository(), + ) + val subscriptionIdsFlow = interactor.filteredSubscriptions .map { subs -> subs.map { it.subscriptionId } } @@ -66,6 +76,7 @@ class MobileIconsViewModelTest : SysuiTestCase() { MobileIconsViewModel( subscriptionIdsFlow, interactor, + airplaneModeInteractor, logger, constants, testScope.backgroundScope, |