1 /*
2  * 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 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
18 
19 import com.android.systemui.dagger.SysUISingleton
20 import com.android.systemui.dump.DumpManager
21 import com.android.systemui.flags.FeatureFlagsClassic
22 import com.android.systemui.flags.Flags
23 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
24 import com.android.systemui.scene.shared.flag.SceneContainerFlag
25 import com.android.systemui.shade.domain.interactor.ShadeInteractor
26 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
27 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
28 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
29 import com.android.systemui.util.kotlin.FlowDumperImpl
30 import javax.inject.Inject
31 import kotlinx.coroutines.flow.Flow
32 
33 /**
34  * ViewModel used by the Notification placeholders inside the scene container to update the
35  * [NotificationStackAppearanceInteractor], and by extension control the NSSL.
36  */
37 @SysUISingleton
38 class NotificationsPlaceholderViewModel
39 @Inject
40 constructor(
41     dumpManager: DumpManager,
42     private val interactor: NotificationStackAppearanceInteractor,
43     shadeInteractor: ShadeInteractor,
44     featureFlags: FeatureFlagsClassic,
45     private val keyguardInteractor: KeyguardInteractor,
46 ) : FlowDumperImpl(dumpManager) {
47     /** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
48     val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
49 
50     /** DEBUG: whether the debug logging should be output. */
51     val isDebugLoggingEnabled: Boolean = SceneContainerFlag.isEnabled
52 
53     /** Notifies that the bounds of the notification scrim have changed. */
onScrimBoundsChangednull54     fun onScrimBoundsChanged(bounds: ShadeScrimBounds?) {
55         interactor.setShadeScrimBounds(bounds)
56     }
57 
58     /** Sets the available space */
onConstrainedAvailableSpaceChangednull59     fun onConstrainedAvailableSpaceChanged(height: Int) {
60         interactor.setConstrainedAvailableSpace(height)
61     }
62 
63     /** Sets the content alpha for the current state of the brightness mirror */
setAlphaForBrightnessMirrornull64     fun setAlphaForBrightnessMirror(alpha: Float) {
65         interactor.setAlphaForBrightnessMirror(alpha)
66     }
67 
68     /** Corner rounding of the stack */
69     val shadeScrimRounding: Flow<ShadeScrimRounding> =
70         interactor.shadeScrimRounding.dumpWhileCollecting("shadeScrimRounding")
71 
72     /**
73      * The amount [0-1] that the shade or quick settings has been opened. At 0, the shade is closed;
74      * at 1, either the shade or quick settings is open.
75      */
76     val expandFraction: Flow<Float> = shadeInteractor.anyExpansion.dumpValue("expandFraction")
77 
78     /**
79      * The amount in px that the notification stack should scroll due to internal expansion. This
80      * should only happen when a notification expansion hits the bottom of the screen, so it is
81      * necessary to scroll up to keep expanding the notification.
82      */
83     val syntheticScroll: Flow<Float> =
84         interactor.syntheticScroll.dumpWhileCollecting("syntheticScroll")
85 
86     /**
87      * Whether the current touch gesture is overscroll. If true, it means the NSSL has already
88      * consumed part of the gesture.
89      */
90     val isCurrentGestureOverscroll: Flow<Boolean> =
91         interactor.isCurrentGestureOverscroll.dumpWhileCollecting("isCurrentGestureOverScroll")
92 
93     /** Sets whether the notification stack is scrolled to the top. */
setScrolledToTopnull94     fun setScrolledToTop(scrolledToTop: Boolean) {
95         interactor.setScrolledToTop(scrolledToTop)
96     }
97 }
98 
99 // Expansion fraction thresholds (between 0-1f) at which the corresponding value should be
100 // at its maximum, given they are at their minimum value at expansion = 0f.
101 object NotificationTransitionThresholds {
102     const val EXPANSION_FOR_MAX_CORNER_RADIUS = 0.1f
103     const val EXPANSION_FOR_MAX_SCRIM_ALPHA = 0.3f
104     const val EXPANSION_FOR_DELAYED_STACK_FADE_IN = 0.5f
105 }
106