diff options
| author | 2024-09-16 22:27:19 +0000 | |
|---|---|---|
| committer | 2024-09-20 14:58:21 +0000 | |
| commit | 190697ffca9de8f313b64e1fe254a2ee03464a02 (patch) | |
| tree | 00522a2076e5b948fbd06feb7d7d9d47819861fc | |
| parent | 6010338e52eb79f9634299b39780e0ba00fc4573 (diff) | |
[SB] Set up is<ChildView>Visible flows in CollapsedStatusBarViewModel.
We want to pull out all the business logic from
CollapsedStatusBarFragment and put it in interactors and view models
instead. This CL sets up three new flows in CollapsedStatusBarViewModel
and populates them based on disable flags:
- isClockVisible
- areNotificationIconsVisible
- isSystemInfoVisible
The visibilities of those child views is dependent on a *lot* more than
just the disable flags, but this CL adds the scaffolding for filling in
those visibilities. Future CLs should *just* touch
CollapsedStatusBarInteractor and/or CollapsedStatusBarViewModel to add
the visibility nuances.
Bug: 364360986
Flag: com.android.systemui.status_bar_simple_fragment
Test: atest CollapsedStatusBarInteractorTest
CollapsedStatusBarViewModelImplTest
Change-Id: I1272d78d4435404272049ddcf7de89bd0290cae0
12 files changed, 575 insertions, 42 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt index 2bb476523cb8..ce25cf5895c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt @@ -17,8 +17,12 @@ package com.android.systemui.statusbar.disableflags.data.model import android.app.StatusBarManager.DISABLE2_NONE import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS +import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS +import android.app.StatusBarManager.DISABLE_CLOCK import android.app.StatusBarManager.DISABLE_NONE import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS +import android.app.StatusBarManager.DISABLE_SYSTEM_INFO import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.disableflags.DisableFlagsLogger @@ -27,12 +31,14 @@ import com.android.systemui.statusbar.disableflags.DisableFlagsLogger * Model for the disable flags that come from [IStatusBar]. * * For clients of the disable flags: do *not* refer to the disable integers directly. Instead, - * re-use or define a helper method that internally processes the flags. (We want to hide the - * bitwise logic here so no one else has to worry about it.) + * re-use or define a helper method or property that internally processes the flags. (We want to + * hide the bitwise logic here so no one else has to worry about it.) */ data class DisableFlagsModel( private val disable1: Int = DISABLE_NONE, private val disable2: Int = DISABLE2_NONE, + /** True if we should animate any view visibility changes and false otherwise. */ + val animate: Boolean = false, ) { /** Returns true if notification alerts are allowed based on the flags. */ fun areNotificationAlertsEnabled(): Boolean { @@ -49,6 +55,13 @@ data class DisableFlagsModel( return (disable2 and DISABLE2_QUICK_SETTINGS) == 0 } + val isClockEnabled = (disable1 and DISABLE_CLOCK) == 0 + + val areNotificationIconsEnabled = (disable1 and DISABLE_NOTIFICATION_ICONS) == 0 + + val isSystemInfoEnabled = + (disable1 and DISABLE_SYSTEM_INFO) == 0 && (disable2 and DISABLE2_SYSTEM_ICONS) == 0 + /** Logs the change to the provided buffer. */ fun logChange(buffer: LogBuffer, disableFlagsLogger: DisableFlagsLogger) { buffer.log( @@ -60,9 +73,9 @@ data class DisableFlagsModel( }, { disableFlagsLogger.getDisableFlagsString( - new = DisableFlagsLogger.DisableState(int1, int2), + new = DisableFlagsLogger.DisableState(int1, int2) ) - } + }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt index 13b74b493905..9004e5d12663 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt @@ -72,6 +72,7 @@ constructor( // [QuickSettingsInteractor]-type class. However, that's out of // scope for the CentralSurfaces removal project. remoteInputQuickSettingsDisabler.adjustDisableFlags(state2), + animate, ) ) } @@ -82,5 +83,5 @@ constructor( .distinctUntilChanged() .onEach { it.logChange(logBuffer, disableFlagsLogger) } // Use Eagerly because we always need to know about disable flags - .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel()) + .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel(animate = false)) } 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 new file mode 100644 index 000000000000..9164da721e3a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractor.kt @@ -0,0 +1,47 @@ +/* + * 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.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository +import com.android.systemui.statusbar.pipeline.shared.domain.model.StatusBarDisableFlagsVisibilityModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** + * Interactor for the home screen status bar (aka + * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment]). + */ +@SysUISingleton +class CollapsedStatusBarInteractor +@Inject +constructor(disableFlagsRepository: DisableFlagsRepository) { + /** + * The visibilities of various status bar child views, based only on the information we received + * from disable flags. + */ + val visibilityViaDisableFlags: Flow<StatusBarDisableFlagsVisibilityModel> = + disableFlagsRepository.disableFlags.map { + StatusBarDisableFlagsVisibilityModel( + isClockAllowed = it.isClockEnabled, + areNotificationIconsAllowed = it.areNotificationIconsEnabled, + isSystemInfoAllowed = it.isSystemInfoEnabled, + animate = it.animate, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/model/StatusBarDisableFlagsVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/model/StatusBarDisableFlagsVisibilityModel.kt new file mode 100644 index 000000000000..69e9746ee24f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/domain/model/StatusBarDisableFlagsVisibilityModel.kt @@ -0,0 +1,32 @@ +/* + * 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.domain.model + +/** + * Represents the visibilities of various status bar child views, based only on the information we + * received from disable flags. + */ +data class StatusBarDisableFlagsVisibilityModel( + /** True if the clock is allowed to be shown. */ + val isClockAllowed: Boolean, + /** True if the notification icons are allowed to be shown. */ + val areNotificationIconsAllowed: Boolean, + /** True if the system information (wifi, mobile, etc.) is allowed to be shown. */ + val isSystemInfoAllowed: Boolean, + /** True if we should animate any view visibility changes and false otherwise. */ + val animate: Boolean, +) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index 49eabba5c2b0..4cb66c19a0bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.animation.Interpolators import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached @@ -28,7 +29,9 @@ import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.core.StatusBarSimpleFragment import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor +import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel import javax.inject.Inject import kotlinx.coroutines.launch @@ -134,6 +137,29 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } } + + if (StatusBarSimpleFragment.isEnabled) { + val clockView = view.requireViewById<View>(R.id.clock) + launch { viewModel.isClockVisible.collect { clockView.adjustVisibility(it) } } + + val notificationIconsArea = view.requireViewById<View>(R.id.notificationIcons) + launch { + viewModel.isNotificationIconContainerVisible.collect { + notificationIconsArea.adjustVisibility(it) + } + } + + val systemInfoView = + view.requireViewById<View>(R.id.status_bar_end_side_content) + // TODO(b/364360986): Also handle operator name view. + launch { + viewModel.isSystemInfoVisible.collect { + systemInfoView.adjustVisibility(it) + // TODO(b/364360986): The system info view has a custom alpha controller + // in CollapsedStatusBarFragment. + } + } + } } } } @@ -167,6 +193,54 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa ) .start() } + + private fun View.adjustVisibility(model: CollapsedStatusBarViewModel.VisibilityModel) { + if (model.visibility == View.VISIBLE) { + this.show(model.shouldAnimateChange) + } else { + this.hide(model.visibility, model.shouldAnimateChange) + } + } + + // See CollapsedStatusBarFragment#hide. + private fun View.hide(state: Int = View.INVISIBLE, shouldAnimateChange: Boolean) { + val v = this + v.animate().cancel() + if (!shouldAnimateChange) { + v.alpha = 0f + v.visibility = state + return + } + + v.animate() + .alpha(0f) + .setDuration(CollapsedStatusBarFragment.FADE_OUT_DURATION.toLong()) + .setStartDelay(0) + .setInterpolator(Interpolators.ALPHA_OUT) + .withEndAction { v.visibility = state } + } + + // See CollapsedStatusBarFragment#show. + private fun View.show(shouldAnimateChange: Boolean) { + val v = this + v.animate().cancel() + v.visibility = View.VISIBLE + if (!shouldAnimateChange) { + v.alpha = 1f + return + } + v.animate() + .alpha(1f) + .setDuration(CollapsedStatusBarFragment.FADE_IN_DURATION.toLong()) + .setInterpolator(Interpolators.ALPHA_IN) + .setStartDelay(CollapsedStatusBarFragment.FADE_IN_DELAY.toLong()) + // We need to clean up any pending end action from animateHide if we call both hide and + // show in the same frame before the animation actually gets started. + // cancel() doesn't really remove the end action. + .withEndAction(null) + + // TODO(b/364360986): Synchronize the motion with the Keyguard fading if necessary. + } } /** Listener for various events that may affect the status bar's visibility. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt index 9cce2b8fb72b..1c499257bb4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import android.view.View import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -33,6 +34,8 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsVie import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor +import com.android.systemui.statusbar.pipeline.shared.domain.interactor.CollapsedStatusBarInteractor +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel.VisibilityModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -80,9 +83,18 @@ interface CollapsedStatusBarViewModel { /** * 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. + * + * TODO(b/364360986): Once the is<SomeChildView>Visible flows are fully enabled, we shouldn't + * need this flow anymore. */ val isHomeStatusBarAllowedByScene: StateFlow<Boolean> + val isClockVisible: Flow<VisibilityModel> + val isNotificationIconContainerVisible: Flow<VisibilityModel> + val isSystemInfoVisible: Flow<VisibilityModel> + + // TODO(b/364360986): Add isOngoingActivityChipVisible: Flow<VisibilityModel> + /** * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where * status bar and navigation icons dim. In this mode, a notification dot appears where the @@ -93,12 +105,20 @@ interface CollapsedStatusBarViewModel { * [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE]. */ fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> + + /** Models the current visibility for a specific child view of status bar. */ + data class VisibilityModel( + @View.Visibility val visibility: Int, + /** True if a visibility change should be animated. */ + val shouldAnimateChange: Boolean, + ) } @SysUISingleton class CollapsedStatusBarViewModelImpl @Inject constructor( + collapsedStatusBarInteractor: CollapsedStatusBarInteractor, private val lightsOutInteractor: LightsOutInteractor, private val notificationsInteractor: ActiveNotificationsInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, @@ -148,4 +168,26 @@ constructor( } .distinctUntilChanged() } + + // TODO(b/364360986): For all the is<SomeChildView>Visible flows, take other conditions into + // account like whether we're on keyguard or shade. + + override val isClockVisible: Flow<VisibilityModel> = + collapsedStatusBarInteractor.visibilityViaDisableFlags.map { + // TODO(b/364360986): Take CollapsedStatusBarFragment.clockHiddenMode into account. + VisibilityModel(it.isClockAllowed.toVisibilityInt(), it.animate) + } + override val isNotificationIconContainerVisible: Flow<VisibilityModel> = + collapsedStatusBarInteractor.visibilityViaDisableFlags.map { + VisibilityModel(it.areNotificationIconsAllowed.toVisibilityInt(), it.animate) + } + override val isSystemInfoVisible: Flow<VisibilityModel> = + collapsedStatusBarInteractor.visibilityViaDisableFlags.map { + VisibilityModel(it.isSystemInfoAllowed.toVisibilityInt(), it.animate) + } + + @View.Visibility + private fun Boolean.toVisibilityInt(): Int { + return if (this) View.VISIBLE else View.GONE + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt index d2dfc9257e7e..907c68440b55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt @@ -17,16 +17,19 @@ package com.android.systemui.statusbar.disableflags.data.repository import android.app.StatusBarManager.DISABLE2_NONE import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS +import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS import android.app.StatusBarManager.DISABLE_CLOCK import android.app.StatusBarManager.DISABLE_NONE import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS +import android.app.StatusBarManager.DISABLE_SYSTEM_INFO import android.content.res.Configuration import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.log.LogBufferFactory +import com.android.systemui.res.R import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.disableflags.DisableFlagsLogger import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel @@ -82,7 +85,7 @@ class DisableFlagsRepositoryTest : SysuiTestCase() { @Test fun disableFlags_initialValue_none() { assertThat(underTest.disableFlags.value) - .isEqualTo(DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)) + .isEqualTo(DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = false)) } @Test @@ -182,12 +185,7 @@ class DisableFlagsRepositoryTest : SysuiTestCase() { fun disableFlags_quickSettingsDisabled_quickSettingsEnabledFalse() = testScope.runTest { getCommandQueueCallback() - .disable( - DISPLAY_ID, - DISABLE_NONE, - DISABLE2_QUICK_SETTINGS, - /* animate= */ false, - ) + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_QUICK_SETTINGS, /* animate= */ false) assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse() } @@ -217,21 +215,84 @@ class DisableFlagsRepositoryTest : SysuiTestCase() { configuration.orientation = Configuration.ORIENTATION_LANDSCAPE mContext.orCreateTestableResources.addOverride( R.bool.config_use_split_notification_shade, - /* value= */ false + /* value= */ false, ) remoteInputQuickSettingsDisabler.setRemoteInputActive(true) remoteInputQuickSettingsDisabler.onConfigChanged(configuration) getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false) + + // THEN quick settings is disabled (even if the disable flags don't say so) + assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse() + } + + @Test + fun disableFlags_clockDisabled() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_CLOCK, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.isClockEnabled).isFalse() + } + + @Test + fun disableFlags_clockEnabled() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.isClockEnabled).isTrue() + } + + @Test + fun disableFlags_notificationIconsDisabled() = + testScope.runTest { + getCommandQueueCallback() .disable( DISPLAY_ID, - DISABLE_NONE, + DISABLE_NOTIFICATION_ICONS, DISABLE2_NONE, /* animate= */ false, ) - // THEN quick settings is disabled (even if the disable flags don't say so) - assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse() + assertThat(underTest.disableFlags.value.areNotificationIconsEnabled).isFalse() + } + + @Test + fun disableFlags_notificationIconsEnabled() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.areNotificationIconsEnabled).isTrue() + } + + @Test + fun disableFlags_systemInfoDisabled_viaDisable1() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_SYSTEM_INFO, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isFalse() + } + + @Test + fun disableFlags_systemInfoDisabled_viaDisable2() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_SYSTEM_ICONS, /* animate= */ false) + + assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isFalse() + } + + @Test + fun disableFlags_systemInfoEnabled() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isTrue() } @Test @@ -267,6 +328,34 @@ class DisableFlagsRepositoryTest : SysuiTestCase() { assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse() } + @Test + fun disableFlags_animateFalse() = + testScope.runTest { + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ false, + ) + + assertThat(underTest.disableFlags.value.animate).isFalse() + } + + @Test + fun disableFlags_animateTrue() = + testScope.runTest { + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ true, + ) + + assertThat(underTest.disableFlags.value.animate).isTrue() + } + private fun getCommandQueueCallback(): CommandQueue.Callbacks { val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>() verify(commandQueue).addCallback(callbackCaptor.capture()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt new file mode 100644 index 000000000000..5036e775211e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorTest.kt @@ -0,0 +1,94 @@ +/* + * 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.domain.interactor + +import android.app.StatusBarManager.DISABLE2_NONE +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 androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.test.runTest + +@SmallTest +class CollapsedStatusBarInteractorTest : SysuiTestCase() { + val kosmos = testKosmos() + val testScope = kosmos.testScope + val disableFlagsRepo = kosmos.fakeDisableFlagsRepository + + val underTest = kosmos.collapsedStatusBarInteractor + + @Test + fun visibilityViaDisableFlags_allDisabled() = + testScope.runTest { + val latest by collectLastValue(underTest.visibilityViaDisableFlags) + + disableFlagsRepo.disableFlags.value = + DisableFlagsModel( + DISABLE_CLOCK or DISABLE_NOTIFICATION_ICONS or DISABLE_SYSTEM_INFO, + DISABLE2_NONE, + animate = false, + ) + + assertThat(latest!!.isClockAllowed).isFalse() + assertThat(latest!!.areNotificationIconsAllowed).isFalse() + assertThat(latest!!.isSystemInfoAllowed).isFalse() + } + + @Test + fun visibilityViaDisableFlags_allEnabled() = + testScope.runTest { + val latest by collectLastValue(underTest.visibilityViaDisableFlags) + + disableFlagsRepo.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = false) + + assertThat(latest!!.isClockAllowed).isTrue() + assertThat(latest!!.areNotificationIconsAllowed).isTrue() + assertThat(latest!!.isSystemInfoAllowed).isTrue() + } + + @Test + fun visibilityViaDisableFlags_animateFalse() = + testScope.runTest { + val latest by collectLastValue(underTest.visibilityViaDisableFlags) + + disableFlagsRepo.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = false) + + assertThat(latest!!.animate).isFalse() + } + + @Test + fun visibilityViaDisableFlags_animateTrue() = + testScope.runTest { + val latest by collectLastValue(underTest.visibilityViaDisableFlags) + + disableFlagsRepo.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = true) + + assertThat(latest!!.animate).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index 7ae6ea51b912..31f06c3f03bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -16,8 +16,14 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import android.app.StatusBarManager.DISABLE2_NONE +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.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags +import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -25,12 +31,10 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope @@ -38,8 +42,6 @@ import com.android.systemui.log.assertLogsWtf import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository import com.android.systemui.scene.data.repository.sceneContainerRepository -import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor -import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository @@ -48,17 +50,16 @@ import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.Me import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip -import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel import com.android.systemui.statusbar.data.model.StatusBarMode import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository import com.android.systemui.statusbar.notification.data.model.activeNotificationModel import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository -import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor -import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow @@ -83,17 +84,9 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository private val activeNotificationListRepository = kosmos.activeNotificationListRepository private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository + private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository - private val underTest = - CollapsedStatusBarViewModelImpl( - kosmos.lightsOutInteractor, - kosmos.activeNotificationsInteractor, - kosmos.keyguardTransitionInteractor, - kosmos.sceneInteractor, - kosmos.sceneContainerOcclusionInteractor, - kosmos.ongoingActivityChipsViewModel, - kosmos.applicationCoroutineScope, - ) + private val underTest = kosmos.collapsedStatusBarViewModel @Before fun setUp() { @@ -495,14 +488,77 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertThat(latest).isTrue() } + @Test + fun isClockVisible_allowedByDisableFlags_visible() = + testScope.runTest { + val latest by collectLastValue(underTest.isClockVisible) + + disableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) + + assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) + } + + @Test + fun isClockVisible_notAllowedByDisableFlags_gone() = + testScope.runTest { + val latest by collectLastValue(underTest.isClockVisible) + + disableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_CLOCK, DISABLE2_NONE) + + assertThat(latest!!.visibility).isEqualTo(View.GONE) + } + + @Test + fun isNotificationIconContainerVisible_allowedByDisableFlags_visible() = + testScope.runTest { + val latest by collectLastValue(underTest.isNotificationIconContainerVisible) + + disableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) + + assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) + } + + @Test + fun isNotificationIconContainerVisible_notAllowedByDisableFlags_gone() = + testScope.runTest { + val latest by collectLastValue(underTest.isNotificationIconContainerVisible) + + disableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NOTIFICATION_ICONS, DISABLE2_NONE) + + assertThat(latest!!.visibility).isEqualTo(View.GONE) + } + + @Test + fun isSystemInfoVisible_allowedByDisableFlags_visible() = + testScope.runTest { + val latest by collectLastValue(underTest.isSystemInfoVisible) + + disableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) + + assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) + } + + @Test + fun isSystemInfoVisible_notAllowedByDisableFlags_gone() = + testScope.runTest { + val latest by collectLastValue(underTest.isSystemInfoVisible) + + disableFlagsRepository.disableFlags.value = + DisableFlagsModel(DISABLE_SYSTEM_INFO, DISABLE2_NONE) + + assertThat(latest!!.visibility).isEqualTo(View.GONE) + } + private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = ActiveNotificationsStore.Builder() .apply { notifications.forEach(::addIndividualNotif) } .build() private val testNotifications = - listOf( - activeNotificationModel(key = "notif1"), - activeNotificationModel(key = "notif2"), - ) + listOf(activeNotificationModel(key = "notif1"), activeNotificationModel(key = "notif2")) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt index 4834d367d4be..cc90c1167ef1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import android.view.View import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import kotlinx.coroutines.flow.Flow @@ -36,9 +37,29 @@ class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { override val isHomeStatusBarAllowedByScene = MutableStateFlow(false) - override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut + override val isClockVisible = + MutableStateFlow( + CollapsedStatusBarViewModel.VisibilityModel( + visibility = View.GONE, + shouldAnimateChange = false, + ) + ) + + override val isNotificationIconContainerVisible = + MutableStateFlow( + CollapsedStatusBarViewModel.VisibilityModel( + visibility = View.GONE, + shouldAnimateChange = false, + ) + ) - fun setNotificationLightsOut(lightsOut: Boolean) { - areNotificationLightsOut.value = lightsOut - } + override val isSystemInfoVisible = + MutableStateFlow( + CollapsedStatusBarViewModel.VisibilityModel( + visibility = View.GONE, + shouldAnimateChange = false, + ) + ) + + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut } 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 new file mode 100644 index 000000000000..385a813996ff --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/domain/interactor/CollapsedStatusBarInteractorKosmos.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.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository + +val Kosmos.collapsedStatusBarInteractor: CollapsedStatusBarInteractor by + Kosmos.Fixture { CollapsedStatusBarInteractor(fakeDisableFlagsRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt new file mode 100644 index 000000000000..f47aa2945319 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelKosmos.kt @@ -0,0 +1,41 @@ +/* + * 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.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.phone.domain.interactor.lightsOutInteractor +import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor + +val Kosmos.collapsedStatusBarViewModel: CollapsedStatusBarViewModel by + Kosmos.Fixture { + CollapsedStatusBarViewModelImpl( + collapsedStatusBarInteractor, + lightsOutInteractor, + activeNotificationsInteractor, + keyguardTransitionInteractor, + sceneInteractor, + sceneContainerOcclusionInteractor, + ongoingActivityChipsViewModel, + applicationCoroutineScope, + ) + } |