<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