Loading services/core/java/com/android/server/policy/ModifierShortcutManager.java +410 −175 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package com.android.server.policy; import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser; import static com.android.hardware.input.Flags.modifierShortcutManagerRefactor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.role.RoleManager; Loading @@ -37,6 +39,7 @@ import android.text.TextUtils; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.LongSparseArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.KeyCharacterMap; Loading Loading @@ -81,8 +84,8 @@ public class ModifierShortcutManager { private static final String ATTRIBUTE_SHIFT = "shift"; private static final String ATTRIBUTE_ROLE = "role"; private final SparseArray<Intent> mIntentShortcuts = new SparseArray<>(); private final SparseArray<Intent> mShiftShortcuts = new SparseArray<>(); private final SparseArray<Intent> mCategoryShortcuts = new SparseArray<>(); private final SparseArray<Intent> mShiftCategoryShortcuts = new SparseArray<>(); private final SparseArray<String> mRoleShortcuts = new SparseArray<String>(); private final SparseArray<String> mShiftRoleShortcuts = new SparseArray<String>(); private final Map<String, Intent> mRoleIntents = new HashMap<String, Intent>(); Loading Loading @@ -127,6 +130,7 @@ public class ModifierShortcutManager { private boolean mSearchKeyShortcutPending = false; private boolean mConsumeSearchKeyUp = true; private UserHandle mCurrentUser; private final Map<Pair<Character, Boolean>, Bookmark> mBookmarks = new HashMap<>(); ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) { mContext = context; Loading @@ -134,7 +138,14 @@ public class ModifierShortcutManager { RoleManager rm = mContext.getSystemService(RoleManager.class); rm.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(), (String roleName, UserHandle user) -> { if (modifierShortcutManagerRefactor()) { mBookmarks.values().stream().filter(b -> b instanceof RoleBookmark && ((RoleBookmark) b).getRole().equals(roleName)) .forEach(Bookmark::clearIntent); } else { mRoleIntents.remove(roleName); } }, UserHandle.ALL); mCurrentUser = currentUser; mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); Loading @@ -146,9 +157,27 @@ public class ModifierShortcutManager { // Role based shortcuts may resolve to different apps for different users // so clear the cache. clearRoleIntents(); clearComponentIntents(); } void clearRoleIntents() { if (modifierShortcutManagerRefactor()) { mBookmarks.values().stream().filter(b -> b instanceof RoleBookmark).forEach(Bookmark::clearIntent); } else { mRoleIntents.clear(); } } void clearComponentIntents() { if (modifierShortcutManagerRefactor()) { mBookmarks.values().stream().filter(b -> b instanceof ComponentBookmark).forEach(Bookmark::clearIntent); } else { mComponentIntents.clear(); } } /** * Gets the shortcut intent for a given keycode+modifier. Make sure you Loading Loading @@ -176,14 +205,35 @@ public class ModifierShortcutManager { Intent shortcutIntent = null; // If the Shift key is pressed, then search for the shift shortcuts. SparseArray<Intent> shortcutMap = isShiftOn ? mShiftShortcuts : mIntentShortcuts; // First try the exact keycode (with modifiers). int shortcutChar = kcm.get(keyCode, metaState); if (shortcutChar == 0) { return null; } if (modifierShortcutManagerRefactor()) { Bookmark bookmark = mBookmarks.get(new Pair<>((char) shortcutChar, isShiftOn)); if (bookmark == null) { // Next try the primary character on that key. shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode)); if (shortcutChar == 0) { return null; } bookmark = mBookmarks.get(new Pair<>((char) shortcutChar, isShiftOn)); } if (bookmark != null) { Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; shortcutIntent = bookmark.getIntent(context); } else { Log.d(TAG, "No bookmark found for " + (isShiftOn ? "SHIFT+" : "") + (char) shortcutChar); } } else { // If the Shift key is pressed, then search for the shift shortcuts. SparseArray<Intent> shortcutMap = isShiftOn ? mShiftCategoryShortcuts : mCategoryShortcuts; shortcutIntent = shortcutMap.get(shortcutChar); if (shortcutIntent == null) { Loading Loading @@ -215,14 +265,13 @@ public class ModifierShortcutManager { } } } } return shortcutIntent; } private Intent getRoleLaunchIntent(String role) { Intent intent = mRoleIntents.get(role); if (intent == null) { Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; @Nullable private static Intent getRoleLaunchIntent(Context context, String role) { Intent intent = null; RoleManager rm = context.getSystemService(RoleManager.class); PackageManager pm = context.getPackageManager(); if (rm.isRoleAvailable(role)) { Loading @@ -231,22 +280,36 @@ public class ModifierShortcutManager { intent = pm.getLaunchIntentForPackage(rolePackage); if (intent != null) { intent.putExtra(EXTRA_ROLE, role); mRoleIntents.put(role, intent); } else { Log.w(TAG, "No launch intent for role " + role); } } else { Log.w(TAG, "No default application for role " + role); Log.w(TAG, "No default application for role " + role + " user=" + context.getUser()); } } else { Log.w(TAG, "Role " + role + " is not available."); } return intent; } @Nullable private Intent getRoleLaunchIntent(String role) { Intent intent = mRoleIntents.get(role); if (intent == null) { Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; intent = getRoleLaunchIntent(context, role); if (intent != null) { mRoleIntents.put(role, intent); } } return intent; } private void loadShortcuts() { try { XmlResourceParser parser = mContext.getResources().getXml(R.xml.bookmarks); XmlUtils.beginDocument(parser, TAG_BOOKMARKS); Loading Loading @@ -276,9 +339,27 @@ public class ModifierShortcutManager { continue; } final int shortcutChar = shortcutName.charAt(0); final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true")); final Intent intent; if (modifierShortcutManagerRefactor()) { final char shortcutChar = shortcutName.charAt(0); Bookmark bookmark = null; if (packageName != null && className != null) { bookmark = new ComponentBookmark( shortcutChar, isShiftShortcut, packageName, className); } else if (categoryName != null) { bookmark = new CategoryBookmark( shortcutChar, isShiftShortcut, categoryName); } else if (roleName != null) { bookmark = new RoleBookmark(shortcutChar, isShiftShortcut, roleName); } if (bookmark != null) { Log.d(TAG, "adding shortcut " + bookmark + "shift=" + isShiftShortcut + " char=" + shortcutChar); mBookmarks.put(new Pair<>(shortcutChar, isShiftShortcut), bookmark); } } else { final int shortcutChar = shortcutName.charAt(0); if (packageName != null && className != null) { if (roleName != null || categoryName != null) { Log.w(TAG, "Cannot specify role or category when package and class" Loading @@ -287,16 +368,22 @@ public class ModifierShortcutManager { continue; } if (modifierShortcutManagerMultiuser()) { ComponentName componentName = new ComponentName(packageName, className); ComponentName componentName = new ComponentName(packageName, className); if (isShiftShortcut) { mShiftComponentShortcuts.put(shortcutChar, componentName); } else { mComponentShortcuts.put(shortcutChar, componentName); } continue; } else { intent = resolveComponentNameIntent(packageName, className); Intent intent = resolveComponentNameIntent(packageName, className); if (isShiftShortcut) { mShiftCategoryShortcuts.put(shortcutChar, intent); } else { mCategoryShortcuts.put(shortcutChar, intent); } } continue; } else if (categoryName != null) { if (roleName != null) { Log.w(TAG, "Cannot specify role bookmark when category is present for" Loading @@ -304,10 +391,18 @@ public class ModifierShortcutManager { + " category= " + categoryName); continue; } intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName); Intent intent = Intent.makeMainSelectorActivity( Intent.ACTION_MAIN, categoryName); if (intent == null) { Log.w(TAG, "Null selector intent for " + categoryName); } else { if (isShiftShortcut) { mShiftCategoryShortcuts.put(shortcutChar, intent); } else { mCategoryShortcuts.put(shortcutChar, intent); } } continue; } else if (roleName != null) { // We can't resolve the role at the time of this file being parsed as the // device hasn't finished booting, so we will look it up lazily. Loading @@ -322,11 +417,6 @@ public class ModifierShortcutManager { + ": missing package/class, category or role attributes"); continue; } if (isShiftShortcut) { mShiftShortcuts.put(shortcutChar, intent); } else { mIntentShortcuts.put(shortcutChar, intent); } } } catch (XmlPullParserException | IOException e) { Loading @@ -336,6 +426,9 @@ public class ModifierShortcutManager { @Nullable private Intent resolveComponentNameIntent(ComponentName componentName) { if (modifierShortcutManagerRefactor()) { return null; } else { Intent intent = mComponentIntents.get(componentName); if (intent == null) { intent = resolveComponentNameIntent( Loading @@ -346,11 +439,22 @@ public class ModifierShortcutManager { } return intent; } } @Nullable private Intent resolveComponentNameIntent(String packageName, String className) { if (modifierShortcutManagerRefactor()) { return null; } else { Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; return resolveComponentNameIntent(context, packageName, className); } } @Nullable private static Intent resolveComponentNameIntent( Context context, String packageName, String className) { PackageManager pm = context.getPackageManager(); int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE; if (!modifierShortcutManagerMultiuser()) { Loading Loading @@ -562,17 +666,30 @@ public class ModifierShortcutManager { */ public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) { List<KeyboardShortcutInfo> shortcuts = new ArrayList(); for (int i = 0; i < mIntentShortcuts.size(); i++) { if (modifierShortcutManagerRefactor()) { for (Bookmark b : mBookmarks.values()) { KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mIntentShortcuts.keyAt(i)), mIntentShortcuts.valueAt(i), false); b.getShortcutChar(), b.getIntent(mContext), b.isShift()); if (info != null) { shortcuts.add(info); } } } else { for (int i = 0; i < mCategoryShortcuts.size(); i++) { KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mCategoryShortcuts.keyAt(i)), mCategoryShortcuts.valueAt(i), false); if (info != null) { shortcuts.add(info); } } for (int i = 0; i < mShiftShortcuts.size(); i++) { for (int i = 0; i < mShiftCategoryShortcuts.size(); i++) { KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mShiftShortcuts.keyAt(i)), mShiftShortcuts.valueAt(i), true); (char) (mShiftCategoryShortcuts.keyAt(i)), mShiftCategoryShortcuts.valueAt(i), true); if (info != null) { shortcuts.add(info); } Loading @@ -581,7 +698,9 @@ public class ModifierShortcutManager { for (int i = 0; i < mRoleShortcuts.size(); i++) { String role = mRoleShortcuts.valueAt(i); KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), false); (char) (mRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), false); if (info != null) { shortcuts.add(info); } Loading @@ -590,7 +709,9 @@ public class ModifierShortcutManager { for (int i = 0; i < mShiftRoleShortcuts.size(); i++) { String role = mShiftRoleShortcuts.valueAt(i); KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mShiftRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), true); (char) (mShiftRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), true); if (info != null) { shortcuts.add(info); } Loading Loading @@ -619,7 +740,7 @@ public class ModifierShortcutManager { } } } } return new KeyboardShortcutGroup( mContext.getString(R.string.keyboard_shortcut_group_applications), shortcuts); Loading Loading @@ -800,7 +921,29 @@ public class ModifierShortcutManager { void dump(String prefix, PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", prefix); ipw.println("ModifierShortcutManager shortcuts:"); if (modifierShortcutManagerRefactor()) { ipw.increaseIndent(); for (Bookmark b : mBookmarks.values()) { boolean isShift = b.isShift(); char shortcutChar = b.getShortcutChar(); Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; Intent intent = b.getIntent(context); ipw.print(isShift ? "SHIFT+" : ""); ipw.println(shortcutChar + " " + intent); ipw.increaseIndent(); ipw.increaseIndent(); KeyboardShortcutInfo info = shortcutInfoFromIntent(shortcutChar, intent, isShift); if (info != null) { ipw.println("Resolves to: " + info.getLabel()); } else { ipw.println("<No KeyboardShortcutInfo available for this shortcut>"); } ipw.decreaseIndent(); ipw.decreaseIndent(); } } else { ipw.increaseIndent(); ipw.println("Roles"); ipw.increaseIndent(); Loading @@ -821,15 +964,15 @@ public class ModifierShortcutManager { ipw.decreaseIndent(); ipw.println("Selectors"); ipw.increaseIndent(); for (int i = 0; i < mIntentShortcuts.size(); i++) { char shortcutChar = (char) mIntentShortcuts.keyAt(i); Intent intent = mIntentShortcuts.valueAt(i); for (int i = 0; i < mCategoryShortcuts.size(); i++) { char shortcutChar = (char) mCategoryShortcuts.keyAt(i); Intent intent = mCategoryShortcuts.valueAt(i); ipw.println(shortcutChar + " " + intent); } for (int i = 0; i < mShiftShortcuts.size(); i++) { char shortcutChar = (char) mShiftShortcuts.keyAt(i); Intent intent = mShiftShortcuts.valueAt(i); for (int i = 0; i < mShiftCategoryShortcuts.size(); i++) { char shortcutChar = (char) mShiftCategoryShortcuts.keyAt(i); Intent intent = mShiftCategoryShortcuts.valueAt(i); ipw.println("SHIFT+" + shortcutChar + " " + intent); } Loading @@ -854,3 +997,95 @@ public class ModifierShortcutManager { } } } private abstract static class Bookmark { private final char mShortcutChar; private final boolean mShift; protected Intent mIntent; Bookmark(char shortcutChar, boolean shift) { mShortcutChar = shortcutChar; mShift = shift; } public char getShortcutChar() { return mShortcutChar; } public boolean isShift() { return mShift; } public abstract Intent getIntent(Context context); public void clearIntent() { mIntent = null; } } private static final class RoleBookmark extends Bookmark { private final String mRole; RoleBookmark(char shortcutChar, boolean shift, String role) { super(shortcutChar, shift); mRole = role; } public String getRole() { return mRole; } @Nullable @Override public Intent getIntent(Context context) { if (mIntent != null) { return mIntent; } mIntent = getRoleLaunchIntent(context, mRole); return mIntent; } } private static final class CategoryBookmark extends Bookmark { private final String mCategory; CategoryBookmark(char shortcutChar, boolean shift, String category) { super(shortcutChar, shift); mCategory = category; } @NonNull @Override public Intent getIntent(Context context) { if (mIntent != null) { return mIntent; } mIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, mCategory); return mIntent; } } private static final class ComponentBookmark extends Bookmark { private final String mPackageName; private final String mClassName; ComponentBookmark( char shortcutChar, boolean shift, String packageName, String className) { super(shortcutChar, shift); mPackageName = packageName; mClassName = className; } @Nullable @Override public Intent getIntent(Context context) { if (mIntent != null) { return mIntent; } mIntent = resolveComponentNameIntent(context, mPackageName, mClassName); return mIntent; } } } services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java +10 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ 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.flag.junit.SetFlagsRule; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; Loading @@ -50,6 +52,8 @@ 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; Loading @@ -62,7 +66,13 @@ import java.util.Collections; */ @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; Loading services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java +5 −10 Original line number Diff line number Diff line Loading @@ -44,26 +44,21 @@ import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Intent; import android.os.RemoteException; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.SparseArray; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @Presubmit @SmallTest @EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR) public class ModifierShortcutTests extends ShortcutKeyTestBase { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); private static final SparseArray<String> INTENT_SHORTCUTS = new SparseArray<>(); private static final SparseArray<String> ROLE_SHORTCUTS = new SparseArray<>(); static { Loading Loading @@ -258,7 +253,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * META+CTRL+BACKSPACE for taking a bugreport when the flag is enabled. */ @Test @RequiresFlagsEnabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) public void testTakeBugReport_flagEnabled() throws RemoteException { sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0); mPhoneWindowManager.assertTakeBugreport(true); Loading @@ -268,7 +263,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * META+CTRL+BACKSPACE for taking a bugreport does nothing when the flag is disabledd. */ @Test @RequiresFlagsDisabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) @DisableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) public void testTakeBugReport_flagDisabled() throws RemoteException { sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0); mPhoneWindowManager.assertTakeBugreport(false); Loading Loading
services/core/java/com/android/server/policy/ModifierShortcutManager.java +410 −175 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package com.android.server.policy; import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser; import static com.android.hardware.input.Flags.modifierShortcutManagerRefactor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.role.RoleManager; Loading @@ -37,6 +39,7 @@ import android.text.TextUtils; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.LongSparseArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.KeyCharacterMap; Loading Loading @@ -81,8 +84,8 @@ public class ModifierShortcutManager { private static final String ATTRIBUTE_SHIFT = "shift"; private static final String ATTRIBUTE_ROLE = "role"; private final SparseArray<Intent> mIntentShortcuts = new SparseArray<>(); private final SparseArray<Intent> mShiftShortcuts = new SparseArray<>(); private final SparseArray<Intent> mCategoryShortcuts = new SparseArray<>(); private final SparseArray<Intent> mShiftCategoryShortcuts = new SparseArray<>(); private final SparseArray<String> mRoleShortcuts = new SparseArray<String>(); private final SparseArray<String> mShiftRoleShortcuts = new SparseArray<String>(); private final Map<String, Intent> mRoleIntents = new HashMap<String, Intent>(); Loading Loading @@ -127,6 +130,7 @@ public class ModifierShortcutManager { private boolean mSearchKeyShortcutPending = false; private boolean mConsumeSearchKeyUp = true; private UserHandle mCurrentUser; private final Map<Pair<Character, Boolean>, Bookmark> mBookmarks = new HashMap<>(); ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) { mContext = context; Loading @@ -134,7 +138,14 @@ public class ModifierShortcutManager { RoleManager rm = mContext.getSystemService(RoleManager.class); rm.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(), (String roleName, UserHandle user) -> { if (modifierShortcutManagerRefactor()) { mBookmarks.values().stream().filter(b -> b instanceof RoleBookmark && ((RoleBookmark) b).getRole().equals(roleName)) .forEach(Bookmark::clearIntent); } else { mRoleIntents.remove(roleName); } }, UserHandle.ALL); mCurrentUser = currentUser; mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); Loading @@ -146,9 +157,27 @@ public class ModifierShortcutManager { // Role based shortcuts may resolve to different apps for different users // so clear the cache. clearRoleIntents(); clearComponentIntents(); } void clearRoleIntents() { if (modifierShortcutManagerRefactor()) { mBookmarks.values().stream().filter(b -> b instanceof RoleBookmark).forEach(Bookmark::clearIntent); } else { mRoleIntents.clear(); } } void clearComponentIntents() { if (modifierShortcutManagerRefactor()) { mBookmarks.values().stream().filter(b -> b instanceof ComponentBookmark).forEach(Bookmark::clearIntent); } else { mComponentIntents.clear(); } } /** * Gets the shortcut intent for a given keycode+modifier. Make sure you Loading Loading @@ -176,14 +205,35 @@ public class ModifierShortcutManager { Intent shortcutIntent = null; // If the Shift key is pressed, then search for the shift shortcuts. SparseArray<Intent> shortcutMap = isShiftOn ? mShiftShortcuts : mIntentShortcuts; // First try the exact keycode (with modifiers). int shortcutChar = kcm.get(keyCode, metaState); if (shortcutChar == 0) { return null; } if (modifierShortcutManagerRefactor()) { Bookmark bookmark = mBookmarks.get(new Pair<>((char) shortcutChar, isShiftOn)); if (bookmark == null) { // Next try the primary character on that key. shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode)); if (shortcutChar == 0) { return null; } bookmark = mBookmarks.get(new Pair<>((char) shortcutChar, isShiftOn)); } if (bookmark != null) { Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; shortcutIntent = bookmark.getIntent(context); } else { Log.d(TAG, "No bookmark found for " + (isShiftOn ? "SHIFT+" : "") + (char) shortcutChar); } } else { // If the Shift key is pressed, then search for the shift shortcuts. SparseArray<Intent> shortcutMap = isShiftOn ? mShiftCategoryShortcuts : mCategoryShortcuts; shortcutIntent = shortcutMap.get(shortcutChar); if (shortcutIntent == null) { Loading Loading @@ -215,14 +265,13 @@ public class ModifierShortcutManager { } } } } return shortcutIntent; } private Intent getRoleLaunchIntent(String role) { Intent intent = mRoleIntents.get(role); if (intent == null) { Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; @Nullable private static Intent getRoleLaunchIntent(Context context, String role) { Intent intent = null; RoleManager rm = context.getSystemService(RoleManager.class); PackageManager pm = context.getPackageManager(); if (rm.isRoleAvailable(role)) { Loading @@ -231,22 +280,36 @@ public class ModifierShortcutManager { intent = pm.getLaunchIntentForPackage(rolePackage); if (intent != null) { intent.putExtra(EXTRA_ROLE, role); mRoleIntents.put(role, intent); } else { Log.w(TAG, "No launch intent for role " + role); } } else { Log.w(TAG, "No default application for role " + role); Log.w(TAG, "No default application for role " + role + " user=" + context.getUser()); } } else { Log.w(TAG, "Role " + role + " is not available."); } return intent; } @Nullable private Intent getRoleLaunchIntent(String role) { Intent intent = mRoleIntents.get(role); if (intent == null) { Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; intent = getRoleLaunchIntent(context, role); if (intent != null) { mRoleIntents.put(role, intent); } } return intent; } private void loadShortcuts() { try { XmlResourceParser parser = mContext.getResources().getXml(R.xml.bookmarks); XmlUtils.beginDocument(parser, TAG_BOOKMARKS); Loading Loading @@ -276,9 +339,27 @@ public class ModifierShortcutManager { continue; } final int shortcutChar = shortcutName.charAt(0); final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true")); final Intent intent; if (modifierShortcutManagerRefactor()) { final char shortcutChar = shortcutName.charAt(0); Bookmark bookmark = null; if (packageName != null && className != null) { bookmark = new ComponentBookmark( shortcutChar, isShiftShortcut, packageName, className); } else if (categoryName != null) { bookmark = new CategoryBookmark( shortcutChar, isShiftShortcut, categoryName); } else if (roleName != null) { bookmark = new RoleBookmark(shortcutChar, isShiftShortcut, roleName); } if (bookmark != null) { Log.d(TAG, "adding shortcut " + bookmark + "shift=" + isShiftShortcut + " char=" + shortcutChar); mBookmarks.put(new Pair<>(shortcutChar, isShiftShortcut), bookmark); } } else { final int shortcutChar = shortcutName.charAt(0); if (packageName != null && className != null) { if (roleName != null || categoryName != null) { Log.w(TAG, "Cannot specify role or category when package and class" Loading @@ -287,16 +368,22 @@ public class ModifierShortcutManager { continue; } if (modifierShortcutManagerMultiuser()) { ComponentName componentName = new ComponentName(packageName, className); ComponentName componentName = new ComponentName(packageName, className); if (isShiftShortcut) { mShiftComponentShortcuts.put(shortcutChar, componentName); } else { mComponentShortcuts.put(shortcutChar, componentName); } continue; } else { intent = resolveComponentNameIntent(packageName, className); Intent intent = resolveComponentNameIntent(packageName, className); if (isShiftShortcut) { mShiftCategoryShortcuts.put(shortcutChar, intent); } else { mCategoryShortcuts.put(shortcutChar, intent); } } continue; } else if (categoryName != null) { if (roleName != null) { Log.w(TAG, "Cannot specify role bookmark when category is present for" Loading @@ -304,10 +391,18 @@ public class ModifierShortcutManager { + " category= " + categoryName); continue; } intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName); Intent intent = Intent.makeMainSelectorActivity( Intent.ACTION_MAIN, categoryName); if (intent == null) { Log.w(TAG, "Null selector intent for " + categoryName); } else { if (isShiftShortcut) { mShiftCategoryShortcuts.put(shortcutChar, intent); } else { mCategoryShortcuts.put(shortcutChar, intent); } } continue; } else if (roleName != null) { // We can't resolve the role at the time of this file being parsed as the // device hasn't finished booting, so we will look it up lazily. Loading @@ -322,11 +417,6 @@ public class ModifierShortcutManager { + ": missing package/class, category or role attributes"); continue; } if (isShiftShortcut) { mShiftShortcuts.put(shortcutChar, intent); } else { mIntentShortcuts.put(shortcutChar, intent); } } } catch (XmlPullParserException | IOException e) { Loading @@ -336,6 +426,9 @@ public class ModifierShortcutManager { @Nullable private Intent resolveComponentNameIntent(ComponentName componentName) { if (modifierShortcutManagerRefactor()) { return null; } else { Intent intent = mComponentIntents.get(componentName); if (intent == null) { intent = resolveComponentNameIntent( Loading @@ -346,11 +439,22 @@ public class ModifierShortcutManager { } return intent; } } @Nullable private Intent resolveComponentNameIntent(String packageName, String className) { if (modifierShortcutManagerRefactor()) { return null; } else { Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; return resolveComponentNameIntent(context, packageName, className); } } @Nullable private static Intent resolveComponentNameIntent( Context context, String packageName, String className) { PackageManager pm = context.getPackageManager(); int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE; if (!modifierShortcutManagerMultiuser()) { Loading Loading @@ -562,17 +666,30 @@ public class ModifierShortcutManager { */ public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) { List<KeyboardShortcutInfo> shortcuts = new ArrayList(); for (int i = 0; i < mIntentShortcuts.size(); i++) { if (modifierShortcutManagerRefactor()) { for (Bookmark b : mBookmarks.values()) { KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mIntentShortcuts.keyAt(i)), mIntentShortcuts.valueAt(i), false); b.getShortcutChar(), b.getIntent(mContext), b.isShift()); if (info != null) { shortcuts.add(info); } } } else { for (int i = 0; i < mCategoryShortcuts.size(); i++) { KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mCategoryShortcuts.keyAt(i)), mCategoryShortcuts.valueAt(i), false); if (info != null) { shortcuts.add(info); } } for (int i = 0; i < mShiftShortcuts.size(); i++) { for (int i = 0; i < mShiftCategoryShortcuts.size(); i++) { KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mShiftShortcuts.keyAt(i)), mShiftShortcuts.valueAt(i), true); (char) (mShiftCategoryShortcuts.keyAt(i)), mShiftCategoryShortcuts.valueAt(i), true); if (info != null) { shortcuts.add(info); } Loading @@ -581,7 +698,9 @@ public class ModifierShortcutManager { for (int i = 0; i < mRoleShortcuts.size(); i++) { String role = mRoleShortcuts.valueAt(i); KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), false); (char) (mRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), false); if (info != null) { shortcuts.add(info); } Loading @@ -590,7 +709,9 @@ public class ModifierShortcutManager { for (int i = 0; i < mShiftRoleShortcuts.size(); i++) { String role = mShiftRoleShortcuts.valueAt(i); KeyboardShortcutInfo info = shortcutInfoFromIntent( (char) (mShiftRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), true); (char) (mShiftRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), true); if (info != null) { shortcuts.add(info); } Loading Loading @@ -619,7 +740,7 @@ public class ModifierShortcutManager { } } } } return new KeyboardShortcutGroup( mContext.getString(R.string.keyboard_shortcut_group_applications), shortcuts); Loading Loading @@ -800,7 +921,29 @@ public class ModifierShortcutManager { void dump(String prefix, PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", prefix); ipw.println("ModifierShortcutManager shortcuts:"); if (modifierShortcutManagerRefactor()) { ipw.increaseIndent(); for (Bookmark b : mBookmarks.values()) { boolean isShift = b.isShift(); char shortcutChar = b.getShortcutChar(); Context context = modifierShortcutManagerMultiuser() ? mContext.createContextAsUser(mCurrentUser, 0) : mContext; Intent intent = b.getIntent(context); ipw.print(isShift ? "SHIFT+" : ""); ipw.println(shortcutChar + " " + intent); ipw.increaseIndent(); ipw.increaseIndent(); KeyboardShortcutInfo info = shortcutInfoFromIntent(shortcutChar, intent, isShift); if (info != null) { ipw.println("Resolves to: " + info.getLabel()); } else { ipw.println("<No KeyboardShortcutInfo available for this shortcut>"); } ipw.decreaseIndent(); ipw.decreaseIndent(); } } else { ipw.increaseIndent(); ipw.println("Roles"); ipw.increaseIndent(); Loading @@ -821,15 +964,15 @@ public class ModifierShortcutManager { ipw.decreaseIndent(); ipw.println("Selectors"); ipw.increaseIndent(); for (int i = 0; i < mIntentShortcuts.size(); i++) { char shortcutChar = (char) mIntentShortcuts.keyAt(i); Intent intent = mIntentShortcuts.valueAt(i); for (int i = 0; i < mCategoryShortcuts.size(); i++) { char shortcutChar = (char) mCategoryShortcuts.keyAt(i); Intent intent = mCategoryShortcuts.valueAt(i); ipw.println(shortcutChar + " " + intent); } for (int i = 0; i < mShiftShortcuts.size(); i++) { char shortcutChar = (char) mShiftShortcuts.keyAt(i); Intent intent = mShiftShortcuts.valueAt(i); for (int i = 0; i < mShiftCategoryShortcuts.size(); i++) { char shortcutChar = (char) mShiftCategoryShortcuts.keyAt(i); Intent intent = mShiftCategoryShortcuts.valueAt(i); ipw.println("SHIFT+" + shortcutChar + " " + intent); } Loading @@ -854,3 +997,95 @@ public class ModifierShortcutManager { } } } private abstract static class Bookmark { private final char mShortcutChar; private final boolean mShift; protected Intent mIntent; Bookmark(char shortcutChar, boolean shift) { mShortcutChar = shortcutChar; mShift = shift; } public char getShortcutChar() { return mShortcutChar; } public boolean isShift() { return mShift; } public abstract Intent getIntent(Context context); public void clearIntent() { mIntent = null; } } private static final class RoleBookmark extends Bookmark { private final String mRole; RoleBookmark(char shortcutChar, boolean shift, String role) { super(shortcutChar, shift); mRole = role; } public String getRole() { return mRole; } @Nullable @Override public Intent getIntent(Context context) { if (mIntent != null) { return mIntent; } mIntent = getRoleLaunchIntent(context, mRole); return mIntent; } } private static final class CategoryBookmark extends Bookmark { private final String mCategory; CategoryBookmark(char shortcutChar, boolean shift, String category) { super(shortcutChar, shift); mCategory = category; } @NonNull @Override public Intent getIntent(Context context) { if (mIntent != null) { return mIntent; } mIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, mCategory); return mIntent; } } private static final class ComponentBookmark extends Bookmark { private final String mPackageName; private final String mClassName; ComponentBookmark( char shortcutChar, boolean shift, String packageName, String className) { super(shortcutChar, shift); mPackageName = packageName; mClassName = className; } @Nullable @Override public Intent getIntent(Context context) { if (mIntent != null) { return mIntent; } mIntent = resolveComponentNameIntent(context, mPackageName, mClassName); return mIntent; } } }
services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java +10 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ 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.flag.junit.SetFlagsRule; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; Loading @@ -50,6 +52,8 @@ 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; Loading @@ -62,7 +66,13 @@ import java.util.Collections; */ @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; Loading
services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java +5 −10 Original line number Diff line number Diff line Loading @@ -44,26 +44,21 @@ import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Intent; import android.os.RemoteException; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.SparseArray; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @Presubmit @SmallTest @EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR) public class ModifierShortcutTests extends ShortcutKeyTestBase { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); private static final SparseArray<String> INTENT_SHORTCUTS = new SparseArray<>(); private static final SparseArray<String> ROLE_SHORTCUTS = new SparseArray<>(); static { Loading Loading @@ -258,7 +253,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * META+CTRL+BACKSPACE for taking a bugreport when the flag is enabled. */ @Test @RequiresFlagsEnabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) public void testTakeBugReport_flagEnabled() throws RemoteException { sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0); mPhoneWindowManager.assertTakeBugreport(true); Loading @@ -268,7 +263,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * META+CTRL+BACKSPACE for taking a bugreport does nothing when the flag is disabledd. */ @Test @RequiresFlagsDisabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) @DisableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) public void testTakeBugReport_flagDisabled() throws RemoteException { sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0); mPhoneWindowManager.assertTakeBugreport(false); Loading