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.back.domain.interactor
18 
19 import android.window.BackEvent
20 import android.window.OnBackAnimationCallback
21 import android.window.OnBackInvokedCallback
22 import android.window.OnBackInvokedDispatcher
23 import android.window.WindowOnBackInvokedDispatcher
24 import com.android.systemui.CoreStartable
25 import com.android.systemui.Flags.predictiveBackAnimateShade
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.dagger.qualifiers.Application
28 import com.android.systemui.plugins.statusbar.StatusBarStateController
29 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
30 import com.android.systemui.shade.QuickSettingsController
31 import com.android.systemui.shade.ShadeController
32 import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor
33 import com.android.systemui.statusbar.NotificationShadeWindowController
34 import com.android.systemui.statusbar.StatusBarState
35 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
36 import javax.inject.Inject
37 import kotlinx.coroutines.CoroutineScope
38 import kotlinx.coroutines.launch
39 
40 /** Handles requests to go back either from a button or gesture. */
41 @SysUISingleton
42 class BackActionInteractor
43 @Inject
44 constructor(
45     @Application private val scope: CoroutineScope,
46     private val statusBarStateController: StatusBarStateController,
47     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
48     private val shadeController: ShadeController,
49     private val notificationShadeWindowController: NotificationShadeWindowController,
50     private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
51 ) : CoreStartable {
52 
53     private var isCallbackRegistered = false
54 
55     private val callback =
56         if (predictiveBackAnimateShade()) {
57             /**
58              * New callback that handles back gesture invoked, cancel, progress and provides
59              * feedback via Shade animation.
60              */
61             object : OnBackAnimationCallback {
62                 override fun onBackInvoked() {
63                     onBackRequested()
64                 }
65 
66                 override fun onBackProgressed(backEvent: BackEvent) {
67                     if (shouldBackBeHandled() && shadeBackActionInteractor.canBeCollapsed()) {
68                         shadeBackActionInteractor.onBackProgressed(backEvent.progress)
69                     }
70                 }
71             }
72         } else {
73             OnBackInvokedCallback { onBackRequested() }
74         }
75 
76     private val onBackInvokedDispatcher: WindowOnBackInvokedDispatcher?
77         get() =
78             notificationShadeWindowController.windowRootView?.viewRootImpl?.onBackInvokedDispatcher
79 
80     private lateinit var shadeBackActionInteractor: ShadeBackActionInteractor
81     private lateinit var qsController: QuickSettingsController
82 
83     fun setup(qsController: QuickSettingsController, svController: ShadeBackActionInteractor) {
84         this.qsController = qsController
85         this.shadeBackActionInteractor = svController
86     }
87 
88     override fun start() {
89         scope.launch {
90             windowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive.collect {
91                 visible ->
92                 if (visible) {
93                     registerBackCallback()
94                 } else {
95                     unregisterBackCallback()
96                 }
97             }
98         }
99     }
100 
101     fun shouldBackBeHandled(): Boolean {
102         return statusBarStateController.state != StatusBarState.KEYGUARD &&
103             statusBarStateController.state != StatusBarState.SHADE_LOCKED &&
104             !statusBarKeyguardViewManager.isBouncerShowingOverDream
105     }
106 
107     fun onBackRequested(): Boolean {
108         if (statusBarKeyguardViewManager.canHandleBackPressed()) {
109             statusBarKeyguardViewManager.onBackPressed()
110             return true
111         }
112         if (qsController.isCustomizing) {
113             qsController.closeQsCustomizer()
114             return true
115         }
116         if (qsController.expanded) {
117             shadeBackActionInteractor.animateCollapseQs(false)
118             return true
119         }
120         if (shadeBackActionInteractor.closeUserSwitcherIfOpen()) {
121             return true
122         }
123         if (shouldBackBeHandled()) {
124             if (shadeBackActionInteractor.canBeCollapsed()) {
125                 // this is the Shade dismiss animation, so make sure QQS closes when it ends.
126                 shadeBackActionInteractor.onBackPressed()
127                 shadeController.animateCollapseShade()
128             }
129             return true
130         }
131         return false
132     }
133 
134     private fun registerBackCallback() {
135         if (isCallbackRegistered) {
136             return
137         }
138         onBackInvokedDispatcher?.let {
139             it.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, callback)
140             isCallbackRegistered = true
141         }
142     }
143 
144     private fun unregisterBackCallback() {
145         if (!isCallbackRegistered) {
146             return
147         }
148         onBackInvokedDispatcher?.let {
149             it.unregisterOnBackInvokedCallback(callback)
150             isCallbackRegistered = false
151         }
152     }
153 }
154