Migrate IME switch dialog to WindowContext
Bug: 157888351 Test: manual - reproducible steps mentioned in the bug Test: atest InputMethodMenuControllerTest Test: atest WindowManagerServiceTests Change-Id: I8adb0c569ea6c208aee659603ee1d69df9939267
This commit is contained in:
@@ -2467,6 +2467,12 @@
|
||||
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
|
||||
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
|
||||
},
|
||||
"781471998": {
|
||||
"message": "moveWindowTokenToDisplay: Cannot move to the original display for token: %s",
|
||||
"level": "WARN",
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"791468751": {
|
||||
"message": "Pausing rotation during re-position",
|
||||
"level": "DEBUG",
|
||||
@@ -2671,6 +2677,12 @@
|
||||
"group": "WM_DEBUG_STATES",
|
||||
"at": "com\/android\/server\/wm\/ActivityRecord.java"
|
||||
},
|
||||
"1033274509": {
|
||||
"message": "moveWindowTokenToDisplay: Attempted to move non-existing token: %s",
|
||||
"level": "WARN",
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"1040675582": {
|
||||
"message": "Can't report activity configuration update - client not running, activityRecord=%s",
|
||||
"level": "WARN",
|
||||
@@ -3457,6 +3469,12 @@
|
||||
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
|
||||
"at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
|
||||
},
|
||||
"2060978050": {
|
||||
"message": "moveWindowTokenToDisplay: Attempted to move token: %s to non-exiting displayId=%d",
|
||||
"level": "WARN",
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"2081291430": {
|
||||
"message": "Focus not requested for window=%s because it has no surface",
|
||||
"level": "DEBUG",
|
||||
|
||||
@@ -136,10 +136,7 @@ public class InputMethodMenuController {
|
||||
}
|
||||
}
|
||||
|
||||
final ActivityThread currentThread = ActivityThread.currentActivityThread();
|
||||
final Context settingsContext = new ContextThemeWrapper(
|
||||
currentThread.createSystemUiContext(displayId),
|
||||
com.android.internal.R.style.Theme_DeviceDefault_Settings);
|
||||
final Context settingsContext = getSettingsContext(displayId);
|
||||
mDialogBuilder = new AlertDialog.Builder(settingsContext);
|
||||
mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
|
||||
|
||||
@@ -214,6 +211,32 @@ public class InputMethodMenuController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window context for IME switch dialogs to receive configuration changes.
|
||||
*
|
||||
* This method initializes the window context if it was not initialized. This method also moves
|
||||
* the context to the targeted display if the current display of context is different than
|
||||
* the display specified by {@code displayId}.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public Context getSettingsContext(int displayId) {
|
||||
if (mSettingsContext == null) {
|
||||
final Context systemUiContext = ActivityThread.currentActivityThread()
|
||||
.createSystemUiContext(displayId);
|
||||
final Context windowContext = systemUiContext.createWindowContext(
|
||||
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
|
||||
mSettingsContext = new ContextThemeWrapper(
|
||||
windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
|
||||
mSwitchingDialogToken = mSettingsContext.getActivityToken();
|
||||
}
|
||||
// TODO(b/159767464): register the listener to another display again if window token is not
|
||||
// yet created.
|
||||
if (mSettingsContext.getDisplayId() != displayId) {
|
||||
mWindowManagerInternal.moveWindowTokenToDisplay(mSwitchingDialogToken, displayId);
|
||||
}
|
||||
return mSettingsContext;
|
||||
}
|
||||
|
||||
private boolean isScreenLocked() {
|
||||
return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()
|
||||
&& mKeyguardManager.isKeyguardSecure();
|
||||
|
||||
@@ -597,4 +597,9 @@ public abstract class WindowManagerInternal {
|
||||
* @return The corresponding {@link WindowState#getName()}
|
||||
*/
|
||||
public abstract @Nullable String getImeTargetNameForLogging(int displayId);
|
||||
|
||||
/**
|
||||
* Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
|
||||
*/
|
||||
public abstract void moveWindowTokenToDisplay(IBinder binder, int displayId);
|
||||
}
|
||||
|
||||
@@ -2723,6 +2723,32 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/** @see WindowManagerInternal#moveWindowTokenToDisplay(IBinder, int) */
|
||||
public void moveWindowTokenToDisplay(IBinder binder, int displayId) {
|
||||
synchronized (mGlobalLock) {
|
||||
final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
|
||||
if (dc == null) {
|
||||
ProtoLog.w(WM_ERROR, "moveWindowTokenToDisplay: Attempted to move token: %s"
|
||||
+ " to non-exiting displayId=%d", binder, displayId);
|
||||
return;
|
||||
}
|
||||
final WindowToken token = mRoot.getWindowToken(binder);
|
||||
if (token == null) {
|
||||
ProtoLog.w(WM_ERROR,
|
||||
"moveWindowTokenToDisplay: Attempted to move non-existing token: %s",
|
||||
binder);
|
||||
return;
|
||||
}
|
||||
if (token.getDisplayContent() == dc) {
|
||||
ProtoLog.w(WM_ERROR,
|
||||
"moveWindowTokenToDisplay: Cannot move to the original display "
|
||||
+ "for token: %s", binder);
|
||||
return;
|
||||
}
|
||||
dc.reParentWindowToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
|
||||
@NonNull DisplayContent dc) {
|
||||
if (dc.mWaitingForConfig) {
|
||||
@@ -7355,6 +7381,11 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveWindowTokenToDisplay(IBinder binder, int displayId) {
|
||||
WindowManagerService.this.moveWindowTokenToDisplay(binder, displayId);
|
||||
}
|
||||
|
||||
// TODO(multi-display): currently only used by PWM to notify keyguard transitions as well
|
||||
// forwarding it to SystemUI for synchronizing status and navigation bar animations.
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.server.inputmethod.InputMethodManagerService;
|
||||
import com.android.server.inputmethod.InputMethodMenuController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
// TODO(b/157888351): Move the test to inputmethod package once we find the way to test the
|
||||
// scenario there.
|
||||
/**
|
||||
* Build/Install/Run:
|
||||
* atest WmTests:InputMethodMenuControllerTest
|
||||
*/
|
||||
@Presubmit
|
||||
@RunWith(WindowTestRunner.class)
|
||||
public class InputMethodMenuControllerTest extends WindowTestsBase {
|
||||
|
||||
private InputMethodMenuController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSettingsContext() {
|
||||
final Context contextOnDefaultDisplay = mController.getSettingsContext(DEFAULT_DISPLAY);
|
||||
|
||||
assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
|
||||
|
||||
// Obtain the context again and check they are the same instance and match the display
|
||||
// metrics of the secondary display.
|
||||
final Context contextOnSecondaryDisplay = mController.getSettingsContext(
|
||||
mDisplayContent.getDisplayId());
|
||||
|
||||
assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mDisplayContent);
|
||||
assertThat(contextOnDefaultDisplay.getActivityToken())
|
||||
.isEqualTo(contextOnSecondaryDisplay.getActivityToken());
|
||||
}
|
||||
|
||||
private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
|
||||
assertThat(context.getDisplayId()).isEqualTo(dc.getDisplayId());
|
||||
|
||||
final Rect contextBounds = context.getSystemService(WindowManager.class)
|
||||
.getMaximumWindowMetrics().getBounds();
|
||||
final Rect imeContainerBounds = dc.getImeContainer().getBounds();
|
||||
assertThat(contextBounds).isEqualTo(imeContainerBounds);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.os.Process.INVALID_UID;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
|
||||
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
|
||||
|
||||
@@ -32,8 +34,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -176,4 +181,31 @@ public class WindowManagerServiceTests extends WindowTestsBase {
|
||||
mWm.dismissKeyguard(null, "test-dismiss-keyguard");
|
||||
verify(mWm.mAtmService.mStackSupervisor).wakeUp(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveWindowTokenToDisplay_NullToken_DoNothing() {
|
||||
mWm.moveWindowTokenToDisplay(null, mDisplayContent.getDisplayId());
|
||||
|
||||
verify(mDisplayContent, never()).reParentWindowToken(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveWindowTokenToDisplay_SameDisplay_DoNothing() {
|
||||
final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD_DIALOG,
|
||||
mDisplayContent);
|
||||
|
||||
mWm.moveWindowTokenToDisplay(windowToken.token, mDisplayContent.getDisplayId());
|
||||
|
||||
verify(mDisplayContent, never()).reParentWindowToken(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveWindowTokenToDisplay_DifferentDisplay_DoMoveDisplay() {
|
||||
final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD_DIALOG,
|
||||
mDisplayContent);
|
||||
|
||||
mWm.moveWindowTokenToDisplay(windowToken.token, DEFAULT_DISPLAY);
|
||||
|
||||
assertThat(windowToken.getDisplayContent()).isEqualTo(mDefaultDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user