Merge "Switch IME subtypes with keyboard shortcuts" into udc-dev
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
@@ -256,6 +264,10 @@ public abstract class InputMethodManagerInternal {
|
||||
@Override
|
||||
public void maybeFinishStylusHandwriting() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchKeyboardLayout(int direction) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -4050,17 +4050,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
if (!calledWithValidTokenLocked(token)) {
|
||||
return false;
|
||||
}
|
||||
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
|
||||
onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype);
|
||||
if (nextSubtype == null) {
|
||||
return false;
|
||||
}
|
||||
setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
|
||||
nextSubtype.mSubtypeId);
|
||||
return true;
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
|
||||
nextSubtype.mSubtypeId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
|
||||
synchronized (ImfLock.class) {
|
||||
@@ -5749,6 +5754,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
|
||||
|
||||
@@ -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;
|
||||
@@ -160,6 +161,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.MathUtils;
|
||||
import android.util.MutableBoolean;
|
||||
@@ -398,7 +400,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
ActivityTaskManagerInternal mActivityTaskManagerInternal;
|
||||
AutofillManagerInternal mAutofillManagerInternal;
|
||||
InputManagerInternal mInputManagerInternal;
|
||||
InputMethodManagerInternal mInputMethodManagerInternal;
|
||||
DreamManagerInternal mDreamManagerInternal;
|
||||
PowerManagerInternal mPowerManagerInternal;
|
||||
IStatusBarService mStatusBarService;
|
||||
@@ -659,6 +660,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
|
||||
@@ -729,6 +731,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1025,14 +1030,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();
|
||||
}
|
||||
@@ -3170,7 +3169,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;
|
||||
@@ -3420,7 +3419,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;
|
||||
}
|
||||
}
|
||||
@@ -3446,6 +3445,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);
|
||||
|
||||
@@ -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();
|
||||
@@ -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.
|
||||
@@ -237,6 +238,7 @@ public class InputMethodManagerServiceTestBase {
|
||||
if (mMockingSession != null) {
|
||||
mMockingSession.finishMocking();
|
||||
}
|
||||
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
|
||||
}
|
||||
|
||||
protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ import android.os.RemoteException;
|
||||
import android.os.Vibrator;
|
||||
import android.service.dreams.DreamManagerInternal;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.util.FeatureFlagUtils;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.autofill.AutofillManagerInternal;
|
||||
@@ -76,6 +77,7 @@ import com.android.internal.accessibility.AccessibilityShortcutController;
|
||||
import com.android.server.GestureLauncherService;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.input.InputManagerInternal;
|
||||
import com.android.server.inputmethod.InputMethodManagerInternal;
|
||||
import com.android.server.statusbar.StatusBarManagerInternal;
|
||||
import com.android.server.vr.VrManagerInternal;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
@@ -114,6 +116,7 @@ class TestPhoneWindowManager {
|
||||
@Mock private Vibrator mVibrator;
|
||||
@Mock private PowerManager mPowerManager;
|
||||
@Mock private WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncsImpl;
|
||||
@Mock private InputMethodManagerInternal mInputMethodManagerInternal;
|
||||
@Mock private AudioManagerInternal mAudioManagerInternal;
|
||||
@Mock private SearchManager mSearchManager;
|
||||
|
||||
@@ -184,6 +187,8 @@ class TestPhoneWindowManager {
|
||||
() -> LocalServices.getService(eq(GestureLauncherService.class)));
|
||||
doReturn(null).when(() -> LocalServices.getService(eq(VrManagerInternal.class)));
|
||||
doReturn(null).when(() -> LocalServices.getService(eq(AutofillManagerInternal.class)));
|
||||
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
|
||||
LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal);
|
||||
|
||||
doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class));
|
||||
doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
|
||||
@@ -242,6 +247,7 @@ class TestPhoneWindowManager {
|
||||
|
||||
void tearDown() {
|
||||
mHandlerThread.quitSafely();
|
||||
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
|
||||
mMockitoSession.finishMocking();
|
||||
}
|
||||
|
||||
@@ -417,7 +423,13 @@ class TestPhoneWindowManager {
|
||||
|
||||
void assertSwitchKeyboardLayout(int direction) {
|
||||
waitForIdle();
|
||||
verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction));
|
||||
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
|
||||
verify(mInputMethodManagerInternal).switchKeyboardLayout(eq(direction));
|
||||
verify(mWindowManagerFuncsImpl, never()).switchKeyboardLayout(anyInt(), anyInt());
|
||||
} else {
|
||||
verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction));
|
||||
verify(mInputMethodManagerInternal, never()).switchKeyboardLayout(anyInt());
|
||||
}
|
||||
}
|
||||
|
||||
void assertTakeBugreport() {
|
||||
|
||||
Reference in New Issue
Block a user