Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 977aba93 authored by Arthur Hung's avatar Arthur Hung
Browse files

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
parent 340d1b85
Loading
Loading
Loading
Loading
+143 −0
Original line number Diff line number Diff line
/*
 * 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<String> 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();
    }
}
+69 −0
Original line number Diff line number Diff line
@@ -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<Intent> 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<Intent> 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());
    }
}