1 /* <lambda>null2 * Copyright (C) 2022 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 @file:OptIn(ExperimentalCoroutinesApi::class) 18 19 package com.android.systemui.keyguard.data.repository 20 21 import android.content.Context 22 import android.graphics.Point 23 import com.android.systemui.R 24 import com.android.systemui.dagger.SysUISingleton 25 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel 26 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource 27 import com.android.systemui.keyguard.shared.model.WakeSleepReason 28 import com.android.systemui.statusbar.CircleReveal 29 import com.android.systemui.statusbar.LiftReveal 30 import com.android.systemui.statusbar.LightRevealEffect 31 import com.android.systemui.statusbar.PowerButtonReveal 32 import javax.inject.Inject 33 import kotlin.math.max 34 import kotlinx.coroutines.ExperimentalCoroutinesApi 35 import kotlinx.coroutines.flow.Flow 36 import kotlinx.coroutines.flow.combine 37 import kotlinx.coroutines.flow.distinctUntilChanged 38 import kotlinx.coroutines.flow.flatMapLatest 39 import kotlinx.coroutines.flow.flowOf 40 import kotlinx.coroutines.flow.map 41 42 val DEFAULT_REVEAL_EFFECT = LiftReveal 43 44 /** 45 * Encapsulates state relevant to the light reveal scrim, the view used to reveal/hide screen 46 * contents during transitions between AOD and lockscreen/unlocked. 47 */ 48 interface LightRevealScrimRepository { 49 50 /** 51 * The reveal effect that should be used for the next lock/unlock. We switch between either the 52 * biometric unlock effect (if wake and unlocking) or the non-biometric effect, and position it 53 * at the current screen position of the appropriate sensor. 54 */ 55 val revealEffect: Flow<LightRevealEffect> 56 } 57 58 @SysUISingleton 59 class LightRevealScrimRepositoryImpl 60 @Inject 61 constructor( 62 keyguardRepository: KeyguardRepository, 63 val context: Context, 64 ) : LightRevealScrimRepository { 65 66 /** The reveal effect used if the device was locked/unlocked via the power button. */ 67 private val powerButtonReveal = 68 PowerButtonReveal( 69 context.resources 70 .getDimensionPixelSize(R.dimen.physical_power_button_center_screen_location_y) 71 .toFloat() 72 ) 73 74 /** 75 * Reveal effect to use for a fingerprint unlock. This is reconstructed if the fingerprint 76 * sensor location on the screen (in pixels) changes due to configuration changes. 77 */ 78 private val fingerprintRevealEffect: Flow<LightRevealEffect?> = <lambda>null79 keyguardRepository.fingerprintSensorLocation.map { 80 it?.let { constructCircleRevealFromPoint(it) } 81 } 82 83 /** 84 * Reveal effect to use for a face unlock. This is reconstructed if the face sensor/front camera 85 * location on the screen (in pixels) changes due to configuration changes. 86 */ 87 private val faceRevealEffect: Flow<LightRevealEffect?> = <lambda>null88 keyguardRepository.faceSensorLocation.map { it?.let { constructCircleRevealFromPoint(it) } } 89 90 /** 91 * The reveal effect we'll use for the next biometric unlock animation. We switch between the 92 * fingerprint/face unlock effect flows depending on the biometric unlock source. 93 */ 94 private val biometricRevealEffect: Flow<LightRevealEffect?> = sourcenull95 keyguardRepository.biometricUnlockSource.flatMapLatest { source -> 96 when (source) { 97 BiometricUnlockSource.FINGERPRINT_SENSOR -> fingerprintRevealEffect 98 BiometricUnlockSource.FACE_SENSOR -> faceRevealEffect 99 else -> flowOf(null) 100 } 101 } 102 103 /** The reveal effect we'll use for the next non-biometric unlock (tap, power button, etc). */ 104 private val nonBiometricRevealEffect: Flow<LightRevealEffect?> = wakefulnessModelnull105 keyguardRepository.wakefulness.map { wakefulnessModel -> 106 val wakingUpFromPowerButton = 107 wakefulnessModel.isWakingUpOrAwake && 108 wakefulnessModel.lastWakeReason == WakeSleepReason.POWER_BUTTON 109 val sleepingFromPowerButton = 110 !wakefulnessModel.isWakingUpOrAwake && 111 wakefulnessModel.lastSleepReason == WakeSleepReason.POWER_BUTTON 112 113 if (wakingUpFromPowerButton || sleepingFromPowerButton) { 114 powerButtonReveal 115 } else { 116 LiftReveal 117 } 118 } 119 120 override val revealEffect = 121 combine( 122 keyguardRepository.biometricUnlockState, 123 biometricRevealEffect, 124 nonBiometricRevealEffect biometricRevealnull125 ) { biometricUnlockState, biometricReveal, nonBiometricReveal -> 126 127 // Use the biometric reveal for any flavor of wake and unlocking. 128 when (biometricUnlockState) { 129 BiometricUnlockModel.WAKE_AND_UNLOCK, 130 BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING, 131 BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal 132 else -> nonBiometricReveal 133 } 134 ?: DEFAULT_REVEAL_EFFECT 135 } 136 .distinctUntilChanged() 137 constructCircleRevealFromPointnull138 private fun constructCircleRevealFromPoint(point: Point): LightRevealEffect { 139 return with(point) { 140 CircleReveal( 141 x, 142 y, 143 startRadius = 0, 144 endRadius = 145 max(max(x, context.display.width - x), max(y, context.display.height - y)), 146 ) 147 } 148 } 149 } 150