1 /*
2  * 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 package com.android.systemui.statusbar
18 
19 import android.content.Context
20 import android.util.IndentingPrintWriter
21 import android.util.MathUtils
22 import androidx.annotation.FloatRange
23 import androidx.annotation.Px
24 import com.android.systemui.R
25 import com.android.systemui.dump.DumpManager
26 import com.android.systemui.plugins.qs.QS
27 import com.android.systemui.statusbar.policy.ConfigurationController
28 import dagger.assisted.Assisted
29 import dagger.assisted.AssistedFactory
30 import dagger.assisted.AssistedInject
31 import kotlin.math.max
32 
33 /** Responsible for the QS components during the lockscreen shade transition. */
34 class LockscreenShadeQsTransitionController
35 @AssistedInject
36 constructor(
37     context: Context,
38     configurationController: ConfigurationController,
39     dumpManager: DumpManager,
40     @Assisted private val qsProvider: () -> QS,
41 ) : AbstractLockscreenShadeTransitionController(context, configurationController, dumpManager) {
42 
43     private val qs: QS
44         get() = qsProvider()
45 
46     /**
47      * The progress fraction of the QS transition during lockscreen shade expansion.
48      *
49      * Note that this value might be 0 for some time when the expansion is already in progress,
50      * since there is a transition start delay for QS on some device configurations. For this
51      * reason, don't use this value to check whether the shade expansion is in progress.
52      */
53     @FloatRange(from = 0.0, to = 1.0)
54     var qsTransitionFraction = 0f
55         private set
56 
57     /**
58      * The fraction of the QS "squish" transition progress during lockscreen shade expansion.
59      *
60      * Note that in some device configurations (large screens) this value can start at a value
61      * greater than 0. For this reason don't use this value to check whether the QS transition has
62      * started or not.
63      */
64     @FloatRange(from = 0.0, to = 1.0)
65     var qsSquishTransitionFraction = 0f
66         private set
67 
68     /**
69      * The drag down amount, in pixels __for the QS transition__, also taking into account the
70      * [qsTransitionStartDelay].
71      *
72      * Since it takes into account the start delay, it is __not__ the same as the raw drag down
73      * amount from the shade expansion.
74      */
75     @Px private var qsDragDownAmount = 0f
76 
77     /**
78      * Distance it takes for the QS transition to complete during the lockscreen shade expansion.
79      */
80     @Px private var qsTransitionDistance = 0
81 
82     /** Distance delay for the QS transition to start during the lockscreen shade expansion. */
83     @Px private var qsTransitionStartDelay = 0
84 
85     /**
86      * Distance that it takes to complete the QS "squish" transition during the lockscreen shade
87      * expansion.
88      */
89     @Px private var qsSquishTransitionDistance = 0
90 
91     /**
92      * Whether the transition to full shade is in progress. Might be `true` even though
93      * [qsTransitionFraction] is still 0, due to [qsTransitionStartDelay].
94      */
95     private var isTransitioningToFullShade = false
96 
97     /**
98      * The fraction at which the QS "squish" transition should start during the lockscreen shade
99      * expansion.
100      *
101      * 0 is fully collapsed, 1 is fully expanded.
102      */
103     @FloatRange(from = 0.0, to = 1.0) private var qsSquishStartFraction = 0f
104 
105     override fun updateResources() {
106         qsTransitionDistance =
107             context.resources.getDimensionPixelSize(R.dimen.lockscreen_shade_qs_transition_distance)
108         qsTransitionStartDelay =
109             context.resources.getDimensionPixelSize(R.dimen.lockscreen_shade_qs_transition_delay)
110         qsSquishTransitionDistance =
111             context.resources.getDimensionPixelSize(
112                 R.dimen.lockscreen_shade_qs_squish_transition_distance
113             )
114         qsSquishStartFraction =
115             context.resources.getFloat(R.dimen.lockscreen_shade_qs_squish_start_fraction)
116 
117         qsSquishTransitionFraction = max(qsSquishTransitionFraction, qsSquishStartFraction)
118     }
119 
120     override fun onDragDownAmountChanged(dragDownAmount: Float) {
121         qsDragDownAmount = dragDownAmount - qsTransitionStartDelay
122         qsTransitionFraction = MathUtils.saturate(qsDragDownAmount / qsTransitionDistance)
123         qsSquishTransitionFraction =
124             MathUtils.lerp(
125                 /* start= */ qsSquishStartFraction,
126                 /* stop= */ 1f,
127                 /* amount= */ MathUtils.saturate(qsDragDownAmount / qsSquishTransitionDistance)
128             )
129         isTransitioningToFullShade = dragDownAmount > 0.0f
130         qs.setTransitionToFullShadeProgress(
131             isTransitioningToFullShade,
132             qsTransitionFraction,
133             qsSquishTransitionFraction
134         )
135     }
136 
137     override fun dump(pw: IndentingPrintWriter) {
138         pw.println(
139             """
140             Resources:
141               qsTransitionDistance: $qsTransitionDistance
142               qsTransitionStartDelay: $qsTransitionStartDelay
143               qsSquishTransitionDistance: $qsSquishTransitionDistance
144               qsSquishStartFraction: $qsSquishStartFraction
145             State:
146               dragDownAmount: $dragDownAmount
147               qsDragDownAmount: $qsDragDownAmount
148               qsDragFraction: $qsTransitionFraction
149               qsSquishFraction: $qsSquishTransitionFraction
150               isTransitioningToFullShade: $isTransitioningToFullShade
151         """
152                 .trimIndent()
153         )
154     }
155 
156     @AssistedFactory
157     fun interface Factory {
158         fun create(qsProvider: () -> QS): LockscreenShadeQsTransitionController
159     }
160 }
161