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

Commit ae08a2ea authored by Vaibhav Devmurari's avatar Vaibhav Devmurari Committed by Android (Google) Code Review
Browse files

Merge "(2/n) Cleanup modifier shortcut manager" into main

parents 51224c68 4ccdc73b
Loading
Loading
Loading
Loading
+0 −14
Original line number Diff line number Diff line
@@ -27,20 +27,6 @@ flag {
    bug: "286551975"
}

flag {
    name: "modifier_shortcut_dump"
    namespace: "input"
    description: "Dump keyboard shortcuts in dumpsys window"
    bug: "351963350"
}

flag {
    name: "modifier_shortcut_manager_refactor"
    namespace: "input"
    description: "Refactor ModifierShortcutManager internal representation of shortcuts."
    bug: "358603902"
}

flag {
    name: "input_manager_lifecycle_support"
    namespace: "input"
+3 −934

File changed.

Preview size limit exceeded, changes collapsed.

+3 −10
Original line number Diff line number Diff line
@@ -87,9 +87,7 @@ import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
import static com.android.hardware.input.Flags.modifierShortcutDump;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -2411,8 +2409,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
        mSettingsObserver = new SettingsObserver(mHandler);
        mSettingsObserver.observe();
        mModifierShortcutManager = new ModifierShortcutManager(
                mContext, mHandler, UserHandle.of(mCurrentUserId));
        mModifierShortcutManager = new ModifierShortcutManager(mContext,
                UserHandle.of(mCurrentUserId));
        mUiMode = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_defaultUiModeType);
        mHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
@@ -6512,9 +6510,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        if (statusBar != null) {
            statusBar.setCurrentUser(newUserId);
        }
        if (modifierShortcutManagerMultiuser()) {
        mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId));
        }
        if (!inputManagerLifecycleSupport()) {
            mInputManagerInternal.setCurrentUser(newUserId);
        }
@@ -6667,9 +6663,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {

        pw.print(prefix); pw.println("Looper state:");
        mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");
        if (modifierShortcutDump()) {
            mModifierShortcutManager.dump(prefix, pw);
        }
    }

    private static String endcallBehaviorToString(int behavior) {
+0 −82
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2024 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.
-->
<bookmarks xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
    <!-- the key combinations for the following shortcuts must be in sync
         with the key combinations sent by the test in ModifierShortcutTests.java -->
    <bookmark
        role="android.app.role.BROWSER"
        androidprv:keycode="KEYCODE_B"
        androidprv:modifierState="META" />
    <bookmark
        category="android.intent.category.APP_CONTACTS"
        androidprv:keycode="KEYCODE_P"
        androidprv:modifierState="META" />
    <bookmark
        category="android.intent.category.APP_EMAIL"
        androidprv:keycode="KEYCODE_E"
        androidprv:modifierState="META" />
    <bookmark
        category="android.intent.category.APP_CALENDAR"
        androidprv:keycode="KEYCODE_C"
        androidprv:modifierState="META" />
    <bookmark
        category="android.intent.category.APP_MAPS"
        androidprv:keycode="KEYCODE_M"
        androidprv:modifierState="META" />
    <bookmark
        category="android.intent.category.APP_CALCULATOR"
        androidprv:keycode="KEYCODE_U"
        androidprv:modifierState="META" />

    <bookmark
        role="android.app.role.BROWSER"
        androidprv:keycode="KEYCODE_B"
        androidprv:modifierState="META|SHIFT" />

    <bookmark
        category="android.intent.category.APP_CONTACTS"
        androidprv:keycode="KEYCODE_P"
        androidprv:modifierState="META|SHIFT" />

    <bookmark
        package="com.test"
        class="com.test.BookmarkTest"
        androidprv:keycode="KEYCODE_J"
        androidprv:modifierState="META|SHIFT" />

    <!-- The following shortcuts will not be invoked by tests but are here to
         provide test coverage of parsing the different types of shortcut. -->
    <bookmark
        package="com.test"
        class="com.test.BookmarkTest"
        androidprv:keycode="KEYCODE_J"
        androidprv:modifierState="META" />
    <bookmark
        package="com.test2"
        class="com.test.BookmarkTest"
        androidprv:keycode="KEYCODE_D"
        androidprv:modifierState="META" />


    <!-- It's intended that this package/class will NOT resolve so we test the resolution
         failure case. -->
    <bookmark
        package="com.test3"
        class="com.test.BookmarkTest"
        androidprv:keycode="KEYCODE_F"
        androidprv:modifierState="META" />

</bookmarks>
+0 −222
Original line number Diff line number Diff line
/*
 * Copyright 2024 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static org.junit.Assert.assertEquals;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;

import androidx.test.filters.SmallTest;

import com.android.internal.R;

import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;

import java.util.Collections;

/**
 * Test class for {@link ModifierShortcutManager}.
 *
 * Build/Install/Run:
 *  atest ModifierShortcutManagerTests
 */
@Presubmit
@SmallTest
@EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR)
public class ModifierShortcutManagerTests {

    @ClassRule public static final SetFlagsRule.ClassRule SET_FLAGS_CLASS_RULE =
            new SetFlagsRule.ClassRule();
    @Rule public final SetFlagsRule mSetFlagsRule = SET_FLAGS_CLASS_RULE.createSetFlagsRule();

    private ModifierShortcutManager mModifierShortcutManager;
    private Handler mHandler;
    private Context mContext;
    private Resources mResources;
    private PackageManager mPackageManager;

    @Before
    public void setUp() {
        mHandler = new Handler(Looper.getMainLooper());
        mContext = spy(getInstrumentation().getTargetContext());
        mResources = spy(mContext.getResources());
        mPackageManager = spy(mContext.getPackageManager());

        XmlResourceParser testBookmarks = mResources.getXml(
                com.android.frameworks.wmtests.R.xml.bookmarks);

        doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
        when(mContext.getResources()).thenReturn(mResources);
        when(mContext.getPackageManager()).thenReturn(mPackageManager);
        when(mResources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks);
        try {
            // Keep packageName / className in sync with
            // services/tests/wmtests/res/xml/bookmarks.xml
            ActivityInfo testActivityInfo = new ActivityInfo();
            testActivityInfo.applicationInfo = new ApplicationInfo();
            testActivityInfo.packageName =
                    testActivityInfo.applicationInfo.packageName = "com.test";
            ResolveInfo testResolveInfo = new ResolveInfo();
            testResolveInfo.activityInfo = testActivityInfo;

            doReturn(testActivityInfo).when(mPackageManager).getActivityInfo(
                    eq(new ComponentName("com.test", "com.test.BookmarkTest")), anyInt());
            doReturn(testResolveInfo).when(mPackageManager).resolveActivity(any(), anyInt());
            doThrow(new PackageManager.NameNotFoundException("com.test3")).when(mPackageManager)
                    .getActivityInfo(eq(new ComponentName("com.test3", "com.test.BookmarkTest")),
                        anyInt());
        } catch (PackageManager.NameNotFoundException ignored) { }
        doReturn(new String[] { "com.test" }).when(mPackageManager)
                .canonicalToCurrentPackageNames(aryEq(new String[] { "com.test2" }));


        mModifierShortcutManager = new ModifierShortcutManager(
                mContext, mHandler, UserHandle.SYSTEM);
        mModifierShortcutManager.onSystemReady();
    }

    @Test
    public void test_getApplicationLaunchKeyboardShortcuts() {
        // Expected values here determined by the number of shortcuts defined in
        // services/tests/wmtests/res/xml/bookmarks.xml

        // Total valid shortcuts.
        KeyboardShortcutGroup group =
                mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(-1);
        assertEquals(11, group.getItems().size());

        // Total valid shift shortcuts.
        assertEquals(3, group.getItems().stream()
                .filter(s -> s.getModifiers() == (KeyEvent.META_SHIFT_ON | KeyEvent.META_META_ON))
                .count());
    }

    @Test
    public void test_shortcutInfoFromIntent_appIntent() {
        Intent mockIntent = mock(Intent.class);
        ActivityInfo mockActivityInfo = mock(ActivityInfo.class);
        when(mockActivityInfo.loadLabel(any())).thenReturn("label");
        mockActivityInfo.packageName = "android";
        when(mockActivityInfo.getIconResource()).thenReturn(R.drawable.sym_def_app_icon);
        when(mockIntent.resolveActivityInfo(any(), anyInt())).thenReturn(mockActivityInfo);

        KeyboardShortcutInfo info = mModifierShortcutManager.shortcutInfoFromIntent(
                'a', mockIntent, true);

        assertEquals("label", info.getLabel().toString());
        assertEquals('a', info.getBaseCharacter());
        assertEquals(R.drawable.sym_def_app_icon, info.getIcon().getResId());
        assertEquals(KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON, info.getModifiers());

    }

    @Test
    public void test_shortcutInfoFromIntent_resolverIntent() {
        Intent mockIntent = mock(Intent.class);
        Intent mockSelector = mock(Intent.class);
        ActivityInfo mockActivityInfo = mock(ActivityInfo.class);
        mockActivityInfo.name = com.android.internal.app.ResolverActivity.class.getName();
        when(mockIntent.resolveActivityInfo(any(), anyInt())).thenReturn(mockActivityInfo);
        when(mockIntent.getSelector()).thenReturn(mockSelector);
        when(mockSelector.getCategories()).thenReturn(
                Collections.singleton(Intent.CATEGORY_APP_BROWSER));

        KeyboardShortcutInfo info = mModifierShortcutManager.shortcutInfoFromIntent(
                'a', mockIntent, false);

        assertEquals(mContext.getString(R.string.keyboard_shortcut_group_applications_browser),
                info.getLabel().toString());
        assertEquals('a', info.getBaseCharacter());
        assertEquals(R.drawable.sym_def_app_icon, info.getIcon().getResId());
        assertEquals(KeyEvent.META_META_ON, info.getModifiers());

        // validate that an unknown category that we can't present a label to the user for
        // returns null shortcut info.
        when(mockSelector.getCategories()).thenReturn(
                Collections.singleton("not_a_category"));
        assertEquals(null,  mModifierShortcutManager.shortcutInfoFromIntent(
                'a', mockIntent, false));
    }

    @Test
    public void test_getIntentCategoryLabel() {
        assertEquals(
                mContext.getString(R.string.keyboard_shortcut_group_applications_browser),
                ModifierShortcutManager.getIntentCategoryLabel(
                    mContext, Intent.CATEGORY_APP_BROWSER));
        assertEquals(
                mContext.getString(R.string.keyboard_shortcut_group_applications_contacts),
                ModifierShortcutManager.getIntentCategoryLabel(
                    mContext, Intent.CATEGORY_APP_CONTACTS));
        assertEquals(
                mContext.getString(R.string.keyboard_shortcut_group_applications_email),
                ModifierShortcutManager.getIntentCategoryLabel(
                    mContext, Intent.CATEGORY_APP_EMAIL));
        assertEquals(
                mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
                ModifierShortcutManager.getIntentCategoryLabel(
                    mContext, Intent.CATEGORY_APP_CALENDAR));
        assertEquals(
                mContext.getString(R.string.keyboard_shortcut_group_applications_maps),
                ModifierShortcutManager.getIntentCategoryLabel(
                    mContext, Intent.CATEGORY_APP_MAPS));
        assertEquals(
                mContext.getString(R.string.keyboard_shortcut_group_applications_music),
                ModifierShortcutManager.getIntentCategoryLabel(
                    mContext, Intent.CATEGORY_APP_MUSIC));
        assertEquals(
                mContext.getString(R.string.keyboard_shortcut_group_applications_sms),
                ModifierShortcutManager.getIntentCategoryLabel(
                    mContext, Intent.CATEGORY_APP_MESSAGING));
        assertEquals(
                mContext.getString(R.string.keyboard_shortcut_group_applications_calculator),
                ModifierShortcutManager.getIntentCategoryLabel(
                    mContext, Intent.CATEGORY_APP_CALCULATOR));
        assertEquals(null, ModifierShortcutManager.getIntentCategoryLabel(mContext, "foo"));
    }
}
Loading