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