summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt126
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
3 files changed, 134 insertions, 45 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index fe1034a6aa32..338d3ed42f95 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -23,6 +23,7 @@ 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.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.dagger.qualifiers.Background
@@ -65,12 +66,6 @@ constructor(
* [NoteTaskController], ensure custom actions can be triggered (i.e., keyboard shortcut).
*/
private fun initializeHandleSystemKey() {
- val callbacks =
- object : CommandQueue.Callbacks {
- override fun handleSystemKey(key: KeyEvent) {
- key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask)
- }
- }
commandQueue.addCallback(callbacks)
}
@@ -134,15 +129,39 @@ constructor(
controller.updateNoteTaskForCurrentUserAndManagedProfiles()
}
}
-}
-/**
- * Maps a [KeyEvent] to a [NoteTaskEntryPoint]. If the [KeyEvent] does not represent a
- * [NoteTaskEntryPoint], returns null.
- */
-private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
- when {
- keyCode == KEYCODE_STYLUS_BUTTON_TAIL -> TAIL_BUTTON
- keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
- else -> null
+ /**
+ * Tracks a [KeyEvent], and determines if it should trigger an action to show the note task.
+ * Returns a [NoteTaskEntryPoint] if an action should be taken, and null otherwise.
+ */
+ private fun KeyEvent.toNoteTaskEntryPointOrNull(): NoteTaskEntryPoint? =
+ when {
+ keyCode == KEYCODE_STYLUS_BUTTON_TAIL && isTailButtonNotesGesture() -> TAIL_BUTTON
+ keyCode == KEYCODE_N && isMetaPressed && isCtrlPressed -> KEYBOARD_SHORTCUT
+ else -> null
+ }
+
+ private var lastStylusButtonTailUpEventTime: Long = -MULTI_PRESS_TIMEOUT
+
+ /**
+ * Perform gesture detection for the stylus tail button to make sure we only show the note task
+ * when there is a single press. Long presses and multi-presses are ignored for now.
+ */
+ private fun KeyEvent.isTailButtonNotesGesture(): Boolean {
+ if (keyCode != KEYCODE_STYLUS_BUTTON_TAIL || action != KeyEvent.ACTION_UP) {
+ return false
+ }
+
+ val isMultiPress = (downTime - lastStylusButtonTailUpEventTime) < MULTI_PRESS_TIMEOUT
+ val isLongPress = (eventTime - downTime) >= LONG_PRESS_TIMEOUT
+ lastStylusButtonTailUpEventTime = eventTime
+ // For now, trigger action immediately on UP of a single press, without waiting for
+ // the multi-press timeout to expire.
+ return !isMultiPress && !isLongPress
}
+
+ companion object {
+ val MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout().toLong()
+ val LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout().toLong()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 95bb3e0a4538..78330078076c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -22,6 +22,8 @@ import android.os.UserManager
import android.testing.AndroidTestingRunner
import android.view.KeyEvent
import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.ACTION_UP
+import android.view.KeyEvent.KEYCODE_N
import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -30,7 +32,6 @@ import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -43,7 +44,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -66,6 +66,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
private val executor = FakeExecutor(FakeSystemClock())
private val userTracker = FakeUserTracker()
+ private val handlerCallbacks = mutableListOf<Runnable>()
@Before
fun setUp() {
@@ -74,19 +75,27 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
}
private fun createUnderTest(
- isEnabled: Boolean,
- bubbles: Bubbles?,
+ isEnabled: Boolean,
+ bubbles: Bubbles?,
): NoteTaskInitializer =
- NoteTaskInitializer(
- controller = controller,
- commandQueue = commandQueue,
- optionalBubbles = Optional.ofNullable(bubbles),
- isEnabled = isEnabled,
- roleManager = roleManager,
- userTracker = userTracker,
- keyguardUpdateMonitor = keyguardMonitor,
- backgroundExecutor = executor,
- )
+ NoteTaskInitializer(
+ controller = controller,
+ commandQueue = commandQueue,
+ optionalBubbles = Optional.ofNullable(bubbles),
+ isEnabled = isEnabled,
+ roleManager = roleManager,
+ userTracker = userTracker,
+ keyguardUpdateMonitor = keyguardMonitor,
+ backgroundExecutor = executor,
+ )
+
+ private fun createKeyEvent(
+ action: Int,
+ code: Int,
+ downTime: Long = 0L,
+ eventTime: Long = 0L,
+ metaState: Int = 0
+ ): KeyEvent = KeyEvent(downTime, eventTime, action, code, 0 /*repeat*/, metaState)
@Test
fun initialize_withUserUnlocked() {
@@ -120,12 +129,12 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@@ -136,18 +145,23 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@Test
fun initialize_handleSystemKey() {
- val expectedKeyEvent = KeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL)
+ val expectedKeyEvent =
+ createKeyEvent(
+ ACTION_DOWN,
+ KEYCODE_N,
+ metaState = KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
underTest.initialize()
val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
@@ -176,7 +190,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
underTest.initialize()
val callback = withArgCaptor {
verify(roleManager)
- .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
+ .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
}
callback.onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle)
@@ -203,4 +217,60 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles()
}
+
+ @Test
+ fun tailButtonGestureDetection_singlePress_shouldShowNoteTaskOnUp() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ verify(controller, never()).showNoteTask(any())
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+ )
+
+ verify(controller).showNoteTask(any())
+ }
+
+ @Test
+ fun tailButtonGestureDetection_doublePress_shouldNotShowNoteTaskTwice() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 99)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 150)
+ )
+
+ verify(controller, times(1)).showNoteTask(any())
+ }
+
+ @Test
+ fun tailButtonGestureDetection_longPress_shouldNotShowNoteTask() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
+
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
+ )
+ callback.handleSystemKey(
+ createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 1000)
+ )
+
+ verify(controller, never()).showNoteTask(any())
+ }
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ca647929c271..d1a4e6008c2a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4559,7 +4559,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
- if (down && mStylusButtonsEnabled) {
+ if (mStylusButtonsEnabled) {
sendSystemKeyToStatusBarAsync(event);
}
result &= ~ACTION_PASS_TO_USER;