1 /*
<lambda>null2  *  Copyright (C) 2023 The Android Open Source Project
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *       http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  */
17 
18 package com.android.systemui.keyguard.domain.interactor
19 
20 import android.util.Log
21 import com.android.keyguard.ClockEventController
22 import com.android.keyguard.KeyguardClockSwitch
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.dagger.qualifiers.Application
25 import com.android.systemui.keyguard.data.repository.KeyguardClockRepository
26 import com.android.systemui.keyguard.shared.model.ClockSize
27 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
28 import com.android.systemui.keyguard.shared.model.KeyguardState
29 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
30 import com.android.systemui.plugins.clocks.ClockController
31 import com.android.systemui.plugins.clocks.ClockId
32 import com.android.systemui.scene.shared.flag.SceneContainerFlag
33 import com.android.systemui.shade.domain.interactor.ShadeInteractor
34 import com.android.systemui.shade.shared.model.ShadeMode
35 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
36 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
37 import com.android.systemui.util.kotlin.combine
38 import javax.inject.Inject
39 import kotlinx.coroutines.CoroutineScope
40 import kotlinx.coroutines.flow.Flow
41 import kotlinx.coroutines.flow.SharingStarted
42 import kotlinx.coroutines.flow.StateFlow
43 import kotlinx.coroutines.flow.combine
44 import kotlinx.coroutines.flow.map
45 import kotlinx.coroutines.flow.stateIn
46 
47 private val TAG = KeyguardClockInteractor::class.simpleName
48 /** Manages and encapsulates the clock components of the lockscreen root view. */
49 @SysUISingleton
50 class KeyguardClockInteractor
51 @Inject
52 constructor(
53     mediaCarouselInteractor: MediaCarouselInteractor,
54     activeNotificationsInteractor: ActiveNotificationsInteractor,
55     shadeInteractor: ShadeInteractor,
56     keyguardInteractor: KeyguardInteractor,
57     keyguardTransitionInteractor: KeyguardTransitionInteractor,
58     headsUpNotificationInteractor: HeadsUpNotificationInteractor,
59     @Application private val applicationScope: CoroutineScope,
60     val keyguardClockRepository: KeyguardClockRepository,
61 ) {
62     private val isOnAod: Flow<Boolean> =
63         keyguardTransitionInteractor.currentKeyguardState.map { it == KeyguardState.AOD }
64 
65     val selectedClockSize: StateFlow<ClockSizeSetting> = keyguardClockRepository.selectedClockSize
66 
67     val currentClockId: Flow<ClockId> = keyguardClockRepository.currentClockId
68 
69     val currentClock: StateFlow<ClockController?> = keyguardClockRepository.currentClock
70 
71     val previewClock: Flow<ClockController> = keyguardClockRepository.previewClock
72 
73     val clockEventController: ClockEventController by keyguardClockRepository::clockEventController
74 
75     var clock: ClockController? by keyguardClockRepository.clockEventController::clock
76 
77     val clockSize: StateFlow<ClockSize> =
78         if (SceneContainerFlag.isEnabled) {
79             combine(
80                     shadeInteractor.shadeMode,
81                     activeNotificationsInteractor.areAnyNotificationsPresent,
82                     mediaCarouselInteractor.hasActiveMediaOrRecommendation,
83                     keyguardInteractor.isDozing,
84                     isOnAod,
85                 ) { shadeMode, hasNotifs, hasMedia, isDozing, isOnAod ->
86                     return@combine when {
87                         keyguardClockRepository.shouldForceSmallClock && !isOnAod -> ClockSize.SMALL
88                         shadeMode == ShadeMode.Single && (hasNotifs || hasMedia) -> ClockSize.SMALL
89                         shadeMode == ShadeMode.Single -> ClockSize.LARGE
90                         hasMedia && !isDozing -> ClockSize.SMALL
91                         else -> ClockSize.LARGE
92                     }
93                 }
94                 .stateIn(
95                     scope = applicationScope,
96                     started = SharingStarted.WhileSubscribed(),
97                     initialValue = ClockSize.LARGE
98                 )
99         } else {
100             keyguardClockRepository.clockSize
101         }
102 
103     val clockShouldBeCentered: Flow<Boolean> =
104         if (SceneContainerFlag.isEnabled) {
105             combine(
106                 shadeInteractor.shadeMode,
107                 activeNotificationsInteractor.areAnyNotificationsPresent,
108                 keyguardInteractor.isActiveDreamLockscreenHosted,
109                 isOnAod,
110                 headsUpNotificationInteractor.isHeadsUpOrAnimatingAway,
111                 keyguardInteractor.isDozing,
112             ) {
113                 shadeMode,
114                 areAnyNotificationsPresent,
115                 isActiveDreamLockscreenHosted,
116                 isOnAod,
117                 isHeadsUp,
118                 isDozing ->
119                 when {
120                     shadeMode != ShadeMode.Split -> true
121                     !areAnyNotificationsPresent -> true
122                     isActiveDreamLockscreenHosted -> true
123                     // Pulsing notification appears on the right. Move clock left to avoid overlap.
124                     isHeadsUp && isDozing -> false
125                     else -> isOnAod
126                 }
127             }
128         } else {
129             keyguardInteractor.clockShouldBeCentered
130         }
131 
132     fun setClockSize(@KeyguardClockSwitch.ClockSize size: Int) =
133         setClockSize(ClockSize.fromLegacy(size))
134 
135     fun setClockSize(size: ClockSize) {
136         SceneContainerFlag.assertInLegacyMode()
137         keyguardClockRepository.setClockSize(size)
138     }
139 
140     val renderedClockId: ClockId
141         get() {
142             return clock?.let { clock -> clock.config.id }
143                 ?: run {
144                     Log.e(TAG, "No clock is available")
145                     KeyguardClockSwitch.MISSING_CLOCK_ID
146                 }
147         }
148 
149     fun animateFoldToAod(foldFraction: Float) {
150         clock?.let { clock ->
151             clock.smallClock.animations.fold(foldFraction)
152             clock.largeClock.animations.fold(foldFraction)
153         }
154     }
155 }
156