1 /*
2  * Copyright (C) 2023 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 package com.android.systemui.shade
17 
18 import android.view.MotionEvent
19 import android.view.ViewGroup
20 import android.view.ViewTreeObserver
21 import com.android.systemui.keyguard.shared.model.WakefulnessModel
22 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
23 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController
24 import com.android.systemui.statusbar.phone.KeyguardStatusBarView
25 import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController
26 import java.util.function.Consumer
27 
28 /**
29  * Controller for the top level shade view
30  *
31  * @see NotificationPanelViewController
32  */
33 interface ShadeViewController {
34     /** Expand the shade either animated or instantly. */
35     fun expand(animate: Boolean)
36 
37     /** Animates to an expanded shade with QS expanded. If the shade starts expanded, expands QS. */
38     fun expandToQs()
39 
40     /**
41      * Expand shade so that notifications are visible. Non-split shade: just expanding shade or
42      * collapsing QS when they're expanded. Split shade: only expanding shade, notifications are
43      * always visible
44      *
45      * Called when `adb shell cmd statusbar expand-notifications` is executed.
46      */
47     fun expandToNotifications()
48 
49     /** Returns whether the shade is expanding or collapsing itself or quick settings. */
50     val isExpandingOrCollapsing: Boolean
51 
52     /**
53      * Returns whether the shade height is greater than zero (i.e. partially or fully expanded),
54      * there is a HUN, the shade is animating, or the shade is instantly expanding.
55      */
56     val isExpanded: Boolean
57 
58     /**
59      * Returns whether the shade height is greater than zero or the shade is expecting a synthesized
60      * down event.
61      */
62     val isPanelExpanded: Boolean
63 
64     /** Returns whether the shade is fully expanded in either QS or QQS. */
65     val isShadeFullyExpanded: Boolean
66 
67     /**
68      * Animates the collapse of a shade with the given delay and the default duration divided by
69      * speedUpFactor.
70      */
71     fun collapse(delayed: Boolean, speedUpFactor: Float)
72 
73     /** Collapses the shade. */
74     fun collapse(animate: Boolean, delayed: Boolean, speedUpFactor: Float)
75 
76     /** Collapses the shade with an animation duration in milliseconds. */
77     fun collapseWithDuration(animationDuration: Int)
78 
79     /** Collapses the shade instantly without animation. */
80     fun instantCollapse()
81 
82     /**
83      * Animate QS collapse by flinging it. If QS is expanded, it will collapse into QQS and stop. If
84      * in split shade, it will collapse the whole shade.
85      *
86      * @param fullyCollapse Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
87      */
88     fun animateCollapseQs(fullyCollapse: Boolean)
89 
90     /** Returns whether the shade can be collapsed. */
91     fun canBeCollapsed(): Boolean
92 
93     /** Returns whether the shade is in the process of collapsing. */
94     val isCollapsing: Boolean
95 
96     /** Returns whether shade's height is zero. */
97     val isFullyCollapsed: Boolean
98 
99     /** Returns whether the shade is tracking touches for expand/collapse of the shade or QS. */
100     val isTracking: Boolean
101 
102     /** Returns whether the shade's top level view is enabled. */
103     val isViewEnabled: Boolean
104 
105     /** Sets a listener to be notified when the shade starts opening or finishes closing. */
106     fun setOpenCloseListener(openCloseListener: OpenCloseListener)
107 
108     /** Returns whether status bar icons should be hidden when the shade is expanded. */
109     fun shouldHideStatusBarIconsWhenExpanded(): Boolean
110 
111     /**
112      * Do not let the user drag the shade up and down for the current touch session. This is
113      * necessary to avoid shade expansion while/after the bouncer is dismissed.
114      */
115     fun blockExpansionForCurrentTouch()
116 
117     /** Sets a listener to be notified when touch tracking begins. */
118     fun setTrackingStartedListener(trackingStartedListener: TrackingStartedListener)
119 
120     /**
121      * Disables the shade header.
122      *
123      * @see ShadeHeaderController.disable
124      */
125     fun disableHeader(state1: Int, state2: Int, animated: Boolean)
126 
127     /** If the latency tracker is enabled, begins tracking expand latency. */
128     fun startExpandLatencyTracking()
129 
130     /** Called before animating Keyguard dismissal, i.e. the animation dismissing the bouncer. */
131     fun startBouncerPreHideAnimation()
132 
133     /** Called once every minute while dozing. */
134     fun dozeTimeTick()
135 
136     /** Close guts, notification menus, and QS. Set scroll and overscroll to 0. */
137     fun resetViews(animate: Boolean)
138 
139     /** Returns the StatusBarState. */
140     val barState: Int
141 
142     /** Sets the amount of progress in the status bar launch animation. */
143     fun applyLaunchAnimationProgress(linearProgress: Float)
144 
145     /**
146      * Close the keyguard user switcher if it is open and capable of closing.
147      *
148      * Has no effect if user switcher isn't supported, if the user switcher is already closed, or if
149      * the user switcher uses "simple" mode. The simple user switcher cannot be closed.
150      *
151      * @return true if the keyguard user switcher was open, and is now closed
152      */
153     fun closeUserSwitcherIfOpen(): Boolean
154 
155     /** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */
156     fun onBackPressed()
157 
158     /** Sets whether the status bar launch animation is currently running. */
159     fun setIsLaunchAnimationRunning(running: Boolean)
160 
161     /** Sets the alpha value of the shade to a value between 0 and 255. */
162     fun setAlpha(alpha: Int, animate: Boolean)
163 
164     /**
165      * Sets the runnable to run after the alpha change animation completes.
166      *
167      * @see .setAlpha
168      */
169     fun setAlphaChangeAnimationEndAction(r: Runnable)
170 
171     /** Sets whether the screen has temporarily woken up to display notifications. */
172     fun setPulsing(pulsing: Boolean)
173 
174     /** Sets Qs ScrimEnabled and updates QS state. */
175     fun setQsScrimEnabled(qsScrimEnabled: Boolean)
176 
177     /** Sets the top spacing for the ambient indicator. */
178     fun setAmbientIndicationTop(ambientIndicationTop: Int, ambientTextVisible: Boolean)
179 
180     /** Updates notification panel-specific flags on [SysUiState]. */
181     fun updateSystemUiStateFlags()
182 
183     /** Ensures that the touchable region is updated. */
184     fun updateTouchableRegion()
185 
186     /** Adds a global layout listener. */
187     fun addOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
188 
189     /** Removes a global layout listener. */
190     fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
191 
192     /** Posts the given runnable to the view. */
193     fun postToView(action: Runnable): Boolean
194 
195     // ******* Begin Keyguard Section *********
196     /** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
197     fun transitionToExpandedShade(delay: Long)
198 
199     /**
200      * Returns whether the unlock hint animation is running. The unlock hint animation is when the
201      * user taps the lock screen, causing the contents of the lock screen visually bounce.
202      */
203     val isUnlockHintRunning: Boolean
204 
205     /** @see ViewGroupFadeHelper.reset */
206     fun resetViewGroupFade()
207 
208     /**
209      * Set the alpha and translationY of the keyguard elements which only show on the lockscreen,
210      * but not in shade locked / shade. This is used when dragging down to the full shade.
211      */
212     fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int)
213 
214     /** Sets the overstretch amount in raw pixels when dragging down. */
215     fun setOverStretchAmount(amount: Float)
216 
217     /**
218      * Sets the alpha value to be set on the keyguard status bar.
219      *
220      * @param alpha value between 0 and 1. -1 if the value is to be reset.
221      */
222     fun setKeyguardStatusBarAlpha(alpha: Float)
223 
224     /**
225      * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
226      * screen off animation controller in order to animate in AOD without "actually" fully switching
227      * to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the
228      * change.
229      */
230     fun showAodUi()
231 
232     /**
233      * This method should not be used anymore, you should probably use [.isShadeFullyOpen] instead.
234      * It was overused as indicating if shade is open or we're on keyguard/AOD. Moving forward we
235      * should be explicit about the what state we're checking.
236      *
237      * @return if panel is covering the screen, which means we're in expanded shade or keyguard/AOD
238      */
239     @Deprecated(
240         "depends on the state you check, use {@link #isShadeFullyExpanded()},\n" +
241             "{@link #isOnAod()}, {@link #isOnKeyguard()} instead."
242     )
243     fun isFullyExpanded(): Boolean
244 
245     /** Sends an external (e.g. Status Bar) touch event to the Shade touch handler. */
246     fun handleExternalTouch(event: MotionEvent): Boolean
247 
248     /** Starts tracking a shade expansion gesture that originated from the status bar. */
249     fun startTrackingExpansionFromStatusBar()
250 
251     /**
252      * Performs haptic feedback from a view with a haptic feedback constant.
253      *
254      * The implementation of this method should use the [android.view.View.performHapticFeedback]
255      * method with the provided constant.
256      *
257      * @param[constant] One of [android.view.HapticFeedbackConstants]
258      */
259     fun performHapticFeedback(constant: Int)
260 
261     // ******* End Keyguard Section *********
262 
263     /** Returns the ShadeHeadsUpTracker. */
264     val shadeHeadsUpTracker: ShadeHeadsUpTracker
265 
266     /** Returns the ShadeFoldAnimator. */
267     val shadeFoldAnimator: ShadeFoldAnimator
268 
269     companion object {
270         /**
271          * Returns a multiplicative factor to use when determining the falsing threshold for touches
272          * on the shade. The factor will be larger when the device is waking up due to a touch or
273          * gesture.
274          */
275         @JvmStatic
276         fun getFalsingThresholdFactor(wakefulness: WakefulnessModel): Float {
277             return if (wakefulness.isDeviceInteractiveFromTapOrGesture()) 1.5f else 1.0f
278         }
279 
280         const val WAKEUP_ANIMATION_DELAY_MS = 250
281         const val FLING_MAX_LENGTH_SECONDS = 0.6f
282         const val FLING_SPEED_UP_FACTOR = 0.6f
283         const val FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f
284         const val FLING_CLOSING_SPEED_UP_FACTOR = 0.6f
285 
286         /** Fling expanding QS. */
287         const val FLING_EXPAND = 0
288 
289         /** Fling collapsing QS, potentially stopping when QS becomes QQS. */
290         const val FLING_COLLAPSE = 1
291 
292         /** Fling until QS is completely hidden. */
293         const val FLING_HIDE = 2
294     }
295 }
296 
297 /** Manages listeners for when users begin expanding the shade from a HUN. */
298 interface ShadeHeadsUpTracker {
299     /** Add a listener for when the user starts expanding the shade from a HUN. */
300     fun addTrackingHeadsUpListener(listener: Consumer<ExpandableNotificationRow>)
301 
302     /** Remove a listener for when the user starts expanding the shade from a HUN. */
303     fun removeTrackingHeadsUpListener(listener: Consumer<ExpandableNotificationRow>)
304 
305     /** Set the controller for the appearance of HUNs in the icon area and the header itself. */
306     fun setHeadsUpAppearanceController(headsUpAppearanceController: HeadsUpAppearanceController?)
307 
308     /** The notification row that was touched to initiate shade expansion. */
309     val trackedHeadsUpNotification: ExpandableNotificationRow?
310 }
311 
312 /** Handles the lifecycle of the shade's animation that happens when folding a foldable. */
313 interface ShadeFoldAnimator {
314     /** Updates the views to the initial state for the fold to AOD animation. */
315     fun prepareFoldToAodAnimation()
316 
317     /**
318      * Starts fold to AOD animation.
319      *
320      * @param startAction invoked when the animation starts.
321      * @param endAction invoked when the animation finishes, also if it was cancelled.
322      * @param cancelAction invoked when the animation is cancelled, before endAction.
323      */
324     fun startFoldToAodAnimation(startAction: Runnable, endAction: Runnable, cancelAction: Runnable)
325 
326     /** Cancels fold to AOD transition and resets view state. */
327     fun cancelFoldToAodAnimation()
328 
329     /** Returns the main view of the shade. */
330     val view: ViewGroup?
331 }
332 
333 /**
334  * An interface that provides the current state of the notification panel and related views, which
335  * is needed to calculate [KeyguardStatusBarView]'s state in [KeyguardStatusBarViewController].
336  */
337 interface ShadeViewStateProvider {
338     /** Returns the expanded height of the panel view. */
339     val panelViewExpandedHeight: Float
340 
341     /**
342      * Returns true if heads up should be visible.
343      *
344      * TODO(b/138786270): If HeadsUpAppearanceController was injectable, we could inject it into
345      *   [KeyguardStatusBarViewController] and remove this method.
346      */
347     fun shouldHeadsUpBeVisible(): Boolean
348 
349     /** Return the fraction of the shade that's expanded, when in lockscreen. */
350     val lockscreenShadeDragProgress: Float
351 }
352 
353 /** Listens for when touch tracking begins. */
354 interface TrackingStartedListener {
355     fun onTrackingStarted()
356 }
357 
358 /** Listens for when shade begins opening or finishes closing. */
359 interface OpenCloseListener {
360     /** Called when the shade finishes closing. */
361     fun onClosingFinished()
362 
363     /** Called when the shade starts opening. */
364     fun onOpenStarted()
365 }
366