<lambda>null1 package com.android.systemui.statusbar
2 
3 import android.animation.Animator
4 import android.animation.ValueAnimator
5 import android.content.Context
6 import android.content.res.Configuration
7 import android.util.MathUtils
8 import android.view.animation.PathInterpolator
9 import com.android.internal.annotations.VisibleForTesting
10 import com.android.systemui.res.R
11 import com.android.app.animation.Interpolators
12 import com.android.systemui.dump.DumpManager
13 import com.android.systemui.plugins.qs.QS
14 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
15 import com.android.systemui.statusbar.phone.ScrimController
16 import com.android.systemui.statusbar.policy.ConfigurationController
17 import dagger.assisted.Assisted
18 import dagger.assisted.AssistedFactory
19 import dagger.assisted.AssistedInject
20 import java.io.PrintWriter
21 
22 class SplitShadeLockScreenOverScroller
23 @AssistedInject
24 constructor(
25     configurationController: ConfigurationController,
26     dumpManager: DumpManager,
27     private val context: Context,
28     private val scrimController: ScrimController,
29     private val statusBarStateController: SysuiStatusBarStateController,
30     @Assisted private val qSProvider: () -> QS?,
31     @Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController
32 ) : LockScreenShadeOverScroller {
33 
34     private var releaseOverScrollAnimator: Animator? = null
35     private var transitionToFullShadeDistance = 0
36     private var releaseOverScrollDuration = 0L
37     private var maxOverScrollAmount = 0
38     private var previousOverscrollAmount = 0
39 
40     private val qS: QS?
41         get() = qSProvider()
42 
43     private val nsslController: NotificationStackScrollLayoutController
44         get() = nsslControllerProvider()
45 
46     init {
47         updateResources()
48         configurationController.addCallback(
49             object : ConfigurationController.ConfigurationListener {
50                 override fun onConfigChanged(newConfig: Configuration?) {
51                     updateResources()
52                 }
53             })
54         dumpManager.registerCriticalDumpable("SplitShadeLockscreenOverScroller") { pw, _ ->
55             dump(pw)
56         }
57     }
58 
59     private fun updateResources() {
60         val resources = context.resources
61         transitionToFullShadeDistance =
62             resources.getDimensionPixelSize(R.dimen.lockscreen_shade_full_transition_distance)
63         maxOverScrollAmount =
64             resources.getDimensionPixelSize(R.dimen.lockscreen_shade_max_over_scroll_amount)
65         releaseOverScrollDuration =
66             resources.getInteger(R.integer.lockscreen_shade_over_scroll_release_duration).toLong()
67     }
68 
69     override var expansionDragDownAmount: Float = 0f
70         set(dragDownAmount) {
71             if (field == dragDownAmount) {
72                 return
73             }
74             field = dragDownAmount
75             if (shouldOverscroll()) {
76                 overScroll(dragDownAmount)
77             } else if (shouldReleaseOverscroll()) {
78                 releaseOverScroll()
79             }
80         }
81 
82     private fun shouldOverscroll() = statusBarStateController.state == StatusBarState.KEYGUARD
83 
84     private fun shouldReleaseOverscroll() = !shouldOverscroll() && previousOverscrollAmount != 0
85 
86     private fun overScroll(dragDownAmount: Float) {
87         val overscrollAmount: Int = calculateOverscrollAmount(dragDownAmount)
88         applyOverscroll(overscrollAmount)
89         previousOverscrollAmount = overscrollAmount
90     }
91 
92     private fun applyOverscroll(overscrollAmount: Int) {
93         qS?.setOverScrollAmount(overscrollAmount)
94         scrimController.setNotificationsOverScrollAmount(overscrollAmount)
95         nsslController.setOverScrollAmount(overscrollAmount)
96     }
97 
98     private fun calculateOverscrollAmount(dragDownAmount: Float): Int {
99         val fullHeight: Int = nsslController.height
100         val fullHeightProgress: Float = MathUtils.saturate(dragDownAmount / fullHeight)
101         val overshootStart: Float = transitionToFullShadeDistance / fullHeight.toFloat()
102         val overShootTransitionProgress: Float =
103             Interpolators.getOvershootInterpolation(
104                 fullHeightProgress, OVER_SHOOT_AMOUNT, overshootStart)
105         return (overShootTransitionProgress * maxOverScrollAmount).toInt()
106     }
107 
108     private fun releaseOverScroll() {
109         val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
110         animator.addUpdateListener {
111             val overScrollAmount = it.animatedValue as Int
112             qS?.setOverScrollAmount(overScrollAmount)
113             scrimController.setNotificationsOverScrollAmount(overScrollAmount)
114             nsslController.setOverScrollAmount(overScrollAmount)
115         }
116         animator.interpolator = RELEASE_OVER_SCROLL_INTERPOLATOR
117         animator.duration = releaseOverScrollDuration
118         animator.start()
119         releaseOverScrollAnimator = animator
120         previousOverscrollAmount = 0
121     }
122 
123     @VisibleForTesting
124     internal fun finishAnimations() {
125         releaseOverScrollAnimator?.end()
126         releaseOverScrollAnimator = null
127     }
128 
129     private fun dump(printWriter: PrintWriter) {
130         printWriter.println(
131             """
132             SplitShadeLockScreenOverScroller:
133                 Resources:
134                     transitionToFullShadeDistance: $transitionToFullShadeDistance
135                     maxOverScrollAmount: $maxOverScrollAmount
136                     releaseOverScrollDuration: $releaseOverScrollDuration
137                 State:
138                     previousOverscrollAmount: $previousOverscrollAmount
139                     expansionDragDownAmount: $expansionDragDownAmount
140             """.trimIndent())
141     }
142 
143     @AssistedFactory
144     fun interface Factory {
145         fun create(
146             qSProvider: () -> QS?,
147             nsslControllerProvider: () -> NotificationStackScrollLayoutController
148         ): SplitShadeLockScreenOverScroller
149     }
150 
151     companion object {
152         private const val OVER_SHOOT_AMOUNT = 0.6f
153         private val RELEASE_OVER_SCROLL_INTERPOLATOR = PathInterpolator(0.17f, 0f, 0f, 1f)
154     }
155 }
156