Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 812950da authored by Yohei Yukawa's avatar Yohei Yukawa Committed by Android (Google) Code Review
Browse files

Merge "Introduce InputMethodDialogWindowContext for testability"

parents 677d040e 515308ce
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+12 −34
Original line number Diff line number Diff line
@@ -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();
+9 −11
Original line number Diff line number Diff line
@@ -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);