• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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