From 977aba93f7dc83425ea3740d64135959fba844a0 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Fri, 23 Sep 2022 07:49:18 +0000 Subject: [PATCH] Introduce modifier shortcut key unit tests This includes these tests for phone device: Shortcut will start an intent to launch the specific app by category: Meta + A - launch calculator Meta + B - launch browser Meta + C - launch contacts Meta + E - launch email Meta + L - launch calendar Meta + M - launch maps Meta + P - launch music Meta + S - launch messaging Shortcut will trigger specific behavior: Alt + Tab - show recent apps Ctrl + Space - switch keyboard layout Meta + Space - switch keyboard layout Ctrl + Alt + Z - enable accessibility service Meta + Ctrl + S - take screenshot Meta + N - expand notification panel Meta + Slash - toggle shortcuts menu Meta + Alt - toggle cap lock Bug: 193278327 Test: atest ModifierShortcutTests Change-Id: I713a0cccdf7304e84fa8df71cfd4c4d51305c887 --- .../server/policy/ModifierShortcutTests.java | 143 ++++++++++++++++++ .../server/policy/TestPhoneWindowManager.java | 69 +++++++++ 2 files changed, 212 insertions(+) create mode 100644 services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java new file mode 100644 index 0000000000000..ad47773747d4b --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import static android.view.KeyEvent.KEYCODE_A; +import static android.view.KeyEvent.KEYCODE_ALT_LEFT; +import static android.view.KeyEvent.KEYCODE_B; +import static android.view.KeyEvent.KEYCODE_C; +import static android.view.KeyEvent.KEYCODE_CTRL_LEFT; +import static android.view.KeyEvent.KEYCODE_E; +import static android.view.KeyEvent.KEYCODE_L; +import static android.view.KeyEvent.KEYCODE_M; +import static android.view.KeyEvent.KEYCODE_META_LEFT; +import static android.view.KeyEvent.KEYCODE_N; +import static android.view.KeyEvent.KEYCODE_P; +import static android.view.KeyEvent.KEYCODE_S; +import static android.view.KeyEvent.KEYCODE_SLASH; +import static android.view.KeyEvent.KEYCODE_SPACE; +import static android.view.KeyEvent.KEYCODE_TAB; +import static android.view.KeyEvent.KEYCODE_Z; + +import android.content.Intent; +import android.os.RemoteException; +import android.util.SparseArray; + +import org.junit.Test; + +public class ModifierShortcutTests extends ShortcutKeyTestBase { + private static final SparseArray META_SHORTCUTS = new SparseArray<>(); + static { + META_SHORTCUTS.append(KEYCODE_A, Intent.CATEGORY_APP_CALCULATOR); + META_SHORTCUTS.append(KEYCODE_B, Intent.CATEGORY_APP_BROWSER); + META_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CONTACTS); + META_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL); + META_SHORTCUTS.append(KEYCODE_L, Intent.CATEGORY_APP_CALENDAR); + META_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS); + META_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC); + META_SHORTCUTS.append(KEYCODE_S, Intent.CATEGORY_APP_MESSAGING); + } + + /** + * Test meta+ shortcuts defined in bookmarks.xml. + */ + @Test + public void testMetaShortcuts() { + for (int i = 0; i < META_SHORTCUTS.size(); i++) { + final int keyCode = META_SHORTCUTS.keyAt(i); + final String category = META_SHORTCUTS.valueAt(i); + + sendKeyCombination(new int[]{KEYCODE_META_LEFT, keyCode}, 0); + mPhoneWindowManager.assertLaunchCategory(category); + } + } + + /** + * ALT + TAB to show recent apps. + */ + @Test + public void testAltTab() { + mPhoneWindowManager.overrideStatusBarManagerInternal(); + sendKeyCombination(new int[]{KEYCODE_ALT_LEFT, KEYCODE_TAB}, 0); + mPhoneWindowManager.assertShowRecentApps(); + } + + /** + * CTRL + SPACE to switch keyboard layout. + */ + @Test + public void testCtrlSpace() { + sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, 0); + mPhoneWindowManager.assertSwitchKeyboardLayout(); + } + + /** + * META + SPACE to switch keyboard layout. + */ + @Test + public void testMetaSpace() { + sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SPACE}, 0); + mPhoneWindowManager.assertSwitchKeyboardLayout(); + } + + /** + * CTRL + ALT + Z to enable accessibility service. + */ + @Test + public void testCtrlAltZ() { + sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_ALT_LEFT, KEYCODE_Z}, 0); + mPhoneWindowManager.assertAccessibilityKeychordCalled(); + } + + /** + * META + CTRL+ S to take screenshot. + */ + @Test + public void testMetaCtrlS() { + sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_S}, 0); + mPhoneWindowManager.assertTakeScreenshotCalled(); + } + + /** + * META + N to expand notification panel. + */ + @Test + public void testMetaN() throws RemoteException { + mPhoneWindowManager.overrideExpandNotificationsPanel(); + sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_N}, 0); + mPhoneWindowManager.assertExpandNotification(); + } + + /** + * META + SLASH to toggle shortcuts menu. + */ + @Test + public void testMetaSlash() { + mPhoneWindowManager.overrideStatusBarManagerInternal(); + sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SLASH}, 0); + mPhoneWindowManager.assertToggleShortcutsMenu(); + } + + /** + * META + ALT to toggle Cap Lock. + */ + @Test + public void testMetaAlt() { + sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_ALT_LEFT}, 0); + mPhoneWindowManager.assertToggleCapsLock(); + } +} diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index ee11ac8caf538..fe46c148c6770 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -32,6 +32,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS; @@ -53,6 +54,7 @@ import android.app.AppOpsManager; import android.app.NotificationManager; import android.app.SearchManager; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; @@ -62,6 +64,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; import android.os.PowerManagerInternal; +import android.os.RemoteException; import android.os.Vibrator; import android.service.dreams.DreamManagerInternal; import android.telecom.TelecomManager; @@ -73,12 +76,16 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.internal.accessibility.AccessibilityShortcutController; import com.android.server.GestureLauncherService; import com.android.server.LocalServices; +import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.DisplayPolicy; import com.android.server.wm.DisplayRotation; import com.android.server.wm.WindowManagerInternal; +import junit.framework.Assert; + +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockSettings; import org.mockito.Mockito; @@ -118,6 +125,8 @@ class TestPhoneWindowManager { @Mock private GlobalActions mGlobalActions; @Mock private AccessibilityShortcutController mAccessibilityShortcutController; + @Mock private StatusBarManagerInternal mStatusBarManagerInternal; + private StaticMockitoSession mMockitoSession; private HandlerThread mHandlerThread; private Handler mHandler; @@ -226,6 +235,8 @@ class TestPhoneWindowManager { mPhoneWindowManager.systemBooted(); overrideLaunchAccessibility(); + doReturn(false).when(mPhoneWindowManager).keyguardOn(); + doNothing().when(mContext).startActivityAsUser(any(), any()); } void tearDown() { @@ -310,6 +321,22 @@ class TestPhoneWindowManager { doReturn(true).when(mTelecomManager).endCall(); } + void overrideExpandNotificationsPanel() { + // Can't directly mock on IStatusbarService, use spyOn and override the specific api. + mPhoneWindowManager.getStatusBarService(); + spyOn(mPhoneWindowManager.mStatusBarService); + try { + doNothing().when(mPhoneWindowManager.mStatusBarService).expandNotificationsPanel(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + void overrideStatusBarManagerInternal() { + doReturn(mStatusBarManagerInternal).when( + () -> LocalServices.getService(eq(StatusBarManagerInternal.class))); + } + /** * Below functions will check the policy behavior could be invoked. */ @@ -368,4 +395,46 @@ class TestPhoneWindowManager { waitForIdle(); verify(mSearchManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)).launchAssist(any()); } + + void assertLaunchCategory(String category) { + waitForIdle(); + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).startActivityAsUser(intentCaptor.capture(), any()); + Assert.assertTrue(intentCaptor.getValue().getSelector().hasCategory(category)); + // Reset verifier for next call. + Mockito.reset(mContext); + } + + void assertShowRecentApps() { + waitForIdle(); + verify(mStatusBarManagerInternal).showRecentApps(anyBoolean()); + } + + void assertSwitchKeyboardLayout() { + waitForIdle(); + verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), anyInt()); + } + + void assertTakeBugreport() { + waitForIdle(); + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).sendOrderedBroadcastAsUser(intentCaptor.capture(), any(), any(), any(), + any(), anyInt(), any(), any()); + Assert.assertTrue(intentCaptor.getValue().getAction() == Intent.ACTION_BUG_REPORT); + } + + void assertExpandNotification() throws RemoteException { + waitForIdle(); + verify(mPhoneWindowManager.mStatusBarService).expandNotificationsPanel(); + } + + void assertToggleShortcutsMenu() { + waitForIdle(); + verify(mStatusBarManagerInternal).toggleKeyboardShortcutsMenu(anyInt()); + } + + void assertToggleCapsLock() { + waitForIdle(); + verify(mInputManagerInternal).toggleCapsLock(anyInt()); + } }