summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/IWindowManager.aidl13
-rw-r--r--core/java/android/view/WindowManager.java15
-rw-r--r--core/java/android/view/WindowManagerImpl.java26
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java52
10 files changed, 226 insertions, 39 deletions
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 209729b2a38b..6d96bb9423c5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -471,10 +471,23 @@ interface IWindowManager
* Requests Keyboard Shortcuts from the displayed window.
*
* @param receiver The receiver to deliver the results to.
+ * @param deviceId The deviceId of KeyEvent by which this request is triggered, or -1 if it's
+ * not triggered by a KeyEvent.
+ * @see #requestImeKeyboardShortcuts(IResultReceiver, int)
*/
void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId);
/**
+ * Requests Keyboard Shortcuts from currently selected IME.
+ *
+ * @param receiver The receiver to deliver the results to.
+ * @param deviceId The deviceId of KeyEvent by which this request is triggered, or -1 if it's
+ * not triggered by a KeyEvent.
+ * @see #requestAppKeyboardShortcuts(IResultReceiver, int)
+ */
+ void requestImeKeyboardShortcuts(IResultReceiver receiver, int deviceId);
+
+ /**
* Retrieves the current stable insets from the primary display.
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f9f86f80666a..d702367965a1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1384,15 +1384,28 @@ public interface WindowManager extends ViewManager {
"android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
/**
- * Request for keyboard shortcuts to be retrieved asynchronously.
+ * Request for app's keyboard shortcuts to be retrieved asynchronously.
*
* @param receiver The callback to be triggered when the result is ready.
+ * @param deviceId The deviceId of KeyEvent by which this request is triggered, or -1 if it's
+ * not triggered by a KeyEvent.
*
* @hide
*/
public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver, int deviceId);
/**
+ * Request for ime's keyboard shortcuts to be retrieved asynchronously.
+ *
+ * @param receiver The callback to be triggered when the result is ready.
+ * @param deviceId The deviceId of KeyEvent by which this request is triggered, or -1 if it's
+ * not triggered by a KeyEvent.
+ *
+ * @hide
+ */
+ default void requestImeKeyboardShortcuts(KeyboardShortcutsReceiver receiver, int deviceId) {};
+
+ /**
* Return the touch region for the current IME window, or an empty region if there is none.
*
* @return The region of the IME that is accepting touch inputs, or null if there is no IME, no
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index df3e0bb74292..b57163c4e435 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -215,14 +215,36 @@ public final class WindowManagerImpl implements WindowManager {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
List<KeyboardShortcutGroup> result =
- resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY, android.view.KeyboardShortcutGroup.class);
+ resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY,
+ android.view.KeyboardShortcutGroup.class);
receiver.onKeyboardShortcutsReceived(result);
}
};
try {
WindowManagerGlobal.getWindowManagerService()
- .requestAppKeyboardShortcuts(resultReceiver, deviceId);
+ .requestAppKeyboardShortcuts(resultReceiver, deviceId);
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void requestImeKeyboardShortcuts(
+ final KeyboardShortcutsReceiver receiver, int deviceId) {
+ IResultReceiver resultReceiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ List<KeyboardShortcutGroup> result =
+ resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY,
+ android.view.KeyboardShortcutGroup.class);
+ receiver.onKeyboardShortcutsReceived(result);
+ }
+ };
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .requestImeKeyboardShortcuts(resultReceiver, deviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index e4defcfa359f..136d1a25255b 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -3379,6 +3379,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "975028389": {
+ "message": "unable to call receiver for empty keyboard shortcuts",
+ "level": "ERROR",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"975275467": {
"message": "Set animatingExit: reason=remove\/isAnimating win=%s",
"level": "VERBOSE",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 06f43f1eeaa5..39181449aaa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -56,7 +56,6 @@ import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
-import android.view.WindowManager.KeyboardShortcutsReceiver;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Button;
import android.widget.EditText;
@@ -337,6 +336,12 @@ public final class KeyboardShortcutListSearch {
mSpecialCharacterNames.put(KeyEvent.KEYCODE_MUHENKAN, "無変換");
mSpecialCharacterNames.put(KeyEvent.KEYCODE_HENKAN, "変換");
mSpecialCharacterNames.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_ALT_LEFT, "Alt");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_ALT_RIGHT, "Alt");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_CTRL_LEFT, "Ctrl");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_CTRL_RIGHT, "Ctrl");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_SHIFT_LEFT, "Shift");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_SHIFT_RIGHT, "Shift");
mModifierNames.put(KeyEvent.META_META_ON, "Meta");
mModifierNames.put(KeyEvent.META_CTRL_ON, "Ctrl");
@@ -411,27 +416,45 @@ public final class KeyboardShortcutListSearch {
mKeyCharacterMap = mBackupKeyCharacterMap;
}
+ private boolean mAppShortcutsReceived;
+ private boolean mImeShortcutsReceived;
+
@VisibleForTesting
void showKeyboardShortcuts(int deviceId) {
retrieveKeyCharacterMap(deviceId);
- mWindowManager.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {
- @Override
- public void onKeyboardShortcutsReceived(
- final List<KeyboardShortcutGroup> result) {
- // Add specific app shortcuts
- if (result.isEmpty()) {
- mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
- } else {
- mSpecificAppGroup = reMapToKeyboardShortcutMultiMappingGroup(result);
- mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
- }
- mFullShortsGroup.add(SHORTCUT_SYSTEM_INDEX, mSystemGroup);
- mFullShortsGroup.add(SHORTCUT_INPUT_INDEX, mInputGroup);
- mFullShortsGroup.add(SHORTCUT_OPENAPPS_INDEX, mOpenAppsGroup);
- mFullShortsGroup.add(SHORTCUT_SPECIFICAPP_INDEX, mSpecificAppGroup);
- showKeyboardShortcutSearchList(mFullShortsGroup);
+ mAppShortcutsReceived = false;
+ mImeShortcutsReceived = false;
+ mWindowManager.requestAppKeyboardShortcuts(result -> {
+ // Add specific app shortcuts
+ if (result.isEmpty()) {
+ mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
+ } else {
+ mSpecificAppGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result));
+ mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
+ }
+ mAppShortcutsReceived = true;
+ if (mImeShortcutsReceived) {
+ mergeAndShowKeyboardShortcutsGroups();
}
}, deviceId);
+ mWindowManager.requestImeKeyboardShortcuts(result -> {
+ // Add specific Ime shortcuts
+ if (!result.isEmpty()) {
+ mInputGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result));
+ }
+ mImeShortcutsReceived = true;
+ if (mAppShortcutsReceived) {
+ mergeAndShowKeyboardShortcutsGroups();
+ }
+ }, deviceId);
+ }
+
+ private void mergeAndShowKeyboardShortcutsGroups() {
+ mFullShortsGroup.add(SHORTCUT_SYSTEM_INDEX, mSystemGroup);
+ mFullShortsGroup.add(SHORTCUT_INPUT_INDEX, mInputGroup);
+ mFullShortsGroup.add(SHORTCUT_OPENAPPS_INDEX, mOpenAppsGroup);
+ mFullShortsGroup.add(SHORTCUT_SPECIFICAPP_INDEX, mSpecificAppGroup);
+ showKeyboardShortcutSearchList(mFullShortsGroup);
}
// The original data structure is only for 1-to-1 shortcut mapping, so remap the old
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 43fbc7cbae03..a3fd82e9b140 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -56,7 +56,6 @@ import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
-import android.view.WindowManager.KeyboardShortcutsReceiver;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -129,6 +128,9 @@ public final class KeyboardShortcuts {
private KeyCharacterMap mKeyCharacterMap;
private KeyCharacterMap mBackupKeyCharacterMap;
+ @Nullable private List<KeyboardShortcutGroup> mReceivedAppShortcutGroups = null;
+ @Nullable private List<KeyboardShortcutGroup> mReceivedImeShortcutGroups = null;
+
@VisibleForTesting
KeyboardShortcuts(Context context, WindowManager windowManager) {
this.mContext = new ContextThemeWrapper(
@@ -324,6 +326,12 @@ public final class KeyboardShortcuts {
mSpecialCharacterNames.put(KeyEvent.KEYCODE_MUHENKAN, "無変換");
mSpecialCharacterNames.put(KeyEvent.KEYCODE_HENKAN, "変換");
mSpecialCharacterNames.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_ALT_LEFT, "Alt");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_ALT_RIGHT, "Alt");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_CTRL_LEFT, "Ctrl");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_CTRL_RIGHT, "Ctrl");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_SHIFT_LEFT, "Shift");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_SHIFT_RIGHT, "Shift");
mModifierNames.put(KeyEvent.META_META_ON, "Meta");
mModifierNames.put(KeyEvent.META_CTRL_ON, "Ctrl");
@@ -382,18 +390,36 @@ public final class KeyboardShortcuts {
@VisibleForTesting
void showKeyboardShortcuts(int deviceId) {
retrieveKeyCharacterMap(deviceId);
- mWindowManager.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {
- @Override
- public void onKeyboardShortcutsReceived(
- final List<KeyboardShortcutGroup> result) {
- result.add(getSystemShortcuts());
- final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
- if (appShortcuts != null) {
- result.add(appShortcuts);
- }
- showKeyboardShortcutsDialog(result);
- }
- }, deviceId);
+ mReceivedAppShortcutGroups = null;
+ mReceivedImeShortcutGroups = null;
+ mWindowManager.requestAppKeyboardShortcuts(
+ result -> {
+ mReceivedAppShortcutGroups = result;
+ maybeMergeAndShowKeyboardShortcuts();
+ }, deviceId);
+ mWindowManager.requestImeKeyboardShortcuts(
+ result -> {
+ mReceivedImeShortcutGroups = result;
+ maybeMergeAndShowKeyboardShortcuts();
+ }, deviceId);
+ }
+
+ private void maybeMergeAndShowKeyboardShortcuts() {
+ if (mReceivedAppShortcutGroups == null || mReceivedImeShortcutGroups == null) {
+ return;
+ }
+ List<KeyboardShortcutGroup> shortcutGroups = mReceivedAppShortcutGroups;
+ shortcutGroups.addAll(mReceivedImeShortcutGroups);
+ mReceivedAppShortcutGroups = null;
+ mReceivedImeShortcutGroups = null;
+
+ final KeyboardShortcutGroup defaultAppShortcuts =
+ getDefaultApplicationShortcuts();
+ if (defaultAppShortcuts != null) {
+ shortcutGroups.add(defaultAppShortcuts);
+ }
+ shortcutGroups.add(getSystemShortcuts());
+ showKeyboardShortcutsDialog(shortcutGroups);
}
private void dismissKeyboardShortcuts() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
index 109f185c625e..22c9e45d48af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
@@ -76,5 +76,6 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
+ verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
index ea822aa00429..a3ecde0fe976 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -75,5 +75,6 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
+ verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7460d12d20b2..0caff466bee3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7185,15 +7185,45 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
- mContext.enforceCallingOrSelfPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
- "requestAppKeyboardShortcuts");
+ enforceRegisterWindowManagerListenersPermission("requestAppKeyboardShortcuts");
+ WindowState focusedWindow = getFocusedWindow();
+ if (focusedWindow == null || focusedWindow.mClient == null) {
+ notifyReceiverWithEmptyBundle(receiver);
+ return;
+ }
try {
- WindowState focusedWindow = getFocusedWindow();
- if (focusedWindow != null && focusedWindow.mClient != null) {
- getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId);
- }
+ focusedWindow.mClient.requestAppKeyboardShortcuts(receiver, deviceId);
+ } catch (RemoteException e) {
+ notifyReceiverWithEmptyBundle(receiver);
+ }
+ }
+
+ @Override
+ public void requestImeKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
+ enforceRegisterWindowManagerListenersPermission("requestImeKeyboardShortcuts");
+
+ WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
+ if (imeWindow == null || imeWindow.mClient == null) {
+ notifyReceiverWithEmptyBundle(receiver);
+ return;
+ }
+ try {
+ imeWindow.mClient.requestAppKeyboardShortcuts(receiver, deviceId);
+ } catch (RemoteException e) {
+ notifyReceiverWithEmptyBundle(receiver);
+ }
+ }
+
+ private void enforceRegisterWindowManagerListenersPermission(String message) {
+ mContext.enforceCallingOrSelfPermission(REGISTER_WINDOW_MANAGER_LISTENERS, message);
+ }
+
+ private static void notifyReceiverWithEmptyBundle(IResultReceiver receiver) {
+ try {
+ receiver.send(0, Bundle.EMPTY);
} catch (RemoteException e) {
+ ProtoLog.e(WM_ERROR, "unable to call receiver for empty keyboard shortcuts");
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index d3f68185a269..197ee92aa7eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -52,6 +52,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -99,6 +100,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+import com.android.internal.os.IResultReceiver;
import org.junit.Rule;
import org.junit.Test;
@@ -905,6 +907,56 @@ public class WindowManagerServiceTests extends WindowTestsBase {
argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY));
}
+ @Test
+ public void testRequestKeyboardShortcuts_noWindow() {
+ doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
+ doReturn(null).when(mWm).getFocusedWindowLocked();
+ doReturn(null).when(mWm.mRoot).getCurrentInputMethodWindow();
+
+ TestResultReceiver receiver = new TestResultReceiver();
+ mWm.requestAppKeyboardShortcuts(receiver, 0);
+ assertNotNull(receiver.resultData);
+ assertTrue(receiver.resultData.isEmpty());
+
+ receiver = new TestResultReceiver();
+ mWm.requestImeKeyboardShortcuts(receiver, 0);
+ assertNotNull(receiver.resultData);
+ assertTrue(receiver.resultData.isEmpty());
+ }
+
+ @Test
+ public void testRequestKeyboardShortcuts() throws RemoteException {
+ final IWindow window = mock(IWindow.class);
+ final IBinder binder = mock(IBinder.class);
+ doReturn(binder).when(window).asBinder();
+ final WindowState windowState =
+ createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appWin", window);
+ doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
+ doReturn(windowState).when(mWm).getFocusedWindowLocked();
+ doReturn(windowState).when(mWm.mRoot).getCurrentInputMethodWindow();
+
+ TestResultReceiver receiver = new TestResultReceiver();
+ mWm.requestAppKeyboardShortcuts(receiver, 0);
+ mWm.requestImeKeyboardShortcuts(receiver, 0);
+ verify(window, times(2)).requestAppKeyboardShortcuts(receiver, 0);
+ }
+
+ class TestResultReceiver implements IResultReceiver {
+ public android.os.Bundle resultData;
+ private final IBinder mBinder = mock(IBinder.class);
+
+ @Override
+ public void send(int resultCode, android.os.Bundle resultData)
+ throws android.os.RemoteException {
+ this.resultData = resultData;
+ }
+
+ @Override
+ public android.os.IBinder asBinder() {
+ return mBinder;
+ }
+ }
+
private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
when(remoteToken.toWindowContainerToken()).thenReturn(wct);