Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 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.util.FloatProperty |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 21 | import com.android.systemui.Interpolators |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 22 | import com.android.systemui.plugins.statusbar.StatusBarStateController |
Selim Cinek | 5040f2e | 2019-02-14 18:22:42 -0800 | [diff] [blame] | 23 | import com.android.systemui.statusbar.AmbientPulseManager |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 24 | import com.android.systemui.statusbar.SysuiStatusBarStateController |
Selim Cinek | 624d6ca | 2019-02-19 15:39:08 -0800 | [diff] [blame] | 25 | import com.android.systemui.statusbar.notification.collection.NotificationEntry |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 26 | import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout |
| 27 | import com.android.systemui.statusbar.notification.stack.StackStateAnimator |
| 28 | |
| 29 | import javax.inject.Inject |
| 30 | import javax.inject.Singleton |
| 31 | |
| 32 | @Singleton |
Selim Cinek | 5040f2e | 2019-02-14 18:22:42 -0800 | [diff] [blame] | 33 | class NotificationWakeUpCoordinator @Inject constructor( |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 34 | private val mAmbientPulseManager: AmbientPulseManager, |
| 35 | private val mStatusBarStateController: StatusBarStateController) |
| 36 | : AmbientPulseManager.OnAmbientChangedListener, StatusBarStateController.StateListener { |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 37 | |
| 38 | private val mNotificationVisibility |
| 39 | = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") { |
| 40 | |
| 41 | override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) { |
| 42 | coordinator.setVisibilityAmount(value) |
| 43 | } |
| 44 | |
| 45 | override fun get(coordinator: NotificationWakeUpCoordinator): Float? { |
| 46 | return coordinator.mLinearVisibilityAmount |
| 47 | } |
| 48 | } |
| 49 | private lateinit var mStackScroller: NotificationStackScrollLayout |
| 50 | private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE |
| 51 | |
| 52 | private var mLinearDozeAmount: Float = 0.0f |
| 53 | private var mDozeAmount: Float = 0.0f |
| 54 | private var mNotificationVisibleAmount = 0.0f |
| 55 | private var mNotificationsVisible = false |
Selim Cinek | 624d6ca | 2019-02-19 15:39:08 -0800 | [diff] [blame] | 56 | private var mNotificationsVisibleForExpansion = false |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 57 | private var mDarkAnimator: ObjectAnimator? = null |
| 58 | private var mVisibilityAmount = 0.0f |
| 59 | private var mLinearVisibilityAmount = 0.0f |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 60 | private var mWakingUp = false |
| 61 | private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>() |
Selim Cinek | 624d6ca | 2019-02-19 15:39:08 -0800 | [diff] [blame] | 62 | |
| 63 | init { |
| 64 | mAmbientPulseManager.addListener(this) |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 65 | mStatusBarStateController.addCallback(this) |
Selim Cinek | 624d6ca | 2019-02-19 15:39:08 -0800 | [diff] [blame] | 66 | } |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 67 | |
| 68 | fun setStackScroller(stackScroller: NotificationStackScrollLayout) { |
| 69 | mStackScroller = stackScroller |
| 70 | } |
| 71 | |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 72 | /** |
| 73 | * @param visible should notifications be visible |
| 74 | * @param animate should this change be animated |
| 75 | * @param increaseSpeed should the speed be increased of the animation |
| 76 | */ |
Selim Cinek | 624d6ca | 2019-02-19 15:39:08 -0800 | [diff] [blame] | 77 | fun setNotificationsVisibleForExpansion(visible: Boolean, animate: Boolean, |
| 78 | increaseSpeed: Boolean) { |
| 79 | mNotificationsVisibleForExpansion = visible |
| 80 | updateNotificationVisibility(animate, increaseSpeed) |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 81 | if (!visible && mNotificationsVisible) { |
| 82 | // If we stopped expanding and we're still visible because we had a pulse that hasn't |
| 83 | // times out, let's release them all to make sure were not stuck in a state where |
| 84 | // notifications are visible |
| 85 | mAmbientPulseManager.releaseAllImmediately() |
| 86 | } |
Selim Cinek | 624d6ca | 2019-02-19 15:39:08 -0800 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) { |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 90 | var visible = mNotificationsVisibleForExpansion || mAmbientPulseManager.hasNotifications() |
| 91 | if (!visible && mNotificationsVisible && mWakingUp && mDozeAmount != 0.0f) { |
| 92 | // let's not make notifications invisible while waking up, otherwise the animation |
| 93 | // is strange |
| 94 | return; |
Selim Cinek | 624d6ca | 2019-02-19 15:39:08 -0800 | [diff] [blame] | 95 | } |
| 96 | setNotificationsVisible(visible, animate, increaseSpeed) |
| 97 | } |
| 98 | |
| 99 | private fun setNotificationsVisible(visible: Boolean, animate: Boolean, |
| 100 | increaseSpeed: Boolean) { |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 101 | if (mNotificationsVisible == visible) { |
| 102 | return |
| 103 | } |
| 104 | mNotificationsVisible = visible |
| 105 | mDarkAnimator?.cancel(); |
| 106 | if (animate) { |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 107 | notifyAnimationStart(visible) |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 108 | startVisibilityAnimation(increaseSpeed) |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 109 | } else { |
| 110 | setVisibilityAmount(if (visible) 1.0f else 0.0f) |
| 111 | } |
| 112 | } |
| 113 | |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 114 | override fun onDozeAmountChanged(linear: Float, eased: Float) { |
| 115 | if (linear != 1.0f && linear != 0.0f |
| 116 | && (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) { |
| 117 | // Let's notify the scroller that an animation started |
| 118 | notifyAnimationStart(mLinearDozeAmount == 1.0f) |
| 119 | } |
| 120 | mLinearDozeAmount = linear |
| 121 | mDozeAmount = eased |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 122 | mStackScroller.setDozeAmount(mDozeAmount) |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 123 | updateDarkAmount() |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 124 | if (linear == 0.0f) { |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 125 | setNotificationsVisible(visible = false, animate = false, increaseSpeed = false); |
| 126 | setNotificationsVisibleForExpansion(visible = false, animate = false, |
| 127 | increaseSpeed = false) |
| 128 | } |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 129 | } |
| 130 | |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 131 | private fun startVisibilityAnimation(increaseSpeed: Boolean) { |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 132 | if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) { |
| 133 | mVisibilityInterpolator = if (mNotificationsVisible) |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 134 | Interpolators.TOUCH_RESPONSE |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 135 | else |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 136 | Interpolators.FAST_OUT_SLOW_IN_REVERSE |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 137 | } |
| 138 | val target = if (mNotificationsVisible) 1.0f else 0.0f |
| 139 | val darkAnimator = ObjectAnimator.ofFloat(this, mNotificationVisibility, target) |
| 140 | darkAnimator.setInterpolator(Interpolators.LINEAR) |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 141 | var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong() |
| 142 | if (increaseSpeed) { |
| 143 | duration = (duration.toFloat() / 1.5F).toLong(); |
| 144 | } |
| 145 | darkAnimator.setDuration(duration) |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 146 | darkAnimator.start() |
| 147 | mDarkAnimator = darkAnimator |
| 148 | } |
| 149 | |
| 150 | private fun setVisibilityAmount(visibilityAmount: Float) { |
| 151 | mLinearVisibilityAmount = visibilityAmount |
| 152 | mVisibilityAmount = mVisibilityInterpolator.getInterpolation( |
| 153 | visibilityAmount) |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 154 | handleAnimationFinished(); |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 155 | updateDarkAmount() |
| 156 | } |
| 157 | |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 158 | private fun handleAnimationFinished() { |
| 159 | if (mLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) { |
| 160 | mEntrySetToClearWhenFinished.forEach { it.setAmbientGoingAway(false) } |
| 161 | } |
| 162 | } |
| 163 | |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 164 | fun getWakeUpHeight() : Float { |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 165 | return mStackScroller.pulseHeight |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 166 | } |
| 167 | |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 168 | private fun updateDarkAmount() { |
| 169 | val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount) |
| 170 | val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount) |
| 171 | mStackScroller.setDarkAmount(linearAmount, amount) |
| 172 | } |
| 173 | |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 174 | private fun notifyAnimationStart(awake: Boolean) { |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 175 | mStackScroller.notifyDarkAnimationStart(!awake) |
| 176 | } |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 177 | |
Selim Cinek | 34518f6 | 2019-02-28 19:41:18 -0800 | [diff] [blame] | 178 | override fun onDozingChanged(isDozing: Boolean) { |
| 179 | if (isDozing) { |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 180 | setNotificationsVisible(visible = false, animate = false, increaseSpeed = false) |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 181 | } |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 182 | } |
| 183 | |
Selim Cinek | 5040f2e | 2019-02-14 18:22:42 -0800 | [diff] [blame] | 184 | fun setPulseHeight(height: Float): Float { |
| 185 | return mStackScroller.setPulseHeight(height) |
| 186 | } |
| 187 | |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 188 | fun setWakingUp(wakingUp: Boolean) { |
| 189 | mWakingUp = wakingUp |
| 190 | if (wakingUp && mNotificationsVisible && !mNotificationsVisibleForExpansion) { |
| 191 | // We're waking up while pulsing, let's make sure the animation looks nice |
| 192 | mStackScroller.wakeUpFromPulse(); |
| 193 | } |
Selim Cinek | 624d6ca | 2019-02-19 15:39:08 -0800 | [diff] [blame] | 194 | } |
| 195 | |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 196 | override fun onAmbientStateChanged(entry: NotificationEntry, isPulsing: Boolean) { |
Selim Cinek | ae55d83 | 2019-02-22 17:43:43 -0800 | [diff] [blame] | 197 | var animate = true |
| 198 | if (!isPulsing) { |
| 199 | if (mLinearDozeAmount != 0.0f) { |
| 200 | if (entry.isRowDismissed) { |
| 201 | // if we animate, we see the shelf briefly visible. Instead we fully animate |
| 202 | // the notification and its background out |
| 203 | animate = false |
| 204 | } else { |
| 205 | entry.setAmbientGoingAway(true) |
| 206 | mEntrySetToClearWhenFinished.add(entry) |
| 207 | } |
| 208 | } |
| 209 | } else if (mEntrySetToClearWhenFinished.contains(entry)) { |
Selim Cinek | 459aee3 | 2019-02-20 11:18:56 -0800 | [diff] [blame] | 210 | mEntrySetToClearWhenFinished.remove(entry) |
| 211 | entry.setAmbientGoingAway(false) |
| 212 | } |
Selim Cinek | ae55d83 | 2019-02-22 17:43:43 -0800 | [diff] [blame] | 213 | updateNotificationVisibility(animate, increaseSpeed = false) |
Selim Cinek | 3d6ae23 | 2019-01-04 14:14:33 -0800 | [diff] [blame] | 214 | } |
Selim Cinek | d5921a8 | 2019-01-29 19:04:08 -0800 | [diff] [blame] | 215 | } |