Loading services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +12 −0 Original line number Diff line number Diff line Loading @@ -177,6 +177,14 @@ public abstract class InputMethodManagerInternal { */ public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId); /** * Switch the keyboard layout in response to a keyboard shortcut. * * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the * previous subtype. */ public abstract void switchKeyboardLayout(int direction); /** * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ Loading Loading @@ -256,6 +264,10 @@ public abstract class InputMethodManagerInternal { @Override public void maybeFinishStylusHandwriting() { } @Override public void switchKeyboardLayout(int direction) { } }; /** Loading services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +24 −8 Original line number Diff line number Diff line Loading @@ -3964,6 +3964,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (!calledWithValidTokenLocked(token)) { return false; } return switchToNextInputMethodLocked(token, onlyCurrentIme); } } @GuardedBy("ImfLock.class") private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) { final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked( onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype); if (nextSubtype == null) { Loading @@ -3973,7 +3979,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub nextSubtype.mSubtypeId); return true; } } @BinderThread private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) { Loading Loading @@ -5622,6 +5627,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mHandler.removeMessages(MSG_FINISH_HANDWRITING); mHandler.obtainMessage(MSG_FINISH_HANDWRITING).sendToTarget(); } @Override public void switchKeyboardLayout(int direction) { synchronized (ImfLock.class) { if (direction > 0) { switchToNextInputMethodLocked(null /* token */, true /* onlyCurrentIme */); } else { // TODO(b/258853866): Support backwards switching. } } } } @BinderThread Loading services/core/java/com/android/server/policy/PhoneWindowManager.java +22 −10 Original line number Diff line number Diff line Loading @@ -89,6 +89,7 @@ import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; Loading Loading @@ -161,6 +162,7 @@ import android.service.dreams.IDreamManager; import android.service.vr.IPersistentVrStateCallbacks; import android.speech.RecognizerIntent; import android.telecom.TelecomManager; import android.util.FeatureFlagUtils; import android.util.Log; import android.util.MutableBoolean; import android.util.PrintWriterPrinter; Loading Loading @@ -396,7 +398,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { ActivityTaskManagerInternal mActivityTaskManagerInternal; AutofillManagerInternal mAutofillManagerInternal; InputManagerInternal mInputManagerInternal; InputMethodManagerInternal mInputMethodManagerInternal; DreamManagerInternal mDreamManagerInternal; PowerManagerInternal mPowerManagerInternal; IStatusBarService mStatusBarService; Loading Loading @@ -656,6 +657,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_HANDLE_ALL_APPS = 22; private static final int MSG_LAUNCH_ASSIST = 23; private static final int MSG_RINGER_TOGGLE_CHORD = 24; private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25; private class PolicyHandler extends Handler { @Override Loading Loading @@ -726,6 +728,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_SCREENSHOT_CHORD: handleScreenShot(msg.arg1); break; case MSG_SWITCH_KEYBOARD_LAYOUT: handleSwitchKeyboardLayout(msg.arg1, msg.arg2); break; } } } Loading Loading @@ -1022,14 +1027,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: { if (mDismissImeOnBackKeyPressed) { if (mInputMethodManagerInternal == null) { mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); } if (mInputMethodManagerInternal != null) { mInputMethodManagerInternal.hideCurrentInputMethod( InputMethodManagerInternal.get().hideCurrentInputMethod( SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME); } } else { shortPressPowerGoHome(); } Loading Loading @@ -3162,7 +3161,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (down && repeatCount == 0) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); sendSwitchKeyboardLayout(event, direction); return key_consumed; } break; Loading Loading @@ -3412,7 +3411,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK, KeyEvent.META_CTRL_ON)) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); sendSwitchKeyboardLayout(event, direction); return true; } } Loading @@ -3438,6 +3437,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, int direction) { mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, event.getDeviceId(), direction).sendToTarget(); } private void handleSwitchKeyboardLayout(int deviceId, int direction) { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) { InputMethodManagerInternal.get().switchKeyboardLayout(direction); } else { mWindowManagerFuncs.switchKeyboardLayout(deviceId, direction); } } private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent, int policyFlags) { int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags); Loading services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +3 −1 Original line number Diff line number Diff line Loading @@ -127,7 +127,7 @@ public class InputMethodManagerServiceTestBase { mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) .mockStatic(LocalServices.class) .spyStatic(LocalServices.class) .mockStatic(ServiceManager.class) .mockStatic(SystemServerInitThreadPool.class) .startMocking(); Loading Loading @@ -212,6 +212,7 @@ public class InputMethodManagerServiceTestBase { new InputMethodManagerService.Lifecycle(mContext, mInputMethodManagerService); // Public local InputMethodManagerService. LocalServices.removeServiceForTest(InputMethodManagerInternal.class); lifecycle.onStart(); try { // After this boot phase, services can broadcast Intents. Loading @@ -237,6 +238,7 @@ public class InputMethodManagerServiceTestBase { if (mMockingSession != null) { mMockingSession.finishMocking(); } LocalServices.removeServiceForTest(InputMethodManagerInternal.class); } protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput) Loading services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java 0 → 100644 +39 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.inputmethod; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class SwitchKeyboardLayoutTest extends InputMethodManagerServiceTestBase { @Test public void testSwitchToNextKeyboardLayout() { ExtendedMockito.spyOn(mInputMethodManagerService.mSwitchingController); InputMethodManagerInternal.get().switchKeyboardLayout(1); verify(mInputMethodManagerService.mSwitchingController) .getNextInputMethodLocked(eq(true) /* onlyCurrentIme */, any(), any()); } } Loading
services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +12 −0 Original line number Diff line number Diff line Loading @@ -177,6 +177,14 @@ public abstract class InputMethodManagerInternal { */ public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId); /** * Switch the keyboard layout in response to a keyboard shortcut. * * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the * previous subtype. */ public abstract void switchKeyboardLayout(int direction); /** * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ Loading Loading @@ -256,6 +264,10 @@ public abstract class InputMethodManagerInternal { @Override public void maybeFinishStylusHandwriting() { } @Override public void switchKeyboardLayout(int direction) { } }; /** Loading
services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +24 −8 Original line number Diff line number Diff line Loading @@ -3964,6 +3964,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (!calledWithValidTokenLocked(token)) { return false; } return switchToNextInputMethodLocked(token, onlyCurrentIme); } } @GuardedBy("ImfLock.class") private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) { final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked( onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype); if (nextSubtype == null) { Loading @@ -3973,7 +3979,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub nextSubtype.mSubtypeId); return true; } } @BinderThread private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) { Loading Loading @@ -5622,6 +5627,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mHandler.removeMessages(MSG_FINISH_HANDWRITING); mHandler.obtainMessage(MSG_FINISH_HANDWRITING).sendToTarget(); } @Override public void switchKeyboardLayout(int direction) { synchronized (ImfLock.class) { if (direction > 0) { switchToNextInputMethodLocked(null /* token */, true /* onlyCurrentIme */); } else { // TODO(b/258853866): Support backwards switching. } } } } @BinderThread Loading
services/core/java/com/android/server/policy/PhoneWindowManager.java +22 −10 Original line number Diff line number Diff line Loading @@ -89,6 +89,7 @@ import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; Loading Loading @@ -161,6 +162,7 @@ import android.service.dreams.IDreamManager; import android.service.vr.IPersistentVrStateCallbacks; import android.speech.RecognizerIntent; import android.telecom.TelecomManager; import android.util.FeatureFlagUtils; import android.util.Log; import android.util.MutableBoolean; import android.util.PrintWriterPrinter; Loading Loading @@ -396,7 +398,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { ActivityTaskManagerInternal mActivityTaskManagerInternal; AutofillManagerInternal mAutofillManagerInternal; InputManagerInternal mInputManagerInternal; InputMethodManagerInternal mInputMethodManagerInternal; DreamManagerInternal mDreamManagerInternal; PowerManagerInternal mPowerManagerInternal; IStatusBarService mStatusBarService; Loading Loading @@ -656,6 +657,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_HANDLE_ALL_APPS = 22; private static final int MSG_LAUNCH_ASSIST = 23; private static final int MSG_RINGER_TOGGLE_CHORD = 24; private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25; private class PolicyHandler extends Handler { @Override Loading Loading @@ -726,6 +728,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_SCREENSHOT_CHORD: handleScreenShot(msg.arg1); break; case MSG_SWITCH_KEYBOARD_LAYOUT: handleSwitchKeyboardLayout(msg.arg1, msg.arg2); break; } } } Loading Loading @@ -1022,14 +1027,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: { if (mDismissImeOnBackKeyPressed) { if (mInputMethodManagerInternal == null) { mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); } if (mInputMethodManagerInternal != null) { mInputMethodManagerInternal.hideCurrentInputMethod( InputMethodManagerInternal.get().hideCurrentInputMethod( SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME); } } else { shortPressPowerGoHome(); } Loading Loading @@ -3162,7 +3161,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (down && repeatCount == 0) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); sendSwitchKeyboardLayout(event, direction); return key_consumed; } break; Loading Loading @@ -3412,7 +3411,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK, KeyEvent.META_CTRL_ON)) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); sendSwitchKeyboardLayout(event, direction); return true; } } Loading @@ -3438,6 +3437,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, int direction) { mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, event.getDeviceId(), direction).sendToTarget(); } private void handleSwitchKeyboardLayout(int deviceId, int direction) { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) { InputMethodManagerInternal.get().switchKeyboardLayout(direction); } else { mWindowManagerFuncs.switchKeyboardLayout(deviceId, direction); } } private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent, int policyFlags) { int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags); Loading
services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +3 −1 Original line number Diff line number Diff line Loading @@ -127,7 +127,7 @@ public class InputMethodManagerServiceTestBase { mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) .mockStatic(LocalServices.class) .spyStatic(LocalServices.class) .mockStatic(ServiceManager.class) .mockStatic(SystemServerInitThreadPool.class) .startMocking(); Loading Loading @@ -212,6 +212,7 @@ public class InputMethodManagerServiceTestBase { new InputMethodManagerService.Lifecycle(mContext, mInputMethodManagerService); // Public local InputMethodManagerService. LocalServices.removeServiceForTest(InputMethodManagerInternal.class); lifecycle.onStart(); try { // After this boot phase, services can broadcast Intents. Loading @@ -237,6 +238,7 @@ public class InputMethodManagerServiceTestBase { if (mMockingSession != null) { mMockingSession.finishMocking(); } LocalServices.removeServiceForTest(InputMethodManagerInternal.class); } protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput) Loading
services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java 0 → 100644 +39 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.inputmethod; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class SwitchKeyboardLayoutTest extends InputMethodManagerServiceTestBase { @Test public void testSwitchToNextKeyboardLayout() { ExtendedMockito.spyOn(mInputMethodManagerService.mSwitchingController); InputMethodManagerInternal.get().switchKeyboardLayout(1); verify(mInputMethodManagerService.mSwitchingController) .getNextInputMethodLocked(eq(true) /* onlyCurrentIme */, any(), any()); } }