Introduce InputMethodDialogWindowContext for testability
This is a follow up CL to our previous CLs [1], which introduced the
following classes for better testability.
* com.android.server.inputmethod.InputMethodMenuController
* com.android.server.wm.InputMethodMenuControllerTest
This CL only aims to clarify what we are currently testing. Other
motivations of this CL are:
* InputMethodMenuController is now a package-private final class,
which is expected to be much easier to keep maintaining.
* We can get rid of mock(InputMethodManagerService.class), which
prevents us from making InputMethodManagerService a final class.
Anyway, this is a mechanical refactoring CL. There should be no
observable behavior change.
[1]: Ief3c691029b14d848f6ad4a5ec8a8743760c7cda
c88452c3ee
[2]: I8adb0c569ea6c208aee659603ee1d69df9939267
9329fbdd72
Bug: 192412909
Test: atest WmTests:InputMethodDialogWindowContextTest
Change-Id: I381b1e09823a7f8361a74d809d4b2fbd23eb556e
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Provides the window context for the IME switcher dialog.
|
||||
*/
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public final class InputMethodDialogWindowContext {
|
||||
@Nullable
|
||||
private Context mDialogWindowContext;
|
||||
|
||||
/**
|
||||
* Returns the window context for IME switch dialogs to receive configuration changes.
|
||||
*
|
||||
* This method initializes the window context if it was not initialized, or moves the context to
|
||||
* the targeted display if the current display of context is different from the display
|
||||
* specified by {@code displayId}.
|
||||
*/
|
||||
@NonNull
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public Context get(int displayId) {
|
||||
if (mDialogWindowContext == null || mDialogWindowContext.getDisplayId() != displayId) {
|
||||
final Context systemUiContext = ActivityThread.currentActivityThread()
|
||||
.getSystemUiContext(displayId);
|
||||
final Context windowContext = systemUiContext.createWindowContext(
|
||||
WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, null /* options */);
|
||||
mDialogWindowContext = new ContextThemeWrapper(
|
||||
windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
|
||||
}
|
||||
return mDialogWindowContext;
|
||||
}
|
||||
}
|
||||
@@ -16,22 +16,19 @@
|
||||
|
||||
package com.android.server.inputmethod;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
||||
import static com.android.server.inputmethod.InputMethodManagerService.DEBUG;
|
||||
import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_ID;
|
||||
|
||||
import android.app.ActivityThread;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -45,7 +42,6 @@ import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
|
||||
import com.android.server.wm.WindowManagerInternal;
|
||||
@@ -53,8 +49,7 @@ import com.android.server.wm.WindowManagerInternal;
|
||||
import java.util.List;
|
||||
|
||||
/** A controller to show/hide the input method menu */
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public class InputMethodMenuController {
|
||||
final class InputMethodMenuController {
|
||||
private static final String TAG = InputMethodMenuController.class.getSimpleName();
|
||||
|
||||
private final InputMethodManagerService mService;
|
||||
@@ -64,17 +59,18 @@ public class InputMethodMenuController {
|
||||
private final KeyguardManager mKeyguardManager;
|
||||
private final WindowManagerInternal mWindowManagerInternal;
|
||||
|
||||
private Context mSettingsContext;
|
||||
private AlertDialog.Builder mDialogBuilder;
|
||||
private AlertDialog mSwitchingDialog;
|
||||
private IBinder mSwitchingDialogToken;
|
||||
private View mSwitchingDialogTitleView;
|
||||
private InputMethodInfo[] mIms;
|
||||
private int[] mSubtypeIds;
|
||||
|
||||
private boolean mShowImeWithHardKeyboard;
|
||||
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
@GuardedBy("ImfLock.class")
|
||||
@Nullable
|
||||
private InputMethodDialogWindowContext mDialogWindowContext;
|
||||
|
||||
public InputMethodMenuController(InputMethodManagerService service) {
|
||||
mService = service;
|
||||
mSettings = mService.mSettings;
|
||||
@@ -132,8 +128,11 @@ public class InputMethodMenuController {
|
||||
}
|
||||
}
|
||||
|
||||
final Context settingsContext = getSettingsContext(displayId);
|
||||
mDialogBuilder = new AlertDialog.Builder(settingsContext);
|
||||
if (mDialogWindowContext == null) {
|
||||
mDialogWindowContext = new InputMethodDialogWindowContext();
|
||||
}
|
||||
final Context dialogWindowContext = mDialogWindowContext.get(displayId);
|
||||
mDialogBuilder = new AlertDialog.Builder(dialogWindowContext);
|
||||
mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu());
|
||||
|
||||
final Context dialogContext = mDialogBuilder.getContext();
|
||||
@@ -199,7 +198,7 @@ public class InputMethodMenuController {
|
||||
// Use an alternate token for the dialog for that window manager can group the token
|
||||
// with other IME windows based on type vs. grouping based on whichever token happens
|
||||
// to get selected by the system later on.
|
||||
attrs.token = mSwitchingDialogToken;
|
||||
attrs.token = dialogWindowContext.getWindowContextToken();
|
||||
attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
attrs.setTitle("Select input method");
|
||||
w.setAttributes(attrs);
|
||||
@@ -208,27 +207,6 @@ 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 || mSettingsContext.getDisplayId() != displayId) {
|
||||
final Context systemUiContext = ActivityThread.currentActivityThread()
|
||||
.getSystemUiContext(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.getWindowContextToken();
|
||||
}
|
||||
return mSettingsContext;
|
||||
}
|
||||
|
||||
private boolean isScreenLocked() {
|
||||
return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()
|
||||
&& mKeyguardManager.isKeyguardSecure();
|
||||
|
||||
@@ -31,7 +31,6 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -48,8 +47,7 @@ import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.window.WindowTokenClient;
|
||||
|
||||
import com.android.server.inputmethod.InputMethodManagerService;
|
||||
import com.android.server.inputmethod.InputMethodMenuController;
|
||||
import com.android.server.inputmethod.InputMethodDialogWindowContext;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -61,13 +59,13 @@ import org.mockito.Mockito;
|
||||
// scenario there.
|
||||
/**
|
||||
* Build/Install/Run:
|
||||
* atest WmTests:InputMethodMenuControllerTest
|
||||
* atest WmTests:InputMethodDialogWindowContextTest
|
||||
*/
|
||||
@Presubmit
|
||||
@RunWith(WindowTestRunner.class)
|
||||
public class InputMethodMenuControllerTest extends WindowTestsBase {
|
||||
public class InputMethodDialogWindowContextTest extends WindowTestsBase {
|
||||
|
||||
private InputMethodMenuController mController;
|
||||
private InputMethodDialogWindowContext mWindowContext;
|
||||
private DualDisplayAreaGroupPolicyTest.DualDisplayContent mSecondaryDisplay;
|
||||
|
||||
private IWindowManager mIWindowManager;
|
||||
@@ -80,7 +78,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
|
||||
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
|
||||
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
|
||||
|
||||
mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
|
||||
mWindowContext = new InputMethodDialogWindowContext();
|
||||
mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
|
||||
.Builder(mAtm, 1000, 1000).build();
|
||||
mSecondaryDisplay.getDisplayInfo().state = STATE_ON;
|
||||
@@ -119,21 +117,21 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
|
||||
|
||||
@Test
|
||||
public void testGetSettingsContext() {
|
||||
final Context contextOnDefaultDisplay = mController.getSettingsContext(DEFAULT_DISPLAY);
|
||||
final Context contextOnDefaultDisplay = mWindowContext.get(DEFAULT_DISPLAY);
|
||||
|
||||
assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
|
||||
|
||||
// Obtain the context again and check if the window metrics match the IME container bounds
|
||||
// of the secondary display.
|
||||
final Context contextOnSecondaryDisplay = mController.getSettingsContext(
|
||||
mSecondaryDisplay.getDisplayId());
|
||||
final Context contextOnSecondaryDisplay =
|
||||
mWindowContext.get(mSecondaryDisplay.getDisplayId());
|
||||
|
||||
assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSettingsContextOnDualDisplayContent() {
|
||||
final Context context = mController.getSettingsContext(mSecondaryDisplay.getDisplayId());
|
||||
final Context context = mWindowContext.get(mSecondaryDisplay.getDisplayId());
|
||||
final WindowTokenClient tokenClient = (WindowTokenClient) context.getWindowContextToken();
|
||||
assertNotNull(tokenClient);
|
||||
spyOn(tokenClient);
|
||||
Reference in New Issue
Block a user