1 /* <lambda>null2 * Copyright (C) 2025 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 package com.android.systemui.scene.ui.view 18 19 import android.view.View 20 import androidx.compose.runtime.getValue 21 import com.android.compose.animation.scene.ContentKey 22 import com.android.internal.jank.Cuj 23 import com.android.internal.jank.Cuj.CujType 24 import com.android.internal.jank.InteractionJankMonitor 25 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor 26 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel 27 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor 28 import com.android.systemui.lifecycle.ExclusiveActivatable 29 import com.android.systemui.lifecycle.Hydrator 30 import com.android.systemui.scene.shared.model.Overlays 31 import dagger.assisted.AssistedFactory 32 import dagger.assisted.AssistedInject 33 34 /** 35 * Monitors scene transitions and reports the beginning and ending of each scene-related CUJ. 36 * 37 * This general-purpose monitor can be expanded to include other rules that respond to the beginning 38 * and/or ending of transitions and reports jank CUI markers to the [InteractionJankMonitor]. 39 */ 40 class SceneJankMonitor 41 @AssistedInject 42 constructor( 43 authenticationInteractor: AuthenticationInteractor, 44 private val deviceUnlockedInteractor: DeviceUnlockedInteractor, 45 private val interactionJankMonitor: InteractionJankMonitor, 46 ) : ExclusiveActivatable() { 47 48 private val hydrator = Hydrator("SceneJankMonitor.hydrator") 49 private val authMethod: AuthenticationMethodModel? by 50 hydrator.hydratedStateOf( 51 traceName = "authMethod", 52 initialValue = null, 53 source = authenticationInteractor.authenticationMethod, 54 ) 55 56 override suspend fun onActivated(): Nothing { 57 hydrator.activate() 58 } 59 60 /** 61 * Notifies that a transition is at its start. 62 * 63 * Should be called exactly once each time a new transition starts. 64 */ 65 fun onTransitionStart(view: View, from: ContentKey, to: ContentKey, @CujType cuj: Int?) { 66 cuj.orCalculated(from, to) { nonNullCuj -> interactionJankMonitor.begin(view, nonNullCuj) } 67 } 68 69 /** 70 * Notifies that the previous transition is at its end. 71 * 72 * Should be called exactly once each time a transition ends. 73 */ 74 fun onTransitionEnd(from: ContentKey, to: ContentKey, @CujType cuj: Int?) { 75 cuj.orCalculated(from, to) { nonNullCuj -> interactionJankMonitor.end(nonNullCuj) } 76 } 77 78 /** 79 * Returns this CUI marker (CUJ identifier), one that's calculated based on other state, or 80 * `null`, if no appropriate CUJ could be calculated. 81 */ 82 private fun Int?.orCalculated( 83 from: ContentKey, 84 to: ContentKey, 85 ifNotNull: (nonNullCuj: Int) -> Unit, 86 ) { 87 val thisOrCalculatedCuj = this ?: calculatedCuj(from = from, to = to) 88 89 if (thisOrCalculatedCuj != null) { 90 ifNotNull(thisOrCalculatedCuj) 91 } 92 } 93 94 @CujType 95 private fun calculatedCuj(from: ContentKey, to: ContentKey): Int? { 96 val isDeviceUnlocked = deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked 97 return when { 98 to == Overlays.Bouncer -> 99 when (authMethod) { 100 is AuthenticationMethodModel.Pin, 101 is AuthenticationMethodModel.Sim -> Cuj.CUJ_LOCKSCREEN_PIN_APPEAR 102 is AuthenticationMethodModel.Pattern -> Cuj.CUJ_LOCKSCREEN_PATTERN_APPEAR 103 is AuthenticationMethodModel.Password -> Cuj.CUJ_LOCKSCREEN_PASSWORD_APPEAR 104 is AuthenticationMethodModel.None -> null 105 null -> null 106 } 107 from == Overlays.Bouncer && isDeviceUnlocked -> 108 when (authMethod) { 109 is AuthenticationMethodModel.Pin, 110 is AuthenticationMethodModel.Sim -> Cuj.CUJ_LOCKSCREEN_PIN_DISAPPEAR 111 is AuthenticationMethodModel.Pattern -> Cuj.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR 112 is AuthenticationMethodModel.Password -> Cuj.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR 113 is AuthenticationMethodModel.None -> null 114 null -> null 115 } 116 else -> null 117 } 118 } 119 120 @AssistedFactory 121 interface Factory { 122 fun create(): SceneJankMonitor 123 } 124 } 125