• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.statusbar.notification
18 
19 import android.animation.ObjectAnimator
20 import android.content.Context
21 import android.util.FloatProperty
22 import com.android.systemui.Interpolators
23 import com.android.systemui.plugins.statusbar.StatusBarStateController
24 import com.android.systemui.statusbar.AmbientPulseManager
25 import com.android.systemui.statusbar.SysuiStatusBarStateController
26 import com.android.systemui.statusbar.notification.collection.NotificationEntry
27 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
28 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
29 import com.android.systemui.statusbar.phone.DozeParameters
30 
31 import javax.inject.Inject
32 import javax.inject.Singleton
33 
34 @Singleton
35 class NotificationWakeUpCoordinator @Inject constructor(
36         private val mContext: Context,
37         private val mAmbientPulseManager: AmbientPulseManager,
38         private val mStatusBarStateController: StatusBarStateController)
39     : AmbientPulseManager.OnAmbientChangedListener, StatusBarStateController.StateListener {
40 
41     private val mNotificationVisibility
42             = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") {
43 
setValuenull44         override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) {
45             coordinator.setVisibilityAmount(value)
46         }
47 
getnull48         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
59     private var mNotificationsVisibleForExpansion = false
60     private var mDarkAnimator: ObjectAnimator? = null
61     private var mVisibilityAmount = 0.0f
62     private var mLinearVisibilityAmount = 0.0f
63     private var mWakingUp = false
64     private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
65     private val mDozeParameters: DozeParameters;
66     var willWakeUp = false
67         set(value) {
68             if (!value || mDozeAmount != 0.0f) {
69                 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 
85 
86     init {
87         mAmbientPulseManager.addListener(this)
88         mStatusBarStateController.addCallback(this)
89         mDozeParameters = DozeParameters.getInstance(mContext)
90     }
91 
setStackScrollernull92     fun setStackScroller(stackScroller: NotificationStackScrollLayout) {
93         mStackScroller = stackScroller
94     }
95 
96     /**
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      */
setNotificationsVisibleForExpansionnull101     fun setNotificationsVisibleForExpansion(visible: Boolean, animate: Boolean,
102                                                     increaseSpeed: Boolean) {
103         mNotificationsVisibleForExpansion = visible
104         updateNotificationVisibility(animate, increaseSpeed)
105         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         }
111     }
112 
updateNotificationVisibilitynull113     private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
114         var visible = (mNotificationsVisibleForExpansion || mAmbientPulseManager.hasNotifications())
115                 && pulsing;
116         if (!visible && mNotificationsVisible && (mWakingUp || willWakeUp) && mDozeAmount != 0.0f) {
117             // let's not make notifications invisible while waking up, otherwise the animation
118             // is strange
119             return;
120         }
121         setNotificationsVisible(visible, animate, increaseSpeed)
122     }
123 
setNotificationsVisiblenull124     private fun setNotificationsVisible(visible: Boolean, animate: Boolean,
125                                         increaseSpeed: Boolean) {
126         if (mNotificationsVisible == visible) {
127             return
128         }
129         mNotificationsVisible = visible
130         mDarkAnimator?.cancel();
131         if (animate) {
132             notifyAnimationStart(visible)
133             startVisibilityAnimation(increaseSpeed)
134         } else {
135             setVisibilityAmount(if (visible) 1.0f else 0.0f)
136         }
137     }
138 
onDozeAmountChangednull139     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
147         mStackScroller.setDozeAmount(mDozeAmount)
148         updateDarkAmount()
149         if (linear == 0.0f) {
150             setNotificationsVisible(visible = false, animate = false, increaseSpeed = false);
151             setNotificationsVisibleForExpansion(visible = false, animate = false,
152                     increaseSpeed = false)
153         }
154     }
155 
startVisibilityAnimationnull156     private fun startVisibilityAnimation(increaseSpeed: Boolean) {
157         if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) {
158             mVisibilityInterpolator = if (mNotificationsVisible)
159                 Interpolators.TOUCH_RESPONSE
160             else
161                 Interpolators.FAST_OUT_SLOW_IN_REVERSE
162         }
163         val target = if (mNotificationsVisible) 1.0f else 0.0f
164         val darkAnimator = ObjectAnimator.ofFloat(this, mNotificationVisibility, target)
165         darkAnimator.setInterpolator(Interpolators.LINEAR)
166         var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
167         if (increaseSpeed) {
168             duration = (duration.toFloat() / 1.5F).toLong();
169         }
170         darkAnimator.setDuration(duration)
171         darkAnimator.start()
172         mDarkAnimator = darkAnimator
173     }
174 
setVisibilityAmountnull175     private fun setVisibilityAmount(visibilityAmount: Float) {
176         mLinearVisibilityAmount = visibilityAmount
177         mVisibilityAmount = mVisibilityInterpolator.getInterpolation(
178                 visibilityAmount)
179         handleAnimationFinished();
180         updateDarkAmount()
181     }
182 
handleAnimationFinishednull183     private fun handleAnimationFinished() {
184         if (mLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) {
185             mEntrySetToClearWhenFinished.forEach { it.setAmbientGoingAway(false) }
186             mEntrySetToClearWhenFinished.clear()
187         }
188     }
189 
getWakeUpHeightnull190     fun getWakeUpHeight() : Float {
191         return mStackScroller.pulseHeight
192     }
193 
updateDarkAmountnull194     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 
notifyAnimationStartnull200     private fun notifyAnimationStart(awake: Boolean) {
201         mStackScroller.notifyDarkAnimationStart(!awake)
202     }
203 
onDozingChangednull204     override fun onDozingChanged(isDozing: Boolean) {
205         if (isDozing) {
206             setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
207         }
208     }
209 
setPulseHeightnull210     fun setPulseHeight(height: Float): Float {
211         return mStackScroller.setPulseHeight(height)
212     }
213 
setWakingUpnull214     fun setWakingUp(wakingUp: Boolean) {
215         willWakeUp = false
216         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         }
221     }
222 
onAmbientStateChangednull223     override fun onAmbientStateChanged(entry: NotificationEntry, isPulsing: Boolean) {
224         var animate = shouldAnimateVisibility()
225         if (!isPulsing) {
226             if (mLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) {
227                 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
231                 } else if (!mWakingUp && !willWakeUp){
232                     entry.setAmbientGoingAway(true)
233                     mEntrySetToClearWhenFinished.add(entry)
234                 }
235             }
236         } else if (mEntrySetToClearWhenFinished.contains(entry)) {
237             mEntrySetToClearWhenFinished.remove(entry)
238             entry.setAmbientGoingAway(false)
239         }
240         updateNotificationVisibility(animate, increaseSpeed = false)
241     }
242 
shouldAnimateVisibilitynull243     private fun shouldAnimateVisibility() =
244             mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking()
245 }