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.SysUIKeyEventHandler.Companion.handleAction 25 import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper 26 import com.android.systemui.plugins.statusbar.StatusBarStateController 27 import com.android.systemui.power.domain.interactor.PowerInteractor 28 import com.android.systemui.shade.ShadeController 29 import com.android.systemui.statusbar.StatusBarState 30 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager 31 import javax.inject.Inject 32 import kotlinx.coroutines.ExperimentalCoroutinesApi 33 34 /** Handles key events arriving when the keyguard is showing or device is dozing. */ 35 @ExperimentalCoroutinesApi 36 @SysUISingleton 37 class KeyguardKeyEventInteractor 38 @Inject 39 constructor( 40 private val context: Context, 41 private val statusBarStateController: StatusBarStateController, 42 private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, 43 private val shadeController: ShadeController, 44 private val mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper, 45 private val backActionInteractor: BackActionInteractor, 46 private val powerInteractor: PowerInteractor, 47 ) { 48 dispatchKeyEventnull49 fun dispatchKeyEvent(event: KeyEvent): Boolean { 50 if (statusBarStateController.isDozing) { 51 when (event.keyCode) { 52 KeyEvent.KEYCODE_VOLUME_DOWN, 53 KeyEvent.KEYCODE_VOLUME_UP -> return dispatchVolumeKeyEvent(event) 54 } 55 } 56 57 if (event.handleAction()) { 58 if (KeyEvent.isConfirmKey(event.keyCode)) { 59 if (isDeviceAwake()) { 60 return collapseShadeLockedOrShowPrimaryBouncer() 61 } 62 } 63 when (event.keyCode) { 64 KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent() 65 } 66 } 67 return false 68 } 69 70 /** 71 * While IME is active and a BACK event is detected, check with {@link 72 * StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event should be 73 * handled before routing to IME, in order to prevent the user from having to hit back twice to 74 * exit bouncer. 75 */ dispatchKeyEventPreImenull76 fun dispatchKeyEventPreIme(event: KeyEvent): Boolean { 77 when (event.action) { 78 KeyEvent.ACTION_DOWN -> { 79 val device = event.getDevice() 80 if (device != null && device.isFullKeyboard() && device.isExternal()) { 81 powerInteractor.onUserTouch() 82 } 83 } 84 } 85 when (event.keyCode) { 86 KeyEvent.KEYCODE_BACK -> 87 if ( 88 statusBarStateController.state == StatusBarState.KEYGUARD && 89 statusBarKeyguardViewManager.dispatchBackKeyEventPreIme() 90 ) { 91 return backActionInteractor.onBackRequested() 92 } 93 } 94 return false 95 } 96 interceptMediaKeynull97 fun interceptMediaKey(event: KeyEvent): Boolean { 98 return statusBarStateController.state == StatusBarState.KEYGUARD && 99 statusBarKeyguardViewManager.interceptMediaKey(event) 100 } 101 dispatchMenuKeyEventnull102 private fun dispatchMenuKeyEvent(): Boolean { 103 val shouldUnlockOnMenuPressed = 104 isDeviceAwake() && 105 (statusBarStateController.state != StatusBarState.SHADE) && 106 statusBarKeyguardViewManager.shouldDismissOnMenuPressed() 107 if (shouldUnlockOnMenuPressed) { 108 shadeController.animateCollapseShadeForced() 109 return true 110 } 111 return false 112 } 113 collapseShadeLockedOrShowPrimaryBouncernull114 private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean { 115 when (statusBarStateController.state) { 116 StatusBarState.SHADE -> return false 117 StatusBarState.SHADE_LOCKED -> { 118 shadeController.animateCollapseShadeForced() 119 return true 120 } 121 StatusBarState.KEYGUARD -> { 122 statusBarKeyguardViewManager.showPrimaryBouncer(true) 123 return true 124 } 125 } 126 return false 127 } 128 dispatchVolumeKeyEventnull129 private fun dispatchVolumeKeyEvent(event: KeyEvent): Boolean { 130 mediaSessionLegacyHelperWrapper 131 .getHelper(context) 132 .sendVolumeKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE, true) 133 return true 134 } 135 isDeviceAwakenull136 private fun isDeviceAwake(): Boolean { 137 return powerInteractor.detailedWakefulness.value.isAwake() 138 } 139 } 140