• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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 
17 package com.android.systemui.shade
18 
19 import android.view.MotionEvent
20 import com.android.systemui.assist.AssistManager
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Background
23 import com.android.systemui.dagger.qualifiers.Main
24 import com.android.systemui.scene.domain.interactor.SceneInteractor
25 import com.android.systemui.scene.shared.model.SceneFamilies
26 import com.android.systemui.scene.shared.model.Scenes
27 import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
28 import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
29 import com.android.systemui.shade.domain.interactor.ShadeInteractor
30 import com.android.systemui.statusbar.CommandQueue
31 import com.android.systemui.statusbar.NotificationShadeWindowController
32 import com.android.systemui.statusbar.VibratorHelper
33 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
34 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
35 import dagger.Lazy
36 import javax.inject.Inject
37 import kotlinx.coroutines.CoroutineDispatcher
38 import kotlinx.coroutines.CoroutineScope
39 import kotlinx.coroutines.ExperimentalCoroutinesApi
40 import kotlinx.coroutines.delay
41 import kotlinx.coroutines.flow.first
42 import kotlinx.coroutines.launch
43 import kotlinx.coroutines.withContext
44 
45 /**
46  * Implementation of ShadeController backed by scenes instead of NPVC.
47  *
48  * TODO(b/300258424) rename to ShadeControllerImpl and inline/delete all the deprecated methods
49  */
50 @OptIn(ExperimentalCoroutinesApi::class)
51 @SysUISingleton
52 class ShadeControllerSceneImpl
53 @Inject
54 constructor(
55     @Main private val mainDispatcher: CoroutineDispatcher,
56     @Background private val scope: CoroutineScope,
57     private val shadeInteractor: ShadeInteractor,
58     private val sceneInteractor: SceneInteractor,
59     private val notificationStackScrollLayout: NotificationStackScrollLayout,
60     private val vibratorHelper: VibratorHelper,
61     commandQueue: CommandQueue,
62     statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
63     notificationShadeWindowController: NotificationShadeWindowController,
64     assistManagerLazy: Lazy<AssistManager>,
65 ) :
66     BaseShadeControllerImpl(
67         commandQueue,
68         statusBarKeyguardViewManager,
69         notificationShadeWindowController,
70         assistManagerLazy,
71     ) {
72 
73     init {
74         scope.launch {
75             shadeInteractor.isAnyExpanded.collect {
76                 if (!it) {
77                     runPostCollapseActions()
78                 }
79             }
80         }
81     }
82 
83     override fun isShadeEnabled() = shadeInteractor.isShadeEnabled.value
84 
85     override fun isShadeFullyOpen(): Boolean = shadeInteractor.isAnyFullyExpanded.value
86 
87     override fun isExpandingOrCollapsing(): Boolean = shadeInteractor.isUserInteracting.value
88 
89     override fun instantExpandShade() {
90         // Do nothing
91     }
92 
93     override fun instantCollapseShade() {
94         sceneInteractor.snapToScene(
95             SceneFamilies.Home,
96             "hide shade",
97         )
98     }
99 
100     override fun animateCollapseShade(
101         flags: Int,
102         force: Boolean,
103         delayed: Boolean,
104         speedUpFactor: Float
105     ) {
106         if (!force && !shadeInteractor.isAnyExpanded.value) {
107             runPostCollapseActions()
108             return
109         }
110         if (
111             shadeInteractor.isAnyExpanded.value &&
112                 flags and CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL == 0
113         ) {
114             // release focus immediately to kick off focus change transition
115             notificationShadeWindowController.setNotificationShadeFocusable(false)
116             notificationStackScrollLayout.cancelExpandHelper()
117             if (delayed) {
118                 scope.launch {
119                     delay(125)
120                     animateCollapseShadeInternal()
121                 }
122             } else {
123                 animateCollapseShadeInternal()
124             }
125         }
126     }
127 
128     override fun collapseWithDuration(animationDuration: Int) {
129         // TODO(b/300258424) inline this. The only caller uses the default duration.
130         animateCollapseShade()
131     }
132 
133     private fun animateCollapseShadeInternal() {
134         sceneInteractor.changeScene(
135             SceneFamilies.Home, // TODO(b/336581871): add sceneState?
136             "ShadeController.animateCollapseShade",
137             SlightlyFasterShadeCollapse,
138         )
139     }
140 
141     override fun cancelExpansionAndCollapseShade() {
142         // TODO do we need to actually cancel the touch session?
143         animateCollapseShade()
144     }
145 
146     override fun closeShadeIfOpen(): Boolean {
147         if (shadeInteractor.isAnyExpanded.value) {
148             commandQueue.animateCollapsePanels(
149                 CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
150                 true /* force */
151             )
152             assistManagerLazy.get().hideAssist()
153         }
154         return false
155     }
156 
157     override fun collapseShade() {
158         animateCollapseShadeForcedDelayed()
159     }
160 
161     override fun collapseShade(animate: Boolean) {
162         if (animate) {
163             animateCollapseShade()
164         } else {
165             instantCollapseShade()
166         }
167     }
168 
169     override fun collapseOnMainThread() {
170         // TODO if this works with delegation alone, we can deprecate and delete
171         collapseShade()
172     }
173 
174     override fun expandToNotifications() {
175         sceneInteractor.changeScene(
176             SceneFamilies.NotifShade,
177             "ShadeController.animateExpandShade",
178         )
179     }
180 
181     override fun expandToQs() {
182         sceneInteractor.changeScene(SceneFamilies.QuickSettings, "ShadeController.animateExpandQs")
183     }
184 
185     override fun setVisibilityListener(listener: ShadeVisibilityListener) {
186         scope.launch {
187             sceneInteractor.isVisible.collect { isVisible ->
188                 withContext(mainDispatcher) { listener.expandedVisibleChanged(isVisible) }
189             }
190         }
191     }
192 
193     @ExperimentalCoroutinesApi
194     override fun collapseShadeForActivityStart() {
195         if (shadeInteractor.isAnyExpanded.value) {
196             animateCollapseShadeForcedDelayed()
197         } else {
198             runPostCollapseActions()
199         }
200     }
201 
202     override fun postAnimateCollapseShade() {
203         animateCollapseShade()
204     }
205 
206     override fun postAnimateForceCollapseShade() {
207         animateCollapseShadeForced()
208     }
209 
210     override fun postAnimateExpandQs() {
211         expandToQs()
212     }
213 
214     override fun postOnShadeExpanded(action: Runnable) {
215         // TODO verify that clicking "reply" in a work profile notification launches the app
216         // TODO verify that there's not a way to replace and deprecate this method
217         scope.launch {
218             shadeInteractor.isAnyFullyExpanded.first { it }
219             action.run()
220         }
221     }
222 
223     override fun makeExpandedInvisible() {
224         // Do nothing
225     }
226 
227     override fun makeExpandedVisible(force: Boolean) {
228         // Do nothing
229     }
230 
231     override fun isExpandedVisible(): Boolean {
232         return sceneInteractor.currentScene.value != Scenes.Gone
233     }
234 
235     override fun onStatusBarTouch(event: MotionEvent) {
236         // The only call to this doesn't happen with MigrateClocksToBlueprint.isEnabled enabled
237         throw UnsupportedOperationException()
238     }
239 
240     override fun performHapticFeedback(constant: Int) {
241         vibratorHelper.performHapticFeedback(notificationStackScrollLayout, constant)
242     }
243 }
244