Loading packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt 0 → 100644 +233 −0 Original line number Diff line number Diff line /* * 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.keyguard.domain.interactor import android.platform.test.annotations.DisableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardClockSwitch.LARGE import com.android.keyguard.KeyguardClockSwitch.SMALL import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardClockRepository import com.android.systemui.keyguard.data.repository.keyguardRepository 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.testScope import com.android.systemui.media.controls.data.repository.mediaFilterRepository import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class KeyguardClockInteractorTest : SysuiTestCase() { private lateinit var kosmos: Kosmos private lateinit var underTest: KeyguardClockInteractor private lateinit var testScope: TestScope @Before fun setup() { kosmos = testKosmos() testScope = kosmos.testScope underTest = kosmos.keyguardClockInteractor } @Test @DisableFlags(AConfigFlags.FLAG_SCENE_CONTAINER) fun clockSize_sceneContainerFlagOff_basedOnRepository() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.keyguardClockRepository.setClockSize(LARGE) assertThat(value).isEqualTo(LARGE) kosmos.keyguardClockRepository.setClockSize(SMALL) assertThat(value).isEqualTo(SMALL) } @Test @DisableFlags(AConfigFlags.FLAG_SCENE_CONTAINER) fun clockShouldBeCentered_sceneContainerFlagOff_basedOnRepository() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.keyguardInteractor.setClockShouldBeCentered(true) assertThat(value).isEqualTo(true) kosmos.keyguardInteractor.setClockShouldBeCentered(false) assertThat(value).isEqualTo(false) } @Test @EnableSceneContainer fun clockSize_forceSmallClock_SMALL() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.fakeKeyguardClockRepository.setShouldForceSmallClock(true) kosmos.fakeFeatureFlagsClassic.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true) transitionTo(KeyguardState.AOD, KeyguardState.LOCKSCREEN) assertThat(value).isEqualTo(SMALL) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSingle_hasNotifs_SMALL() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.shadeRepository.setShadeMode(ShadeMode.Single) kosmos.activeNotificationListRepository.setActiveNotifs(1) assertThat(value).isEqualTo(SMALL) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSingle_hasMedia_SMALL() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.shadeRepository.setShadeMode(ShadeMode.Single) val userMedia = MediaData().copy(active = true) kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia) assertThat(value).isEqualTo(SMALL) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSplit_isMediaVisible_SMALL() = testScope.runTest { val value by collectLastValue(underTest.clockSize) val userMedia = MediaData().copy(active = true) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia) kosmos.keyguardRepository.setIsDozing(false) assertThat(value).isEqualTo(SMALL) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSplit_noMedia_LARGE() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.keyguardRepository.setIsDozing(false) assertThat(value).isEqualTo(LARGE) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSplit_isDozing_LARGE() = testScope.runTest { val value by collectLastValue(underTest.clockSize) val userMedia = MediaData().copy(active = true) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia) kosmos.keyguardRepository.setIsDozing(true) assertThat(value).isEqualTo(LARGE) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_notSplitMode_true() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Single) assertThat(value).isEqualTo(true) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_noActiveNotifications_true() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(0) assertThat(value).isEqualTo(true) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_isActiveDreamLockscreenHosted_true() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(1) kosmos.keyguardRepository.setIsActiveDreamLockscreenHosted(true) assertThat(value).isEqualTo(true) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_hasPulsingNotifications_false() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(1) kosmos.headsUpNotificationRepository.headsUpAnimatingAway.value = true kosmos.keyguardRepository.setIsDozing(true) assertThat(value).isEqualTo(false) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_onAod_true() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(1) transitionTo(KeyguardState.LOCKSCREEN, KeyguardState.AOD) assertThat(value).isEqualTo(true) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_offAod_false() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(1) transitionTo(KeyguardState.AOD, KeyguardState.LOCKSCREEN) assertThat(value).isEqualTo(false) } private suspend fun transitionTo(from: KeyguardState, to: KeyguardState) { kosmos.fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep(from, to, 0f, TransitionState.STARTED) ) kosmos.fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep(from, to, 0.5f, TransitionState.RUNNING) ) kosmos.fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep(from, to, 1f, TransitionState.FINISHED) ) } } packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt +20 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.data.repository import android.content.Context import android.os.UserHandle import android.provider.Settings import com.android.keyguard.ClockEventController Loading @@ -24,9 +25,12 @@ import com.android.keyguard.KeyguardClockSwitch.LARGE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.SettingsClockSize import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.res.R import com.android.systemui.shared.clocks.ClockRegistry import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SettingsProxyExt.observerFlow Loading @@ -47,7 +51,11 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext interface KeyguardClockRepository { /** clock size determined by notificationPanelViewController, LARGE or SMALL */ /** * clock size determined by notificationPanelViewController, LARGE or SMALL * * @deprecated When scene container flag is on use clockSize from domain level. */ val clockSize: StateFlow<Int> /** clock size selected in picker, DYNAMIC or SMALL */ Loading @@ -61,6 +69,9 @@ interface KeyguardClockRepository { val previewClock: Flow<ClockController> val clockEventController: ClockEventController val shouldForceSmallClock: Boolean fun setClockSize(@ClockSize size: Int) } Loading @@ -73,6 +84,8 @@ constructor( override val clockEventController: ClockEventController, @Background private val backgroundDispatcher: CoroutineDispatcher, @Application private val applicationScope: CoroutineScope, @Application private val applicationContext: Context, private val featureFlags: FeatureFlagsClassic, ) : KeyguardClockRepository { /** Receive SMALL or LARGE clock should be displayed on keyguard. */ Loading Loading @@ -135,6 +148,12 @@ constructor( clockRegistry.createCurrentClock() } override val shouldForceSmallClock: Boolean get() = featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE) && // True on small landscape screens applicationContext.resources.getBoolean(R.bool.force_small_clock_on_lockscreen) private fun getClockSize(): SettingsClockSize { return if ( secureSettings.getIntForUser( Loading packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +5 −1 Original line number Diff line number Diff line Loading @@ -206,7 +206,11 @@ interface KeyguardRepository { ) val keyguardDoneAnimationsFinished: Flow<Unit> /** Receive whether clock should be centered on lockscreen. */ /** * Receive whether clock should be centered on lockscreen. * * @deprecated When scene container flag is on use clockShouldBeCentered from domain level. */ val clockShouldBeCentered: Flow<Boolean> /** Loading packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt +84 −2 Original line number Diff line number Diff line Loading @@ -21,23 +21,48 @@ import android.util.Log import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch import com.android.keyguard.KeyguardClockSwitch.ClockSize import com.android.keyguard.KeyguardClockSwitch.LARGE import com.android.keyguard.KeyguardClockSwitch.SMALL import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardClockRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SettingsClockSize import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor import com.android.systemui.util.kotlin.combine import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn private val TAG = KeyguardClockInteractor::class.simpleName /** Manages and ecapsulates the clock components of the lockscreen root view. */ /** Manages and encapsulates the clock components of the lockscreen root view. */ @SysUISingleton class KeyguardClockInteractor @Inject constructor( mediaCarouselInteractor: MediaCarouselInteractor, activeNotificationsInteractor: ActiveNotificationsInteractor, shadeInteractor: ShadeInteractor, keyguardInteractor: KeyguardInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, headsUpNotificationInteractor: HeadsUpNotificationInteractor, @Application private val applicationScope: CoroutineScope, private val keyguardClockRepository: KeyguardClockRepository, ) { private val isOnAod: Flow<Boolean> = keyguardTransitionInteractor.currentKeyguardState.map { it == KeyguardState.AOD } val selectedClockSize: StateFlow<SettingsClockSize> = keyguardClockRepository.selectedClockSize Loading @@ -51,7 +76,64 @@ constructor( var clock: ClockController? by keyguardClockRepository.clockEventController::clock val clockSize: StateFlow<Int> = keyguardClockRepository.clockSize // TODO (b/333389512): Convert this into a more readable enum. val clockSize: StateFlow<Int> = if (SceneContainerFlag.isEnabled) { combine( shadeInteractor.shadeMode, activeNotificationsInteractor.areAnyNotificationsPresent, mediaCarouselInteractor.hasActiveMediaOrRecommendation, keyguardInteractor.isDozing, isOnAod, ) { shadeMode, hasNotifs, hasMedia, isDozing, isOnAod -> return@combine when { keyguardClockRepository.shouldForceSmallClock && !isOnAod -> SMALL shadeMode == ShadeMode.Single && (hasNotifs || hasMedia) -> SMALL shadeMode == ShadeMode.Single -> LARGE hasMedia && !isDozing -> SMALL else -> LARGE } } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = LARGE ) } else { SceneContainerFlag.assertInLegacyMode() keyguardClockRepository.clockSize } val clockShouldBeCentered: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { combine( shadeInteractor.shadeMode, activeNotificationsInteractor.areAnyNotificationsPresent, keyguardInteractor.isActiveDreamLockscreenHosted, isOnAod, headsUpNotificationInteractor.isHeadsUpOrAnimatingAway, keyguardInteractor.isDozing, ) { shadeMode, areAnyNotificationsPresent, isActiveDreamLockscreenHosted, isOnAod, isHeadsUp, isDozing -> when { shadeMode != ShadeMode.Split -> true !areAnyNotificationsPresent -> true isActiveDreamLockscreenHosted -> true // Pulsing notification appears on the right. Move clock left to avoid overlap. isHeadsUp && isDozing -> false else -> isOnAod } } } else { SceneContainerFlag.assertInLegacyMode() keyguardInteractor.clockShouldBeCentered } fun setClockSize(@ClockSize size: Int) { keyguardClockRepository.setClockSize(size) } Loading packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt +3 −5 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import com.android.systemui.customization.R as customizationR import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.keyguard.shared.model.SettingsClockSize import com.android.systemui.res.R Loading @@ -46,11 +45,10 @@ import kotlinx.coroutines.flow.stateIn class KeyguardClockViewModel @Inject constructor( keyguardInteractor: KeyguardInteractor, private val keyguardClockInteractor: KeyguardClockInteractor, keyguardClockInteractor: KeyguardClockInteractor, @Application private val applicationScope: CoroutineScope, notifsKeyguardInteractor: NotificationsKeyguardInteractor, @VisibleForTesting val shadeInteractor: ShadeInteractor, @get:VisibleForTesting val shadeInteractor: ShadeInteractor, ) { var burnInLayer: Layer? = null val useLargeClock: Boolean Loading Loading @@ -99,7 +97,7 @@ constructor( ) val clockShouldBeCentered: StateFlow<Boolean> = keyguardInteractor.clockShouldBeCentered.stateIn( keyguardClockInteractor.clockShouldBeCentered.stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = false Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt 0 → 100644 +233 −0 Original line number Diff line number Diff line /* * 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.keyguard.domain.interactor import android.platform.test.annotations.DisableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardClockSwitch.LARGE import com.android.keyguard.KeyguardClockSwitch.SMALL import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardClockRepository import com.android.systemui.keyguard.data.repository.keyguardRepository 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.testScope import com.android.systemui.media.controls.data.repository.mediaFilterRepository import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class KeyguardClockInteractorTest : SysuiTestCase() { private lateinit var kosmos: Kosmos private lateinit var underTest: KeyguardClockInteractor private lateinit var testScope: TestScope @Before fun setup() { kosmos = testKosmos() testScope = kosmos.testScope underTest = kosmos.keyguardClockInteractor } @Test @DisableFlags(AConfigFlags.FLAG_SCENE_CONTAINER) fun clockSize_sceneContainerFlagOff_basedOnRepository() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.keyguardClockRepository.setClockSize(LARGE) assertThat(value).isEqualTo(LARGE) kosmos.keyguardClockRepository.setClockSize(SMALL) assertThat(value).isEqualTo(SMALL) } @Test @DisableFlags(AConfigFlags.FLAG_SCENE_CONTAINER) fun clockShouldBeCentered_sceneContainerFlagOff_basedOnRepository() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.keyguardInteractor.setClockShouldBeCentered(true) assertThat(value).isEqualTo(true) kosmos.keyguardInteractor.setClockShouldBeCentered(false) assertThat(value).isEqualTo(false) } @Test @EnableSceneContainer fun clockSize_forceSmallClock_SMALL() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.fakeKeyguardClockRepository.setShouldForceSmallClock(true) kosmos.fakeFeatureFlagsClassic.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true) transitionTo(KeyguardState.AOD, KeyguardState.LOCKSCREEN) assertThat(value).isEqualTo(SMALL) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSingle_hasNotifs_SMALL() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.shadeRepository.setShadeMode(ShadeMode.Single) kosmos.activeNotificationListRepository.setActiveNotifs(1) assertThat(value).isEqualTo(SMALL) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSingle_hasMedia_SMALL() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.shadeRepository.setShadeMode(ShadeMode.Single) val userMedia = MediaData().copy(active = true) kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia) assertThat(value).isEqualTo(SMALL) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSplit_isMediaVisible_SMALL() = testScope.runTest { val value by collectLastValue(underTest.clockSize) val userMedia = MediaData().copy(active = true) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia) kosmos.keyguardRepository.setIsDozing(false) assertThat(value).isEqualTo(SMALL) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSplit_noMedia_LARGE() = testScope.runTest { val value by collectLastValue(underTest.clockSize) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.keyguardRepository.setIsDozing(false) assertThat(value).isEqualTo(LARGE) } @Test @EnableSceneContainer fun clockSize_SceneContainerFlagOn_shadeModeSplit_isDozing_LARGE() = testScope.runTest { val value by collectLastValue(underTest.clockSize) val userMedia = MediaData().copy(active = true) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia) kosmos.keyguardRepository.setIsDozing(true) assertThat(value).isEqualTo(LARGE) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_notSplitMode_true() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Single) assertThat(value).isEqualTo(true) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_noActiveNotifications_true() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(0) assertThat(value).isEqualTo(true) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_isActiveDreamLockscreenHosted_true() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(1) kosmos.keyguardRepository.setIsActiveDreamLockscreenHosted(true) assertThat(value).isEqualTo(true) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_hasPulsingNotifications_false() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(1) kosmos.headsUpNotificationRepository.headsUpAnimatingAway.value = true kosmos.keyguardRepository.setIsDozing(true) assertThat(value).isEqualTo(false) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_onAod_true() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(1) transitionTo(KeyguardState.LOCKSCREEN, KeyguardState.AOD) assertThat(value).isEqualTo(true) } @Test @EnableSceneContainer fun clockShouldBeCentered_sceneContainerFlagOn_splitMode_offAod_false() = testScope.runTest { val value by collectLastValue(underTest.clockShouldBeCentered) kosmos.shadeRepository.setShadeMode(ShadeMode.Split) kosmos.activeNotificationListRepository.setActiveNotifs(1) transitionTo(KeyguardState.AOD, KeyguardState.LOCKSCREEN) assertThat(value).isEqualTo(false) } private suspend fun transitionTo(from: KeyguardState, to: KeyguardState) { kosmos.fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep(from, to, 0f, TransitionState.STARTED) ) kosmos.fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep(from, to, 0.5f, TransitionState.RUNNING) ) kosmos.fakeKeyguardTransitionRepository.sendTransitionStep( TransitionStep(from, to, 1f, TransitionState.FINISHED) ) } }
packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt +20 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.data.repository import android.content.Context import android.os.UserHandle import android.provider.Settings import com.android.keyguard.ClockEventController Loading @@ -24,9 +25,12 @@ import com.android.keyguard.KeyguardClockSwitch.LARGE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.SettingsClockSize import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.res.R import com.android.systemui.shared.clocks.ClockRegistry import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SettingsProxyExt.observerFlow Loading @@ -47,7 +51,11 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext interface KeyguardClockRepository { /** clock size determined by notificationPanelViewController, LARGE or SMALL */ /** * clock size determined by notificationPanelViewController, LARGE or SMALL * * @deprecated When scene container flag is on use clockSize from domain level. */ val clockSize: StateFlow<Int> /** clock size selected in picker, DYNAMIC or SMALL */ Loading @@ -61,6 +69,9 @@ interface KeyguardClockRepository { val previewClock: Flow<ClockController> val clockEventController: ClockEventController val shouldForceSmallClock: Boolean fun setClockSize(@ClockSize size: Int) } Loading @@ -73,6 +84,8 @@ constructor( override val clockEventController: ClockEventController, @Background private val backgroundDispatcher: CoroutineDispatcher, @Application private val applicationScope: CoroutineScope, @Application private val applicationContext: Context, private val featureFlags: FeatureFlagsClassic, ) : KeyguardClockRepository { /** Receive SMALL or LARGE clock should be displayed on keyguard. */ Loading Loading @@ -135,6 +148,12 @@ constructor( clockRegistry.createCurrentClock() } override val shouldForceSmallClock: Boolean get() = featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE) && // True on small landscape screens applicationContext.resources.getBoolean(R.bool.force_small_clock_on_lockscreen) private fun getClockSize(): SettingsClockSize { return if ( secureSettings.getIntForUser( Loading
packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +5 −1 Original line number Diff line number Diff line Loading @@ -206,7 +206,11 @@ interface KeyguardRepository { ) val keyguardDoneAnimationsFinished: Flow<Unit> /** Receive whether clock should be centered on lockscreen. */ /** * Receive whether clock should be centered on lockscreen. * * @deprecated When scene container flag is on use clockShouldBeCentered from domain level. */ val clockShouldBeCentered: Flow<Boolean> /** Loading
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt +84 −2 Original line number Diff line number Diff line Loading @@ -21,23 +21,48 @@ import android.util.Log import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch import com.android.keyguard.KeyguardClockSwitch.ClockSize import com.android.keyguard.KeyguardClockSwitch.LARGE import com.android.keyguard.KeyguardClockSwitch.SMALL import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardClockRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SettingsClockSize import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockId import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor import com.android.systemui.util.kotlin.combine import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn private val TAG = KeyguardClockInteractor::class.simpleName /** Manages and ecapsulates the clock components of the lockscreen root view. */ /** Manages and encapsulates the clock components of the lockscreen root view. */ @SysUISingleton class KeyguardClockInteractor @Inject constructor( mediaCarouselInteractor: MediaCarouselInteractor, activeNotificationsInteractor: ActiveNotificationsInteractor, shadeInteractor: ShadeInteractor, keyguardInteractor: KeyguardInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, headsUpNotificationInteractor: HeadsUpNotificationInteractor, @Application private val applicationScope: CoroutineScope, private val keyguardClockRepository: KeyguardClockRepository, ) { private val isOnAod: Flow<Boolean> = keyguardTransitionInteractor.currentKeyguardState.map { it == KeyguardState.AOD } val selectedClockSize: StateFlow<SettingsClockSize> = keyguardClockRepository.selectedClockSize Loading @@ -51,7 +76,64 @@ constructor( var clock: ClockController? by keyguardClockRepository.clockEventController::clock val clockSize: StateFlow<Int> = keyguardClockRepository.clockSize // TODO (b/333389512): Convert this into a more readable enum. val clockSize: StateFlow<Int> = if (SceneContainerFlag.isEnabled) { combine( shadeInteractor.shadeMode, activeNotificationsInteractor.areAnyNotificationsPresent, mediaCarouselInteractor.hasActiveMediaOrRecommendation, keyguardInteractor.isDozing, isOnAod, ) { shadeMode, hasNotifs, hasMedia, isDozing, isOnAod -> return@combine when { keyguardClockRepository.shouldForceSmallClock && !isOnAod -> SMALL shadeMode == ShadeMode.Single && (hasNotifs || hasMedia) -> SMALL shadeMode == ShadeMode.Single -> LARGE hasMedia && !isDozing -> SMALL else -> LARGE } } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = LARGE ) } else { SceneContainerFlag.assertInLegacyMode() keyguardClockRepository.clockSize } val clockShouldBeCentered: Flow<Boolean> = if (SceneContainerFlag.isEnabled) { combine( shadeInteractor.shadeMode, activeNotificationsInteractor.areAnyNotificationsPresent, keyguardInteractor.isActiveDreamLockscreenHosted, isOnAod, headsUpNotificationInteractor.isHeadsUpOrAnimatingAway, keyguardInteractor.isDozing, ) { shadeMode, areAnyNotificationsPresent, isActiveDreamLockscreenHosted, isOnAod, isHeadsUp, isDozing -> when { shadeMode != ShadeMode.Split -> true !areAnyNotificationsPresent -> true isActiveDreamLockscreenHosted -> true // Pulsing notification appears on the right. Move clock left to avoid overlap. isHeadsUp && isDozing -> false else -> isOnAod } } } else { SceneContainerFlag.assertInLegacyMode() keyguardInteractor.clockShouldBeCentered } fun setClockSize(@ClockSize size: Int) { keyguardClockRepository.setClockSize(size) } Loading
packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt +3 −5 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import com.android.systemui.customization.R as customizationR import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.keyguard.shared.model.SettingsClockSize import com.android.systemui.res.R Loading @@ -46,11 +45,10 @@ import kotlinx.coroutines.flow.stateIn class KeyguardClockViewModel @Inject constructor( keyguardInteractor: KeyguardInteractor, private val keyguardClockInteractor: KeyguardClockInteractor, keyguardClockInteractor: KeyguardClockInteractor, @Application private val applicationScope: CoroutineScope, notifsKeyguardInteractor: NotificationsKeyguardInteractor, @VisibleForTesting val shadeInteractor: ShadeInteractor, @get:VisibleForTesting val shadeInteractor: ShadeInteractor, ) { var burnInLayer: Layer? = null val useLargeClock: Boolean Loading Loading @@ -99,7 +97,7 @@ constructor( ) val clockShouldBeCentered: StateFlow<Boolean> = keyguardInteractor.clockShouldBeCentered.stateIn( keyguardClockInteractor.clockShouldBeCentered.stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = false Loading