blob: 7c193b19654b7b63866a2d8e0cbd4585bb80c715 [file] [log] [blame]
Selim Cinekd5921a82019-01-29 19:04:08 -08001/*
2 * Copyright (C) 2019 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
17package com.android.systemui.statusbar.notification
18
19import android.animation.ObjectAnimator
Selim Cinek15af9762019-03-19 18:32:37 -070020import android.content.Context
Selim Cinekd5921a82019-01-29 19:04:08 -080021import android.util.FloatProperty
Selim Cinekd5921a82019-01-29 19:04:08 -080022import com.android.systemui.Interpolators
Selim Cinek34518f62019-02-28 19:41:18 -080023import com.android.systemui.plugins.statusbar.StatusBarStateController
Selim Cinek5040f2e2019-02-14 18:22:42 -080024import com.android.systemui.statusbar.AmbientPulseManager
Selim Cinek34518f62019-02-28 19:41:18 -080025import com.android.systemui.statusbar.SysuiStatusBarStateController
Selim Cinek624d6ca2019-02-19 15:39:08 -080026import com.android.systemui.statusbar.notification.collection.NotificationEntry
Selim Cinekd5921a82019-01-29 19:04:08 -080027import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
28import com.android.systemui.statusbar.notification.stack.StackStateAnimator
Selim Cinek15af9762019-03-19 18:32:37 -070029import com.android.systemui.statusbar.phone.DozeParameters
Selim Cinekd5921a82019-01-29 19:04:08 -080030
31import javax.inject.Inject
32import javax.inject.Singleton
33
34@Singleton
Selim Cinek5040f2e2019-02-14 18:22:42 -080035class NotificationWakeUpCoordinator @Inject constructor(
Selim Cinek15af9762019-03-19 18:32:37 -070036 private val mContext: Context,
Selim Cinek34518f62019-02-28 19:41:18 -080037 private val mAmbientPulseManager: AmbientPulseManager,
38 private val mStatusBarStateController: StatusBarStateController)
39 : AmbientPulseManager.OnAmbientChangedListener, StatusBarStateController.StateListener {
Selim Cinekd5921a82019-01-29 19:04:08 -080040
41 private val mNotificationVisibility
42 = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") {
43
44 override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) {
45 coordinator.setVisibilityAmount(value)
46 }
47
48 override fun get(coordinator: NotificationWakeUpCoordinator): Float? {
49 return coordinator.mLinearVisibilityAmount
50 }
51 }
52 private lateinit var mStackScroller: NotificationStackScrollLayout
53 private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
54
55 private var mLinearDozeAmount: Float = 0.0f
56 private var mDozeAmount: Float = 0.0f
57 private var mNotificationVisibleAmount = 0.0f
58 private var mNotificationsVisible = false
Selim Cinek624d6ca2019-02-19 15:39:08 -080059 private var mNotificationsVisibleForExpansion = false
Selim Cinekd5921a82019-01-29 19:04:08 -080060 private var mDarkAnimator: ObjectAnimator? = null
61 private var mVisibilityAmount = 0.0f
62 private var mLinearVisibilityAmount = 0.0f
Selim Cinek459aee32019-02-20 11:18:56 -080063 private var mWakingUp = false
64 private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
Selim Cinek15af9762019-03-19 18:32:37 -070065 private val mDozeParameters: DozeParameters;
Selim Cinekd0b48e32019-05-24 20:49:23 -070066 var willWakeUp = false
67 set(value) {
Selim Cinek2bbd4572019-05-28 15:31:10 -070068 if (!value || mDozeAmount != 0.0f) {
Selim Cinekd0b48e32019-05-24 20:49:23 -070069 field = value
70 }
71 }
72
73 var pulsing: Boolean = false
74 set(value) {
75 field = value
76 if (value) {
77 // Only when setting pulsing to true we want an immediate update, since we get
78 // this already when the doze service finishes which is usually before we get
79 // the waking up callback
80 updateNotificationVisibility(animate = shouldAnimateVisibility(),
81 increaseSpeed = false)
82 }
83 }
84
Selim Cinek624d6ca2019-02-19 15:39:08 -080085
86 init {
87 mAmbientPulseManager.addListener(this)
Selim Cinek34518f62019-02-28 19:41:18 -080088 mStatusBarStateController.addCallback(this)
Selim Cinek15af9762019-03-19 18:32:37 -070089 mDozeParameters = DozeParameters.getInstance(mContext)
Selim Cinek624d6ca2019-02-19 15:39:08 -080090 }
Selim Cinekd5921a82019-01-29 19:04:08 -080091
92 fun setStackScroller(stackScroller: NotificationStackScrollLayout) {
93 mStackScroller = stackScroller
94 }
95
Selim Cinek3d6ae232019-01-04 14:14:33 -080096 /**
97 * @param visible should notifications be visible
98 * @param animate should this change be animated
99 * @param increaseSpeed should the speed be increased of the animation
100 */
Selim Cinek624d6ca2019-02-19 15:39:08 -0800101 fun setNotificationsVisibleForExpansion(visible: Boolean, animate: Boolean,
102 increaseSpeed: Boolean) {
103 mNotificationsVisibleForExpansion = visible
104 updateNotificationVisibility(animate, increaseSpeed)
Selim Cinek34518f62019-02-28 19:41:18 -0800105 if (!visible && mNotificationsVisible) {
106 // If we stopped expanding and we're still visible because we had a pulse that hasn't
107 // times out, let's release them all to make sure were not stuck in a state where
108 // notifications are visible
109 mAmbientPulseManager.releaseAllImmediately()
110 }
Selim Cinek624d6ca2019-02-19 15:39:08 -0800111 }
112
113 private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
Selim Cinekd0b48e32019-05-24 20:49:23 -0700114 var visible = (mNotificationsVisibleForExpansion || mAmbientPulseManager.hasNotifications())
115 && pulsing;
116 if (!visible && mNotificationsVisible && (mWakingUp || willWakeUp) && mDozeAmount != 0.0f) {
Selim Cinek459aee32019-02-20 11:18:56 -0800117 // let's not make notifications invisible while waking up, otherwise the animation
118 // is strange
119 return;
Selim Cinek624d6ca2019-02-19 15:39:08 -0800120 }
121 setNotificationsVisible(visible, animate, increaseSpeed)
122 }
123
124 private fun setNotificationsVisible(visible: Boolean, animate: Boolean,
125 increaseSpeed: Boolean) {
Selim Cinekd5921a82019-01-29 19:04:08 -0800126 if (mNotificationsVisible == visible) {
127 return
128 }
129 mNotificationsVisible = visible
130 mDarkAnimator?.cancel();
131 if (animate) {
Selim Cinekd5921a82019-01-29 19:04:08 -0800132 notifyAnimationStart(visible)
Selim Cinek3d6ae232019-01-04 14:14:33 -0800133 startVisibilityAnimation(increaseSpeed)
Selim Cinekd5921a82019-01-29 19:04:08 -0800134 } else {
135 setVisibilityAmount(if (visible) 1.0f else 0.0f)
136 }
137 }
138
Selim Cinek34518f62019-02-28 19:41:18 -0800139 override fun onDozeAmountChanged(linear: Float, eased: Float) {
140 if (linear != 1.0f && linear != 0.0f
141 && (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
142 // Let's notify the scroller that an animation started
143 notifyAnimationStart(mLinearDozeAmount == 1.0f)
144 }
145 mLinearDozeAmount = linear
146 mDozeAmount = eased
Selim Cinek3d6ae232019-01-04 14:14:33 -0800147 mStackScroller.setDozeAmount(mDozeAmount)
Selim Cinekd5921a82019-01-29 19:04:08 -0800148 updateDarkAmount()
Selim Cinek34518f62019-02-28 19:41:18 -0800149 if (linear == 0.0f) {
Selim Cinek459aee32019-02-20 11:18:56 -0800150 setNotificationsVisible(visible = false, animate = false, increaseSpeed = false);
151 setNotificationsVisibleForExpansion(visible = false, animate = false,
152 increaseSpeed = false)
153 }
Selim Cinekd5921a82019-01-29 19:04:08 -0800154 }
155
Selim Cinek3d6ae232019-01-04 14:14:33 -0800156 private fun startVisibilityAnimation(increaseSpeed: Boolean) {
Selim Cinekd5921a82019-01-29 19:04:08 -0800157 if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) {
158 mVisibilityInterpolator = if (mNotificationsVisible)
Selim Cinek3d6ae232019-01-04 14:14:33 -0800159 Interpolators.TOUCH_RESPONSE
Selim Cinekd5921a82019-01-29 19:04:08 -0800160 else
Selim Cinek3d6ae232019-01-04 14:14:33 -0800161 Interpolators.FAST_OUT_SLOW_IN_REVERSE
Selim Cinekd5921a82019-01-29 19:04:08 -0800162 }
163 val target = if (mNotificationsVisible) 1.0f else 0.0f
164 val darkAnimator = ObjectAnimator.ofFloat(this, mNotificationVisibility, target)
165 darkAnimator.setInterpolator(Interpolators.LINEAR)
Selim Cinek3d6ae232019-01-04 14:14:33 -0800166 var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
167 if (increaseSpeed) {
168 duration = (duration.toFloat() / 1.5F).toLong();
169 }
170 darkAnimator.setDuration(duration)
Selim Cinekd5921a82019-01-29 19:04:08 -0800171 darkAnimator.start()
172 mDarkAnimator = darkAnimator
173 }
174
175 private fun setVisibilityAmount(visibilityAmount: Float) {
176 mLinearVisibilityAmount = visibilityAmount
177 mVisibilityAmount = mVisibilityInterpolator.getInterpolation(
178 visibilityAmount)
Selim Cinek459aee32019-02-20 11:18:56 -0800179 handleAnimationFinished();
Selim Cinekd5921a82019-01-29 19:04:08 -0800180 updateDarkAmount()
181 }
182
Selim Cinek459aee32019-02-20 11:18:56 -0800183 private fun handleAnimationFinished() {
184 if (mLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) {
185 mEntrySetToClearWhenFinished.forEach { it.setAmbientGoingAway(false) }
Selim Cinek0e180ac2019-04-02 15:55:22 -0700186 mEntrySetToClearWhenFinished.clear()
Selim Cinek459aee32019-02-20 11:18:56 -0800187 }
188 }
189
Selim Cinek3d6ae232019-01-04 14:14:33 -0800190 fun getWakeUpHeight() : Float {
Selim Cinek34518f62019-02-28 19:41:18 -0800191 return mStackScroller.pulseHeight
Selim Cinek3d6ae232019-01-04 14:14:33 -0800192 }
193
Selim Cinekd5921a82019-01-29 19:04:08 -0800194 private fun updateDarkAmount() {
195 val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount)
196 val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount)
197 mStackScroller.setDarkAmount(linearAmount, amount)
198 }
199
Selim Cinek3d6ae232019-01-04 14:14:33 -0800200 private fun notifyAnimationStart(awake: Boolean) {
Selim Cinekd5921a82019-01-29 19:04:08 -0800201 mStackScroller.notifyDarkAnimationStart(!awake)
202 }
Selim Cinek3d6ae232019-01-04 14:14:33 -0800203
Selim Cinek34518f62019-02-28 19:41:18 -0800204 override fun onDozingChanged(isDozing: Boolean) {
205 if (isDozing) {
Selim Cinek459aee32019-02-20 11:18:56 -0800206 setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
Selim Cinek3d6ae232019-01-04 14:14:33 -0800207 }
Selim Cinek3d6ae232019-01-04 14:14:33 -0800208 }
209
Selim Cinek5040f2e2019-02-14 18:22:42 -0800210 fun setPulseHeight(height: Float): Float {
211 return mStackScroller.setPulseHeight(height)
212 }
213
Selim Cinek459aee32019-02-20 11:18:56 -0800214 fun setWakingUp(wakingUp: Boolean) {
Selim Cinekd0b48e32019-05-24 20:49:23 -0700215 willWakeUp = false
Selim Cinek459aee32019-02-20 11:18:56 -0800216 mWakingUp = wakingUp
217 if (wakingUp && mNotificationsVisible && !mNotificationsVisibleForExpansion) {
218 // We're waking up while pulsing, let's make sure the animation looks nice
219 mStackScroller.wakeUpFromPulse();
220 }
Selim Cinek624d6ca2019-02-19 15:39:08 -0800221 }
222
Selim Cinek459aee32019-02-20 11:18:56 -0800223 override fun onAmbientStateChanged(entry: NotificationEntry, isPulsing: Boolean) {
Selim Cinekd0b48e32019-05-24 20:49:23 -0700224 var animate = shouldAnimateVisibility()
Selim Cinekae55d832019-02-22 17:43:43 -0800225 if (!isPulsing) {
Selim Cinekd0b48e32019-05-24 20:49:23 -0700226 if (mLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) {
Selim Cinekae55d832019-02-22 17:43:43 -0800227 if (entry.isRowDismissed) {
228 // if we animate, we see the shelf briefly visible. Instead we fully animate
229 // the notification and its background out
230 animate = false
Selim Cinekf434a742019-05-28 17:39:49 -0700231 } else if (!mWakingUp && !willWakeUp){
Selim Cinekae55d832019-02-22 17:43:43 -0800232 entry.setAmbientGoingAway(true)
233 mEntrySetToClearWhenFinished.add(entry)
234 }
235 }
236 } else if (mEntrySetToClearWhenFinished.contains(entry)) {
Selim Cinek459aee32019-02-20 11:18:56 -0800237 mEntrySetToClearWhenFinished.remove(entry)
238 entry.setAmbientGoingAway(false)
239 }
Selim Cinekae55d832019-02-22 17:43:43 -0800240 updateNotificationVisibility(animate, increaseSpeed = false)
Selim Cinek3d6ae232019-01-04 14:14:33 -0800241 }
Selim Cinekd0b48e32019-05-24 20:49:23 -0700242
243 private fun shouldAnimateVisibility() =
244 mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking()
Selim Cinekd5921a82019-01-29 19:04:08 -0800245}