• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.systemui.statusbar.notification
2 
3 import android.view.ViewGroup
4 import com.android.internal.jank.InteractionJankMonitor
5 import com.android.systemui.animation.ActivityLaunchAnimator
6 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
7 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
8 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
9 import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
10 import com.android.systemui.statusbar.policy.HeadsUpUtil
11 import kotlin.math.ceil
12 import kotlin.math.max
13 
14 /** A provider of [NotificationLaunchAnimatorController]. */
15 class NotificationLaunchAnimatorControllerProvider(
16     private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
17     private val notificationListContainer: NotificationListContainer,
18     private val headsUpManager: HeadsUpManagerPhone
19 ) {
getAnimatorControllernull20     fun getAnimatorController(
21         notification: ExpandableNotificationRow
22     ): NotificationLaunchAnimatorController {
23         return NotificationLaunchAnimatorController(
24             notificationShadeWindowViewController,
25             notificationListContainer,
26             headsUpManager,
27             notification
28         )
29     }
30 }
31 
32 /**
33  * An [ActivityLaunchAnimator.Controller] that animates an [ExpandableNotificationRow]. An instance
34  * of this class can be passed to [ActivityLaunchAnimator.startIntentWithAnimation] to animate a
35  * notification expanding into an opening window.
36  */
37 class NotificationLaunchAnimatorController(
38     private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
39     private val notificationListContainer: NotificationListContainer,
40     private val headsUpManager: HeadsUpManagerPhone,
41     private val notification: ExpandableNotificationRow
42 ) : ActivityLaunchAnimator.Controller {
43 
44     companion object {
45         const val ANIMATION_DURATION_TOP_ROUNDING = 100L
46     }
47 
48     private val notificationEntry = notification.entry
49     private val notificationKey = notificationEntry.sbn.key
50 
51     override var launchContainer: ViewGroup
52         get() = notification.rootView as ViewGroup
53         set(ignored) {
54             // Do nothing. Notifications are always animated inside their rootView.
55         }
56 
createAnimatorStatenull57     override fun createAnimatorState(): ActivityLaunchAnimator.State {
58         // If the notification panel is collapsed, the clip may be larger than the height.
59         val height = max(0, notification.actualHeight - notification.clipBottomAmount)
60         val location = notification.locationOnScreen
61 
62         val clipStartLocation = notificationListContainer.getTopClippingStartLocation()
63         val roundedTopClipping = Math.max(clipStartLocation - location[1], 0)
64         val windowTop = location[1] + roundedTopClipping
65         val topCornerRadius = if (roundedTopClipping > 0) {
66             // Because the rounded Rect clipping is complex, we start the top rounding at
67             // 0, which is pretty close to matching the real clipping.
68             // We'd have to clipOut the overlaid drawable too with the outer rounded rect in case
69             // if we'd like to have this perfect, but this is close enough.
70             0f
71         } else {
72             notification.currentBackgroundRadiusTop
73         }
74         val params = ExpandAnimationParameters(
75                 top = windowTop,
76                 bottom = location[1] + height,
77                 left = location[0],
78                 right = location[0] + notification.width,
79                 topCornerRadius = topCornerRadius,
80                 bottomCornerRadius = notification.currentBackgroundRadiusBottom
81         )
82 
83         params.startTranslationZ = notification.translationZ
84         params.startNotificationTop = notification.translationY
85         params.startRoundedTopClipping = roundedTopClipping
86         params.startClipTopAmount = notification.clipTopAmount
87         if (notification.isChildInGroup) {
88             params.startNotificationTop += notification.notificationParent.translationY
89             val parentRoundedClip = Math.max(clipStartLocation
90                 - notification.notificationParent.locationOnScreen[1], 0)
91             params.parentStartRoundedTopClipping = parentRoundedClip
92 
93             val parentClip = notification.notificationParent.clipTopAmount
94             params.parentStartClipTopAmount = parentClip
95 
96             // We need to calculate how much the child is clipped by the parent because children
97             // always have 0 clipTopAmount
98             if (parentClip != 0) {
99                 val childClip = parentClip - notification.translationY
100                 if (childClip > 0) {
101                     params.startClipTopAmount = ceil(childClip.toDouble()).toInt()
102                 }
103             }
104         }
105 
106         return params
107     }
108 
onIntentStartednull109     override fun onIntentStarted(willAnimate: Boolean) {
110         notificationShadeWindowViewController.setExpandAnimationRunning(willAnimate)
111         notificationEntry.isExpandAnimationRunning = willAnimate
112 
113         if (!willAnimate) {
114             removeHun(animate = true)
115         }
116     }
117 
removeHunnull118     private fun removeHun(animate: Boolean) {
119         if (!headsUpManager.isAlerting(notificationKey)) {
120             return
121         }
122 
123         HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(notification, animate)
124         headsUpManager.removeNotification(notificationKey, true /* releaseImmediately */, animate)
125     }
126 
onLaunchAnimationCancellednull127     override fun onLaunchAnimationCancelled() {
128         // TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
129         // here?
130         notificationShadeWindowViewController.setExpandAnimationRunning(false)
131         notificationEntry.isExpandAnimationRunning = false
132         removeHun(animate = true)
133     }
134 
onLaunchAnimationStartnull135     override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
136         notification.isExpandAnimationRunning = true
137         notificationListContainer.setExpandingNotification(notification)
138 
139         InteractionJankMonitor.getInstance().begin(notification,
140             InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
141     }
142 
onLaunchAnimationEndnull143     override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
144         InteractionJankMonitor.getInstance().end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
145 
146         notification.isExpandAnimationRunning = false
147         notificationShadeWindowViewController.setExpandAnimationRunning(false)
148         notificationEntry.isExpandAnimationRunning = false
149         notificationListContainer.setExpandingNotification(null)
150         applyParams(null)
151         removeHun(animate = false)
152     }
153 
applyParamsnull154     private fun applyParams(params: ExpandAnimationParameters?) {
155         notification.applyExpandAnimationParams(params)
156         notificationListContainer.applyExpandAnimationParams(params)
157     }
158 
onLaunchAnimationProgressnull159     override fun onLaunchAnimationProgress(
160         state: ActivityLaunchAnimator.State,
161         progress: Float,
162         linearProgress: Float
163     ) {
164         val params = state as ExpandAnimationParameters
165         params.progress = progress
166         params.linearProgress = linearProgress
167 
168         applyParams(params)
169     }
170 }
171