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