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 17 package com.android.systemui.keyguard.domain.interactor 18 19 import android.content.Context 20 import android.media.AudioManager 21 import android.view.KeyEvent 22 import com.android.systemui.back.domain.interactor.BackActionInteractor 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor.Companion.handleAction 25 import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper 26 import com.android.systemui.plugins.statusbar.StatusBarStateController 27 import com.android.systemui.shade.ShadeController 28 import com.android.systemui.statusbar.StatusBarState 29 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager 30 import javax.inject.Inject 31 32 /** Handles key events arriving when the keyguard is showing or device is dozing. */ 33 @SysUISingleton 34 class KeyguardKeyEventInteractor 35 @Inject 36 constructor( 37 private val context: Context, 38 private val statusBarStateController: StatusBarStateController, 39 private val keyguardInteractor: KeyguardInteractor, 40 private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, 41 private val shadeController: ShadeController, 42 private val mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper, 43 private val backActionInteractor: BackActionInteractor, 44 ) { 45 46 fun dispatchKeyEvent(event: KeyEvent): Boolean { 47 if (statusBarStateController.isDozing) { 48 when (event.keyCode) { 49 KeyEvent.KEYCODE_VOLUME_DOWN, 50 KeyEvent.KEYCODE_VOLUME_UP -> return dispatchVolumeKeyEvent(event) 51 } 52 } 53 54 if (event.handleAction()) { 55 when (event.keyCode) { 56 KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent() 57 KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent() 58 } 59 } 60 return false 61 } 62 63 /** 64 * While IME is active and a BACK event is detected, check with {@link 65 * StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event should be 66 * handled before routing to IME, in order to prevent the user from having to hit back twice to 67 * exit bouncer. 68 */ 69 fun dispatchKeyEventPreIme(event: KeyEvent): Boolean { 70 when (event.keyCode) { 71 KeyEvent.KEYCODE_BACK -> 72 if ( 73 statusBarStateController.state == StatusBarState.KEYGUARD && 74 statusBarKeyguardViewManager.dispatchBackKeyEventPreIme() 75 ) { 76 return backActionInteractor.onBackRequested() 77 } 78 } 79 return false 80 } 81 82 fun interceptMediaKey(event: KeyEvent): Boolean { 83 return statusBarStateController.state == StatusBarState.KEYGUARD && 84 statusBarKeyguardViewManager.interceptMediaKey(event) 85 } 86 87 private fun dispatchMenuKeyEvent(): Boolean { 88 val shouldUnlockOnMenuPressed = 89 isDeviceInteractive() && 90 (statusBarStateController.state != StatusBarState.SHADE) && 91 statusBarKeyguardViewManager.shouldDismissOnMenuPressed() 92 if (shouldUnlockOnMenuPressed) { 93 shadeController.animateCollapseShadeForced() 94 return true 95 } 96 return false 97 } 98 99 private fun dispatchSpaceEvent(): Boolean { 100 if (isDeviceInteractive() && statusBarStateController.state != StatusBarState.SHADE) { 101 shadeController.animateCollapseShadeForced() 102 return true 103 } 104 return false 105 } 106 107 private fun dispatchVolumeKeyEvent(event: KeyEvent): Boolean { 108 mediaSessionLegacyHelperWrapper 109 .getHelper(context) 110 .sendVolumeKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE, true) 111 return true 112 } 113 114 private fun isDeviceInteractive(): Boolean { 115 return keyguardInteractor.wakefulnessModel.value.isDeviceInteractive() 116 } 117 } 118