Loading packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt +47 −1 Original line number Diff line number Diff line Loading @@ -19,11 +19,15 @@ import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.Context import android.content.pm.UserInfo import android.hardware.input.InputManager import android.hardware.input.KeyGestureEvent import android.os.IBinder import android.os.UserHandle import android.view.KeyEvent import android.view.KeyEvent.KEYCODE_N import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL import android.view.ViewConfiguration import com.android.hardware.input.Flags.useKeyGestureEventHandler import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.dagger.qualifiers.Background Loading @@ -47,6 +51,7 @@ constructor( private val optionalBubbles: Optional<Bubbles>, private val userTracker: UserTracker, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val inputManager: InputManager, @Background private val backgroundExecutor: Executor, @NoteTaskEnabledKey private val isEnabled: Boolean, ) { Loading @@ -59,6 +64,7 @@ constructor( if (!isEnabled || optionalBubbles.isEmpty) return initializeHandleSystemKey() initializeKeyGestureEventHandler() initializeOnRoleHoldersChanged() initializeOnUserUnlocked() initializeUserTracker() Loading @@ -72,6 +78,16 @@ constructor( commandQueue.addCallback(callbacks) } /** * Initializes a [InputManager.KeyGestureEventHandler] which will handle shortcuts for opening * the notes role via [NoteTaskController]. */ private fun initializeKeyGestureEventHandler() { if (useKeyGestureEventHandler()) { inputManager.registerKeyGestureEventHandler(callbacks) } } /** * Initializes the [RoleManager] role holder changed listener to ensure [NoteTaskController] * will always update whenever the role holder app changes. Keep in mind that a role may change Loading Loading @@ -110,7 +126,8 @@ constructor( KeyguardUpdateMonitorCallback(), CommandQueue.Callbacks, UserTracker.Callback, OnRoleHoldersChangedListener { OnRoleHoldersChangedListener, InputManager.KeyGestureEventHandler { override fun handleSystemKey(key: KeyEvent) { key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask) Loading @@ -131,6 +148,17 @@ constructor( override fun onProfilesChanged(profiles: List<UserInfo>) { controller.updateNoteTaskForCurrentUserAndManagedProfiles() } override fun handleKeyGestureEvent( event: KeyGestureEvent, focusedToken: IBinder? ): Boolean { return this@NoteTaskInitializer.handleKeyGestureEvent(event) } override fun isKeyGestureSupported(gestureType: Int): Boolean { return this@NoteTaskInitializer.isKeyGestureSupported(gestureType); } } /** Loading Loading @@ -171,6 +199,24 @@ constructor( return !isMultiPress && !isLongPress } private fun handleKeyGestureEvent(event: KeyGestureEvent): Boolean { // This method is on input hot path and should be kept lightweight. Shift all complex // processing onto background executor wherever possible. if (event.keyGestureType != KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES) { return false } debugLog { "handleKeyGestureEvent: Received OPEN_NOTES gesture event from keycodes: " + event.keycodes.contentToString() } backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) } return true } private fun isKeyGestureSupported(gestureType: Int): Boolean { return gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES } companion object { val MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout().toLong() val LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout().toLong() Loading packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt +30 −0 Original line number Diff line number Diff line Loading @@ -17,8 +17,12 @@ package com.android.systemui.notetask import android.app.role.RoleManager import android.app.role.RoleManager.ROLE_NOTES import android.hardware.input.InputManager import android.hardware.input.KeyGestureEvent import android.os.UserHandle import android.os.UserManager import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import android.view.KeyEvent.ACTION_DOWN import android.view.KeyEvent.ACTION_UP Loading @@ -42,6 +46,7 @@ import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock Loading @@ -57,7 +62,11 @@ import org.mockito.MockitoAnnotations.initMocks @RunWith(AndroidJUnit4::class) internal class NoteTaskInitializerTest : SysuiTestCase() { @get:Rule val setFlagsRule = SetFlagsRule() @Mock lateinit var commandQueue: CommandQueue @Mock lateinit var inputManager: InputManager @Mock lateinit var bubbles: Bubbles @Mock lateinit var controller: NoteTaskController @Mock lateinit var roleManager: RoleManager Loading Loading @@ -86,6 +95,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { roleManager = roleManager, userTracker = userTracker, keyguardUpdateMonitor = keyguardMonitor, inputManager = inputManager, backgroundExecutor = executor, ) Loading Loading @@ -171,6 +181,26 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { verify(controller).showNoteTask(any()) } @Test @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) fun initialize_handleKeyGestureEvent() { val gestureEvent = KeyGestureEvent.Builder() .setKeycodes(intArrayOf(KeyEvent.KEYCODE_N)) .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON) .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES) .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) .build() val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) underTest.initialize() val callback = withArgCaptor { verify(inputManager).registerKeyGestureEventHandler(capture()) } assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue() executor.runAllReady() verify(controller).showNoteTask(any()) } @Test fun initialize_userUnlocked_shouldUpdateNoteTask() { whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(false) Loading services/core/java/com/android/server/input/KeyGestureController.java +5 −1 Original line number Diff line number Diff line Loading @@ -259,7 +259,11 @@ final class KeyGestureController { case KeyEvent.KEYCODE_N: if (firstDown && event.isMetaPressed()) { if (event.isCtrlPressed()) { // TODO(b/358569822): Move open notes handling in System UI instead of PWM return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */0); } else { return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, Loading tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +12 −0 Original line number Diff line number Diff line Loading @@ -285,6 +285,18 @@ class KeyGestureControllerTests { KeyEvent.META_META_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( "META + CTRL + N -> Open Notes", intArrayOf( KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_N ), KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, intArrayOf(KeyEvent.KEYCODE_N), KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( "META + CTRL + S -> Take Screenshot", intArrayOf( Loading Loading
packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt +47 −1 Original line number Diff line number Diff line Loading @@ -19,11 +19,15 @@ import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.Context import android.content.pm.UserInfo import android.hardware.input.InputManager import android.hardware.input.KeyGestureEvent import android.os.IBinder import android.os.UserHandle import android.view.KeyEvent import android.view.KeyEvent.KEYCODE_N import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL import android.view.ViewConfiguration import com.android.hardware.input.Flags.useKeyGestureEventHandler import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.dagger.qualifiers.Background Loading @@ -47,6 +51,7 @@ constructor( private val optionalBubbles: Optional<Bubbles>, private val userTracker: UserTracker, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val inputManager: InputManager, @Background private val backgroundExecutor: Executor, @NoteTaskEnabledKey private val isEnabled: Boolean, ) { Loading @@ -59,6 +64,7 @@ constructor( if (!isEnabled || optionalBubbles.isEmpty) return initializeHandleSystemKey() initializeKeyGestureEventHandler() initializeOnRoleHoldersChanged() initializeOnUserUnlocked() initializeUserTracker() Loading @@ -72,6 +78,16 @@ constructor( commandQueue.addCallback(callbacks) } /** * Initializes a [InputManager.KeyGestureEventHandler] which will handle shortcuts for opening * the notes role via [NoteTaskController]. */ private fun initializeKeyGestureEventHandler() { if (useKeyGestureEventHandler()) { inputManager.registerKeyGestureEventHandler(callbacks) } } /** * Initializes the [RoleManager] role holder changed listener to ensure [NoteTaskController] * will always update whenever the role holder app changes. Keep in mind that a role may change Loading Loading @@ -110,7 +126,8 @@ constructor( KeyguardUpdateMonitorCallback(), CommandQueue.Callbacks, UserTracker.Callback, OnRoleHoldersChangedListener { OnRoleHoldersChangedListener, InputManager.KeyGestureEventHandler { override fun handleSystemKey(key: KeyEvent) { key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask) Loading @@ -131,6 +148,17 @@ constructor( override fun onProfilesChanged(profiles: List<UserInfo>) { controller.updateNoteTaskForCurrentUserAndManagedProfiles() } override fun handleKeyGestureEvent( event: KeyGestureEvent, focusedToken: IBinder? ): Boolean { return this@NoteTaskInitializer.handleKeyGestureEvent(event) } override fun isKeyGestureSupported(gestureType: Int): Boolean { return this@NoteTaskInitializer.isKeyGestureSupported(gestureType); } } /** Loading Loading @@ -171,6 +199,24 @@ constructor( return !isMultiPress && !isLongPress } private fun handleKeyGestureEvent(event: KeyGestureEvent): Boolean { // This method is on input hot path and should be kept lightweight. Shift all complex // processing onto background executor wherever possible. if (event.keyGestureType != KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES) { return false } debugLog { "handleKeyGestureEvent: Received OPEN_NOTES gesture event from keycodes: " + event.keycodes.contentToString() } backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) } return true } private fun isKeyGestureSupported(gestureType: Int): Boolean { return gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES } companion object { val MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout().toLong() val LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout().toLong() Loading
packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt +30 −0 Original line number Diff line number Diff line Loading @@ -17,8 +17,12 @@ package com.android.systemui.notetask import android.app.role.RoleManager import android.app.role.RoleManager.ROLE_NOTES import android.hardware.input.InputManager import android.hardware.input.KeyGestureEvent import android.os.UserHandle import android.os.UserManager import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import android.view.KeyEvent.ACTION_DOWN import android.view.KeyEvent.ACTION_UP Loading @@ -42,6 +46,7 @@ import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock Loading @@ -57,7 +62,11 @@ import org.mockito.MockitoAnnotations.initMocks @RunWith(AndroidJUnit4::class) internal class NoteTaskInitializerTest : SysuiTestCase() { @get:Rule val setFlagsRule = SetFlagsRule() @Mock lateinit var commandQueue: CommandQueue @Mock lateinit var inputManager: InputManager @Mock lateinit var bubbles: Bubbles @Mock lateinit var controller: NoteTaskController @Mock lateinit var roleManager: RoleManager Loading Loading @@ -86,6 +95,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { roleManager = roleManager, userTracker = userTracker, keyguardUpdateMonitor = keyguardMonitor, inputManager = inputManager, backgroundExecutor = executor, ) Loading Loading @@ -171,6 +181,26 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { verify(controller).showNoteTask(any()) } @Test @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) fun initialize_handleKeyGestureEvent() { val gestureEvent = KeyGestureEvent.Builder() .setKeycodes(intArrayOf(KeyEvent.KEYCODE_N)) .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON) .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES) .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) .build() val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) underTest.initialize() val callback = withArgCaptor { verify(inputManager).registerKeyGestureEventHandler(capture()) } assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue() executor.runAllReady() verify(controller).showNoteTask(any()) } @Test fun initialize_userUnlocked_shouldUpdateNoteTask() { whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(false) Loading
services/core/java/com/android/server/input/KeyGestureController.java +5 −1 Original line number Diff line number Diff line Loading @@ -259,7 +259,11 @@ final class KeyGestureController { case KeyEvent.KEYCODE_N: if (firstDown && event.isMetaPressed()) { if (event.isCtrlPressed()) { // TODO(b/358569822): Move open notes handling in System UI instead of PWM return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */0); } else { return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, Loading
tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +12 −0 Original line number Diff line number Diff line Loading @@ -285,6 +285,18 @@ class KeyGestureControllerTests { KeyEvent.META_META_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( "META + CTRL + N -> Open Notes", intArrayOf( KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_N ), KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, intArrayOf(KeyEvent.KEYCODE_N), KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( "META + CTRL + S -> Take Screenshot", intArrayOf( Loading