Add support for Meta+Ctrl+N to open Notes task

Test: manual
Bug: 246661404
Change-Id: I6a0e6a2d44d56c1e27c842b51a3c3469ea607af4
This commit is contained in:
Vaibhav Devmurari
2023-03-07 15:48:10 +00:00
parent ecb1be12b9
commit 432041b435
17 changed files with 100 additions and 34 deletions

View File

@@ -427,7 +427,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
method public void expandNotificationsPanel();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getLastSystemKey();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(int);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(@NonNull android.view.KeyEvent);
method public void sendNotificationFeedback(@Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void togglePanel();

View File

@@ -46,6 +46,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
import android.view.KeyEvent;
import android.view.View;
import com.android.internal.statusbar.AppClipsServiceConnector;
@@ -740,7 +741,7 @@ public class StatusBarManager {
*/
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
@TestApi
public void handleSystemKey(int key) {
public void handleSystemKey(@NonNull KeyEvent key) {
try {
final IStatusBarService svc = getService();
if (svc != null) {

View File

@@ -28,6 +28,7 @@ import android.media.INearbyMediaDevicesProvider;
import android.media.MediaRoute2Info;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.view.KeyEvent;
import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.IAddTileResultCallback;
@@ -141,7 +142,7 @@ oneway interface IStatusBar
void addQsTile(in ComponentName tile);
void remQsTile(in ComponentName tile);
void clickQsTile(in ComponentName tile);
void handleSystemKey(in int key);
void handleSystemKey(in KeyEvent key);
/**
* Methods to show toast messages for screen pinning

View File

@@ -29,6 +29,7 @@ import android.media.MediaRoute2Info;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.view.KeyEvent;
import android.service.notification.StatusBarNotification;
import com.android.internal.logging.InstanceId;
@@ -110,7 +111,7 @@ interface IStatusBarService
void remTile(in ComponentName tile);
void clickTile(in ComponentName tile);
@UnsupportedAppUsage
void handleSystemKey(in int key);
void handleSystemKey(in KeyEvent key);
int getLastSystemKey();
/**

View File

@@ -296,7 +296,8 @@ private fun createNoteTaskIntent(info: NoteTaskInfo): Intent =
// EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
// was used to start the note task.
putExtra(Intent.EXTRA_USE_STYLUS_MODE, true)
val useStylusMode = info.entryPoint != NoteTaskEntryPoint.KEYBOARD_SHORTCUT
putExtra(Intent.EXTRA_USE_STYLUS_MODE, useStylusMode)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// We should ensure the note experience can be opened both as a full screen (lockscreen)

View File

@@ -25,7 +25,8 @@ import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity
* An entry point represents where the note task has ben called from. In rare cases, it may
* represent a "re-entry" (i.e., [APP_CLIPS]).
*/
enum class NoteTaskEntryPoint {
enum class
NoteTaskEntryPoint {
/** @see [LaunchNoteTaskActivity] */
WIDGET_PICKER_SHORTCUT,
@@ -38,4 +39,7 @@ enum class NoteTaskEntryPoint {
/** @see [AppClipsTrampolineActivity] */
APP_CLIPS,
/** @see [NoteTaskInitializer.callbacks] */
KEYBOARD_SHORTCUT,
}

View File

@@ -18,6 +18,7 @@ package com.android.systemui.notetask
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
@@ -51,6 +52,7 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent
WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT
QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
APP_CLIPS -> return
KEYBOARD_SHORTCUT -> return
null -> return
}
uiEventLogger.log(event, info.uid, info.packageName)
@@ -70,6 +72,7 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent
WIDGET_PICKER_SHORTCUT -> return
QUICK_AFFORDANCE -> return
APP_CLIPS -> return
KEYBOARD_SHORTCUT -> return
null -> return
}
uiEventLogger.log(event, info.uid, info.packageName)

View File

@@ -41,9 +41,12 @@ constructor(
@VisibleForTesting
val callbacks =
object : CommandQueue.Callbacks {
override fun handleSystemKey(keyCode: Int) {
if (keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
override fun handleSystemKey(key: KeyEvent) {
if (key.keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
controller.showNoteTask(NoteTaskEntryPoint.TAIL_BUTTON)
} else if (key.keyCode == KeyEvent.KEYCODE_N && key.isMetaPressed &&
key.isCtrlPressed) {
controller.showNoteTask(NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
}
}
}

View File

@@ -336,7 +336,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
@Override
public void expandNotificationPanel() {
verifyCallerAndClearCallingIdentity("expandNotificationPanel",
() -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
() -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
}
@Override

View File

@@ -53,6 +53,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.util.Pair;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -302,7 +303,7 @@ public class CommandQueue extends IStatusBar.Stub implements
default void remQsTile(ComponentName tile) { }
default void clickTile(ComponentName tile) { }
default void handleSystemKey(int arg1) { }
default void handleSystemKey(KeyEvent arg1) { }
default void showPinningEnterExitToast(boolean entering) { }
default void showPinningEscapeToast() { }
default void handleShowGlobalActionsMenu() { }
@@ -891,9 +892,9 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
public void handleSystemKey(int key) {
public void handleSystemKey(KeyEvent key) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_HANDLE_SYSTEM_KEY, key, 0).sendToTarget();
mHandler.obtainMessage(MSG_HANDLE_SYSTEM_KEY, key).sendToTarget();
}
}
@@ -1534,7 +1535,7 @@ public class CommandQueue extends IStatusBar.Stub implements
break;
case MSG_HANDLE_SYSTEM_KEY:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).handleSystemKey(msg.arg1);
mCallbacks.get(i).handleSystemKey((KeyEvent) msg.obj);
}
break;
case MSG_SHOW_GLOBAL_ACTIONS:

View File

@@ -308,7 +308,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
* settings. Down action closes the entire panel.
*/
@Override
public void handleSystemKey(int key) {
public void handleSystemKey(KeyEvent key) {
if (CentralSurfaces.SPEW) {
Log.d(CentralSurfaces.TAG, "handleNavigationKey: " + key);
}
@@ -320,11 +320,11 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
// Panels are not available in setup
if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key.getKeyCode()) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
mNotificationPanelViewController.collapse(
false /* delayed */, 1.0f /* speedUpFactor */);
} else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
} else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key.getKeyCode()) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
if (mNotificationPanelViewController.isFullyCollapsed()) {
if (mVibrateOnOpening) {

View File

@@ -295,8 +295,8 @@ public final class WMShell implements
@Override
public void notifyExpandNotification() {
mSysUiMainExecutor.execute(
() -> mCommandQueue.handleSystemKey(
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
() -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
}
});

View File

@@ -369,6 +369,39 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
verifyZeroInteractions(context, bubbles, eventLogger)
}
@Test
fun showNoteTask_keyboardShortcut_shouldStartActivity() {
val expectedInfo =
NOTE_TASK_INFO.copy(
entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT,
isKeyguardLocked = true,
)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTask(
entryPoint = expectedInfo.entryPoint!!,
)
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
intentCaptor.value.let { intent ->
assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
.isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
.isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, true)).isFalse()
}
assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
verify(eventLogger).logNoteTaskOpened(expectedInfo)
verifyZeroInteractions(bubbles)
}
// endregion
// region setNoteTaskShortcutEnabled

View File

@@ -98,14 +98,24 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
// region handleSystemKey
@Test
fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL)
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
}
@Test
fun handleSystemKey_receiveKeyboardShortcut_shouldShowNoteTask() {
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_N, 0, KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON))
verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
}
@Test
fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_UNKNOWN))
verifyZeroInteractions(controller)
}

View File

@@ -33,6 +33,7 @@ import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
@@ -397,9 +398,10 @@ public class CommandQueueTest extends SysuiTestCase {
@Test
public void testHandleSysKey() {
mCommandQueue.handleSystemKey(1);
KeyEvent testEvent = new KeyEvent(1, 1);
mCommandQueue.handleSystemKey(testEvent);
waitForIdleSync();
verify(mCallbacks).handleSystemKey(eq(1));
verify(mCallbacks).handleSystemKey(eq(testEvent));
}
@Test

View File

@@ -729,7 +729,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mAutofillManagerInternal.onBackKeyPressed();
break;
case MSG_SYSTEM_KEY_PRESS:
sendSystemKeyToStatusBar(msg.arg1);
sendSystemKeyToStatusBar((KeyEvent) msg.obj);
break;
case MSG_HANDLE_ALL_APPS:
launchAllAppsAction();
@@ -949,7 +949,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
// Inform the StatusBar; but do not allow it to consume the event.
sendSystemKeyToStatusBarAsync(event.getKeyCode());
sendSystemKeyToStatusBarAsync(event);
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
@@ -3001,7 +3001,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
case KeyEvent.KEYCODE_N:
if (down && event.isMetaPressed()) {
toggleNotificationPanel();
if (event.isCtrlPressed()) {
sendSystemKeyToStatusBarAsync(event);
} else {
toggleNotificationPanel();
}
return key_consumed;
}
break;
@@ -4119,7 +4123,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (down) {
sendSystemKeyToStatusBarAsync(event.getKeyCode());
sendSystemKeyToStatusBarAsync(event);
NotificationManager nm = getNotificationService();
if (nm != null && !mHandleVolumeKeysInWM) {
@@ -4397,7 +4401,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
if (down && mStylusButtonsEnabled) {
sendSystemKeyToStatusBarAsync(keyCode);
sendSystemKeyToStatusBarAsync(event);
}
result &= ~ACTION_PASS_TO_USER;
break;
@@ -4494,7 +4498,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (!mAccessibilityManager.isEnabled()
|| !mAccessibilityManager.sendFingerprintGesture(event.getKeyCode())) {
if (mSystemNavigationKeysEnabled) {
sendSystemKeyToStatusBarAsync(event.getKeyCode());
sendSystemKeyToStatusBarAsync(event);
}
}
}
@@ -4503,11 +4507,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/**
* Notify the StatusBar that a system key was pressed.
*/
private void sendSystemKeyToStatusBar(int keyCode) {
private void sendSystemKeyToStatusBar(KeyEvent key) {
IStatusBarService statusBar = getStatusBarService();
if (statusBar != null) {
try {
statusBar.handleSystemKey(keyCode);
statusBar.handleSystemKey(key);
} catch (RemoteException e) {
// Oh well.
}
@@ -4517,8 +4521,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/**
* Notify the StatusBar that a system key was pressed without blocking the current thread.
*/
private void sendSystemKeyToStatusBarAsync(int keyCode) {
Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyCode, 0);
private void sendSystemKeyToStatusBarAsync(KeyEvent keyEvent) {
Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyEvent);
message.setAsynchronous(true);
mHandler.sendMessage(message);
}

View File

@@ -84,6 +84,7 @@ import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
@@ -902,12 +903,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
public void handleSystemKey(int key) throws RemoteException {
public void handleSystemKey(KeyEvent key) throws RemoteException {
if (!checkCanCollapseStatusBar("handleSystemKey")) {
return;
}
mLastSystemKey = key;
mLastSystemKey = key.getKeyCode();
if (mBar != null) {
try {