diff options
20 files changed, 552 insertions, 36 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt index db24d4bc8070..fdc77993437b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt @@ -21,13 +21,25 @@ import android.app.StatusBarManager.DISABLE_CLOCK import android.app.StatusBarManager.DISABLE_NONE import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS import android.app.StatusBarManager.DISABLE_SYSTEM_INFO +import android.telephony.CarrierConfigManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel +import com.android.systemui.statusbar.pipeline.airplane.data.repository.airplaneModeRepository +import com.android.systemui.statusbar.pipeline.airplane.data.repository.fake +import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig +import com.android.systemui.statusbar.pipeline.mobile.data.repository.carrierConfigRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.configWithOverride +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor +import com.android.systemui.statusbar.pipeline.shared.connectivityConstants +import com.android.systemui.statusbar.pipeline.shared.fake import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.test.Test @@ -45,7 +57,7 @@ class CollapsedStatusBarInteractorTest : SysuiTestCase() { @Test fun visibilityViaDisableFlags_allDisabled() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.visibilityViaDisableFlags) disableFlagsRepo.disableFlags.value = @@ -62,7 +74,7 @@ class CollapsedStatusBarInteractorTest : SysuiTestCase() { @Test fun visibilityViaDisableFlags_allEnabled() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.visibilityViaDisableFlags) disableFlagsRepo.disableFlags.value = @@ -75,7 +87,7 @@ class CollapsedStatusBarInteractorTest : SysuiTestCase() { @Test fun visibilityViaDisableFlags_animateFalse() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.visibilityViaDisableFlags) disableFlagsRepo.disableFlags.value = @@ -86,7 +98,7 @@ class CollapsedStatusBarInteractorTest : SysuiTestCase() { @Test fun visibilityViaDisableFlags_animateTrue() = - testScope.runTest { + kosmos.runTest { val latest by collectLastValue(underTest.visibilityViaDisableFlags) disableFlagsRepo.disableFlags.value = @@ -94,4 +106,106 @@ class CollapsedStatusBarInteractorTest : SysuiTestCase() { assertThat(latest!!.animate).isTrue() } + + @Test + fun shouldShowOperatorName_trueIfCarrierConfigSaysSoAndDeviceHasData() = + kosmos.runTest { + // GIVEN default data subId is 1 + fakeMobileIconsInteractor.defaultDataSubId.value = 1 + // GIVEN Config is enabled + carrierConfigRepository.fake.configsById[1] = + SystemUiCarrierConfig( + 1, + configWithOverride( + CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, + true, + ), + ) + + // GIVEN airplane mode is off + airplaneModeRepository.fake.isAirplaneMode.value = false + + // GIVEN hasDataCapabilities is true + connectivityConstants.fake.hasDataCapabilities = true + + val latest by collectLastValue(underTest.shouldShowOperatorName) + + // THEN we should show the operator name + assertThat(latest).isTrue() + } + + @Test + fun shouldShowOperatorName_falseNoDataCapabilities() = + kosmos.runTest { + // GIVEN default data subId is 1 + fakeMobileIconsInteractor.defaultDataSubId.value = 1 + // GIVEN Config is enabled + carrierConfigRepository.fake.configsById[1] = + SystemUiCarrierConfig( + 1, + configWithOverride( + CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, + true, + ), + ) + + // GIVEN airplane mode is off + airplaneModeRepository.fake.isAirplaneMode.value = true + + // WHEN hasDataCapabilities is false + connectivityConstants.fake.hasDataCapabilities = false + + val latest by collectLastValue(underTest.shouldShowOperatorName) + + // THEN we should not show the operator name + assertThat(latest).isFalse() + } + + @Test + fun shouldShowOperatorName_falseWhenConfigIsOff() = + kosmos.runTest { + // GIVEN default data subId is 1 + fakeMobileIconsInteractor.defaultDataSubId.value = 1 + // GIVEN airplane mode is off + airplaneModeRepository.fake.isAirplaneMode.value = false + + // WHEN Config is disabled + carrierConfigRepository.fake.configsById[1] = + SystemUiCarrierConfig( + 1, + configWithOverride( + CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, + false, + ), + ) + + val latest by collectLastValue(underTest.shouldShowOperatorName) + + // THEN we should not show the operator name + assertThat(latest).isFalse() + } + + @Test + fun shouldShowOperatorName_falseIfAirplaneMode() = + kosmos.runTest { + // GIVEN default data subId is 1 + fakeMobileIconsInteractor.defaultDataSubId.value = 1 + // GIVEN Config is enabled + carrierConfigRepository.fake.configsById[1] = + SystemUiCarrierConfig( + 1, + configWithOverride( + CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, + true, + ), + ) + + // WHEN airplane mode is on + airplaneModeRepository.fake.isAirplaneMode.value = true + + val latest by collectLastValue(underTest.shouldShowOperatorName) + + // THEN we should not show the operator name + assertThat(latest).isFalse() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt index f8070e817a3e..3131a256b039 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -27,7 +27,9 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -class FakeHomeStatusBarViewModel : HomeStatusBarViewModel { +class FakeHomeStatusBarViewModel( + override val operatorNameViewModel: StatusBarOperatorNameViewModel +) : HomeStatusBarViewModel { private val areNotificationLightsOut = MutableStateFlow(false) override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false) @@ -41,6 +43,8 @@ class FakeHomeStatusBarViewModel : HomeStatusBarViewModel { override val isHomeStatusBarAllowedByScene = MutableStateFlow(false) + override val shouldShowOperatorNameView = MutableStateFlow(false) + override val isClockVisible = MutableStateFlow( HomeStatusBarViewModel.VisibilityModel( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt index 2d09ab66b250..c5502aefa80b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt @@ -77,6 +77,7 @@ import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataS import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher import com.android.systemui.statusbar.phone.data.repository.fakeDarkIconRepository +import com.android.systemui.statusbar.pipeline.shared.domain.interactor.setCollapsedStatusBarInteractorShowOperatorName import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -500,6 +501,72 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { } @Test + fun shouldShowOperatorNameView_allowedByInteractor_allowedByDisableFlags_visible() = + kosmos.runTest { + kosmos.setCollapsedStatusBarInteractorShowOperatorName(true) + + val latest by collectLastValue(underTest.shouldShowOperatorNameView) + transitionKeyguardToGone() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) + + assertThat(latest).isTrue() + } + + @Test + fun shouldShowOperatorNameView_disAllowedByInteractor_allowedByDisableFlags_notVisible() = + kosmos.runTest { + kosmos.setCollapsedStatusBarInteractorShowOperatorName(false) + + transitionKeyguardToGone() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) + + val latest by collectLastValue(underTest.shouldShowOperatorNameView) + + assertThat(latest).isFalse() + } + + @Test + fun shouldShowOperatorNameView_allowedByInteractor_disallowedByDisableFlags_notVisible() = + kosmos.runTest { + kosmos.setCollapsedStatusBarInteractorShowOperatorName(true) + + val latest by collectLastValue(underTest.shouldShowOperatorNameView) + transitionKeyguardToGone() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_SYSTEM_INFO, DISABLE2_NONE) + + assertThat(latest).isFalse() + } + + @Test + fun shouldShowOperatorNameView_allowedByInteractor_hunPinned_false() = + kosmos.runTest { + kosmos.setCollapsedStatusBarInteractorShowOperatorName(false) + + transitionKeyguardToGone() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) + + // there is an active HUN + headsUpNotificationRepository.setNotifications( + UnconfinedFakeHeadsUpRowRepository( + key = "key", + pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser), + ) + ) + + val latest by collectLastValue(underTest.shouldShowOperatorNameView) + + assertThat(latest).isFalse() + } + + @Test fun isClockVisible_allowedByDisableFlags_visible() = kosmos.runTest { val latest by collectLastValue(underTest.isClockVisible) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt new file mode 100644 index 000000000000..20cc85f08b01 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 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.shared.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class StatusBarOperatorNameViewModelTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val Kosmos.underTest by Kosmos.Fixture { kosmos.statusBarOperatorNameViewModel } + + @Test + fun operatorName_tracksDefaultDataCarrierName() = + kosmos.runTest { + val intr1 = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(1) + val intr2 = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(2) + val invalidIntr = fakeMobileIconsInteractor.getMobileConnectionInteractorForSubId(-1) + + // GIVEN default data subId is 1 + fakeMobileIconsInteractor.defaultDataSubId.value = 1 + + intr1.carrierName.value = "Test Name 1" + intr2.carrierName.value = "Test Name 2" + invalidIntr.carrierName.value = "default network name" + + val latest by collectLastValue(underTest.operatorName) + + assertThat(latest).isEqualTo("Test Name 1") + + fakeMobileIconsInteractor.defaultDataSubId.value = 2 + + assertThat(latest).isEqualTo("Test Name 2") + + fakeMobileIconsInteractor.defaultDataSubId.value = -1 + + assertThat(latest).isEqualTo("default network name") + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt index a88b74cafe56..96666d83b39b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt @@ -50,6 +50,8 @@ import com.android.systemui.statusbar.pipeline.satellite.data.RealDeviceBasedSat import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl import com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel.DeviceBasedSatelliteViewModel import com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel.DeviceBasedSatelliteViewModelImpl +import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants +import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstantsImpl import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder @@ -108,6 +110,9 @@ abstract class StatusBarPipelineModule { impl: DeviceBasedSatelliteViewModelImpl ): DeviceBasedSatelliteViewModel + @Binds + abstract fun connectivityConstants(impl: ConnectivityConstantsImpl): ConnectivityConstants + @Binds abstract fun wifiRepository(impl: WifiRepositorySwitcher): WifiRepository @Binds abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt index 9ea167f4815c..09314efdedc0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstants.kt @@ -19,9 +19,9 @@ package com.android.systemui.statusbar.pipeline.shared import android.content.Context import android.telephony.TelephonyManager import com.android.systemui.Dumpable -import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager +import com.android.systemui.res.R import java.io.PrintWriter import javax.inject.Inject @@ -30,23 +30,26 @@ import javax.inject.Inject * * Stored in a class for logging purposes. */ +interface ConnectivityConstants { + /** True if this device has the capability for data connections and false otherwise. */ + val hasDataCapabilities: Boolean + + /** True if we should show the activityIn/activityOut icons and false otherwise */ + val shouldShowActivityConfig: Boolean +} + @SysUISingleton -class ConnectivityConstants +class ConnectivityConstantsImpl @Inject -constructor( - context: Context, - dumpManager: DumpManager, - telephonyManager: TelephonyManager, -) : Dumpable { +constructor(context: Context, dumpManager: DumpManager, telephonyManager: TelephonyManager) : + ConnectivityConstants, Dumpable { init { dumpManager.registerNormalDumpable("ConnectivityConstants", this) } - /** True if this device has the capability for data connections and false otherwise. */ - val hasDataCapabilities = telephonyManager.isDataCapable + override val hasDataCapabilities = telephonyManager.isDataCapable - /** True if we should show the activityIn/activityOut icons and false otherwise */ - val shouldShowActivityConfig = context.resources.getBoolean(R.bool.config_showActivity) + override val shouldShowActivityConfig = context.resources.getBoolean(R.bool.config_showActivity) override fun dump(pw: PrintWriter, args: Array<out String>) { pw.apply { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt index a0cb8298bdb2..730f1be660f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt @@ -18,19 +18,30 @@ package com.android.systemui.statusbar.pipeline.shared.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.CarrierConfigInteractor import com.android.systemui.statusbar.pipeline.shared.domain.model.StatusBarDisableFlagsVisibilityModel import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map /** * Interactor for the home screen status bar (aka * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment]). */ +@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class CollapsedStatusBarInteractor @Inject -constructor(disableFlagsInteractor: DisableFlagsInteractor) { +constructor( + airplaneModeInteractor: AirplaneModeInteractor, + carrierConfigInteractor: CarrierConfigInteractor, + disableFlagsInteractor: DisableFlagsInteractor, +) { /** * The visibilities of various status bar child views, based only on the information we received * from disable flags. @@ -44,4 +55,21 @@ constructor(disableFlagsInteractor: DisableFlagsInteractor) { animate = it.animate, ) } + + private val defaultDataSubConfigShowOperatorView = + carrierConfigInteractor.defaultDataSubscriptionCarrierConfig.flatMapLatest { + it?.showOperatorNameInStatusBar ?: flowOf(false) + } + + /** + * True if the carrier config for the default data subscription has + * [SystemUiCarrierConfig.showOperatorNameInStatusBar] set and the device is not in airplane + * mode + */ + val shouldShowOperatorName: Flow<Boolean> = + combine(defaultDataSubConfigShowOperatorView, airplaneModeInteractor.isAirplaneMode) { + showOperatorName, + isAirplaneMode -> + showOperatorName && !isAirplaneMode + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt index d9b2bd1d66c9..7e06c35315f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.binder import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.view.View +import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -204,6 +205,18 @@ constructor( } if (StatusBarRootModernization.isEnabled) { + val operatorNameView = view.requireViewById<View>(R.id.operator_name_frame) + StatusBarOperatorNameViewBinder.bind( + operatorNameView, + viewModel.operatorNameViewModel, + viewModel::areaTint, + ) + launch { + viewModel.shouldShowOperatorNameView.collect { + operatorNameView.isVisible = it + } + } + val clockView = view.requireViewById<View>(R.id.clock) launch { viewModel.isClockVisible.collect { clockView.adjustVisibility(it) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarOperatorNameViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarOperatorNameViewBinder.kt new file mode 100644 index 000000000000..b7744d34560d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarOperatorNameViewBinder.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 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.shared.ui.binder + +import android.view.View +import android.widget.TextView +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.StatusBarOperatorNameViewModel +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.StatusBarTintColor +import com.android.systemui.util.view.viewBoundsOnScreen +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.launch + +object StatusBarOperatorNameViewBinder { + fun bind( + operatorFrameView: View, + viewModel: StatusBarOperatorNameViewModel, + areaTint: (Int) -> Flow<StatusBarTintColor>, + ) { + operatorFrameView.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + val displayId = operatorFrameView.display.displayId + + val operatorNameText = + operatorFrameView.requireViewById<TextView>(R.id.operator_name) + launch { viewModel.operatorName.collect { operatorNameText.text = it } } + + launch { + val tint = areaTint(displayId) + tint.collect { statusBarTintColors -> + operatorNameText.setTextColor( + statusBarTintColors.tint(operatorNameText.viewBoundsOnScreen()) + ) + } + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt index b472725b833f..5abcdac8fceb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt @@ -95,6 +95,9 @@ interface HomeStatusBarViewModel { */ val ongoingActivityChips: StateFlow<MultipleOngoingActivityChipsModel> + /** View model for the carrier name that may show in the status bar based on carrier config */ + val operatorNameViewModel: StatusBarOperatorNameViewModel + /** * True if the current scene can show the home status bar (aka this status bar), and false if * the current scene should never show the home status bar. @@ -104,6 +107,8 @@ interface HomeStatusBarViewModel { */ val isHomeStatusBarAllowedByScene: StateFlow<Boolean> + /** True if the operator name view is not hidden due to HUN or other visibility state */ + val shouldShowOperatorNameView: Flow<Boolean> val isClockVisible: Flow<VisibilityModel> val isNotificationIconContainerVisible: Flow<VisibilityModel> /** @@ -155,6 +160,7 @@ constructor( headsUpNotificationInteractor: HeadsUpNotificationInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, keyguardInteractor: KeyguardInteractor, + override val operatorNameViewModel: StatusBarOperatorNameViewModel, sceneInteractor: SceneInteractor, sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor, shadeInteractor: ShadeInteractor, @@ -260,6 +266,20 @@ constructor( primaryOngoingActivityChip.map { it is OngoingActivityChipModel.Shown } } + override val shouldShowOperatorNameView: Flow<Boolean> = + combine( + shouldHomeStatusBarBeVisible, + headsUpNotificationInteractor.statusBarHeadsUpState, + collapsedStatusBarInteractor.visibilityViaDisableFlags, + collapsedStatusBarInteractor.shouldShowOperatorName, + ) { shouldStatusBarBeVisible, headsUpState, visibilityViaDisableFlags, shouldShowOperator -> + val hideForHeadsUp = headsUpState == PinnedStatus.PinnedBySystem + shouldStatusBarBeVisible && + !hideForHeadsUp && + visibilityViaDisableFlags.isSystemInfoAllowed && + shouldShowOperator + } + override val isClockVisible: Flow<VisibilityModel> = combine( shouldHomeStatusBarBeVisible, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt new file mode 100644 index 000000000000..7ae74c3bfb65 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModel.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 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.shared.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flatMapLatest + +/** + * View model for the operator name (aka carrier name) of the carrier for the default data + * subscription. + */ +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton +class StatusBarOperatorNameViewModel +@Inject +constructor(mobileIconsInteractor: MobileIconsInteractor) { + val operatorName: Flow<String?> = + mobileIconsInteractor.defaultDataSubId.flatMapLatest { + mobileIconsInteractor.getMobileConnectionInteractorForSubId(it).carrierName + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index b39e38bd71cd..0b4436755fa5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -73,6 +73,7 @@ import com.android.systemui.statusbar.phone.ui.DarkIconManager; import com.android.systemui.statusbar.phone.ui.StatusBarIconController; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewBinder; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewModel; +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.StatusBarOperatorNameViewModel; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; @@ -1245,7 +1246,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mSecureSettings = mock(SecureSettings.class); mShadeExpansionStateManager = new ShadeExpansionStateManager(); - mCollapsedStatusBarViewModel = new FakeHomeStatusBarViewModel(); + mCollapsedStatusBarViewModel = new FakeHomeStatusBarViewModel( + mock(StatusBarOperatorNameViewModel.class)); mCollapsedStatusBarViewBinder = new FakeHomeStatusBarViewBinder(); return new CollapsedStatusBarFragment( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryKosmos.kt new file mode 100644 index 000000000000..02ae3ad03dad --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 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.airplane.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.airplaneModeRepository by Kosmos.Fixture { FakeAirplaneModeRepository() } + +val AirplaneModeRepository.fake + get() = this as FakeAirplaneModeRepository diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt index 74b2da49d43f..c30124c1f1cb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt @@ -17,14 +17,12 @@ package com.android.systemui.statusbar.pipeline.airplane.data.repository import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow class FakeAirplaneModeRepository : AirplaneModeRepository { - private val _isAirplaneMode = MutableStateFlow(false) - override val isAirplaneMode: StateFlow<Boolean> = _isAirplaneMode + override val isAirplaneMode = MutableStateFlow(false) override suspend fun setIsAirplaneMode(isEnabled: Boolean) { - _isAirplaneMode.value = isEnabled + isAirplaneMode.value = isEnabled } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt index 99ed4f0db64d..62d7601d7232 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt @@ -17,15 +17,15 @@ package com.android.systemui.statusbar.pipeline.airplane.domain.interactor import com.android.systemui.kosmos.Kosmos -import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository +import com.android.systemui.statusbar.pipeline.airplane.data.repository.airplaneModeRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository val Kosmos.airplaneModeInteractor: AirplaneModeInteractor by Kosmos.Fixture { AirplaneModeInteractor( - FakeAirplaneModeRepository(), - FakeConnectivityRepository(), + airplaneModeRepository, + connectivityRepository, mobileConnectionsRepository, ) } 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 652d105f7c92..3b8adb4a8307 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 @@ -60,7 +60,8 @@ class FakeMobileIconsInteractor( private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false) override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled - override val activeDataIconInteractor = MutableStateFlow(null) + override val activeDataIconInteractor: MutableStateFlow<MobileIconInteractor?> = + MutableStateFlow(null) override val alwaysShowDataRatIcon = MutableStateFlow(false) @@ -85,13 +86,14 @@ class FakeMobileIconsInteractor( override val isDeviceInEmergencyCallsOnlyMode = MutableStateFlow(false) - /** Always returns a new fake interactor */ override fun getMobileConnectionInteractorForSubId(subId: Int): FakeMobileIconInteractor { - return FakeMobileIconInteractor(tableLogBuffer).also { - interactorCache[subId] = it - // Also update the icons - icons.value = interactorCache.values.toList() - } + return interactorCache + .getOrElse(subId) { FakeMobileIconInteractor(tableLogBuffer) } + .also { + interactorCache[subId] = it + // Also update the icons + icons.value = interactorCache.values.toList() + } } /** diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstantsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstantsKosmos.kt new file mode 100644 index 000000000000..3bdddf8b9a0b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityConstantsKosmos.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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.shared + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.connectivityConstants by Kosmos.Fixture { FakeConnectivityConstnants() } + +class FakeConnectivityConstnants : ConnectivityConstants { + override var hasDataCapabilities: Boolean = true + + override var shouldShowActivityConfig: Boolean = false +} + +val ConnectivityConstants.fake + get() = this as FakeConnectivityConstnants diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt index 13fde9608017..c665bd0aa310 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.kt @@ -16,8 +16,32 @@ package com.android.systemui.statusbar.pipeline.shared.domain.interactor +import android.telephony.CarrierConfigManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor +import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig +import com.android.systemui.statusbar.pipeline.mobile.data.repository.carrierConfigRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.configWithOverride +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.carrierConfigInteractor +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor val Kosmos.collapsedStatusBarInteractor: CollapsedStatusBarInteractor by - Kosmos.Fixture { CollapsedStatusBarInteractor(disableFlagsInteractor) } + Kosmos.Fixture { + CollapsedStatusBarInteractor( + airplaneModeInteractor, + carrierConfigInteractor, + disableFlagsInteractor, + ) + } + +/** Set the default data subId to 1, and sets the carrier config setting to [show] */ +fun Kosmos.setCollapsedStatusBarInteractorShowOperatorName(show: Boolean) { + fakeMobileIconsInteractor.defaultDataSubId.value = 1 + carrierConfigRepository.fake.configsById[1] = + SystemUiCarrierConfig( + 1, + configWithOverride(CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, show), + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt index f46a6cc8c8ec..02f96ee85e20 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt @@ -31,7 +31,7 @@ import com.android.systemui.statusbar.phone.domain.interactor.darkIconInteractor import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor -val Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by +var Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by Kosmos.Fixture { HomeStatusBarViewModelImpl( collapsedStatusBarInteractor, @@ -41,6 +41,7 @@ val Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by headsUpNotificationInteractor, keyguardTransitionInteractor, keyguardInteractor, + statusBarOperatorNameViewModel, sceneInteractor, sceneContainerOcclusionInteractor, shadeInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelKosmos.kt new file mode 100644 index 000000000000..5887e3c323d9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/StatusBarOperatorNameViewModelKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 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.shared.ui.viewmodel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractor + +val Kosmos.statusBarOperatorNameViewModel by + Kosmos.Fixture { StatusBarOperatorNameViewModel(mobileIconsInteractor) } |