Merge "Fix IME snapshot applied on non-IME input target window"

This commit is contained in:
Ming-Shin Lu
2022-01-22 06:44:43 +00:00
committed by Android (Google) Code Review
10 changed files with 248 additions and 6 deletions

View File

@@ -3987,11 +3987,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (target == mImeLayeringTarget) {
return;
}
// Prepare the IME screenshot for the last IME target when its task is applying app
// transition. This is for the better IME transition to keep IME visibility when
// transitioning to the next task.
// If the IME target is the input target, before it changes, prepare the IME screenshot
// for the last IME target when its task is applying app transition. This is for the
// better IME transition to keep IME visibility when transitioning to the next task.
if (mImeLayeringTarget != null && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
&& mImeLayeringTarget == mImeInputTarget) {
attachAndShowImeScreenshotOnTarget();
}

View File

@@ -1982,6 +1982,7 @@ public class DisplayContentTests extends WindowTestsBase {
// Test step 1: appWin1 is the current IME target and soft-keyboard is visible.
mDisplayContent.computeImeTarget(true);
assertEquals(appWin1, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
mDisplayContent.setImeInputTarget(appWin1);
spyOn(mDisplayContent.mInputMethodWindow);
doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
@@ -1998,7 +1999,6 @@ public class DisplayContentTests extends WindowTestsBase {
// be shown at this time.
final Transaction t = mDisplayContent.getPendingTransaction();
spyOn(t);
mDisplayContent.setImeInputTarget(appWin2);
mDisplayContent.computeImeTarget(true);
assertEquals(appWin2, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
assertTrue(mDisplayContent.shouldImeAttachedToApp());

View File

@@ -17,7 +17,9 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.server.wm.traces.parser.toFlickerComponent
@@ -47,4 +49,29 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
}
launcherStrategy.launch(appName, expectedPackage)
}
fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
val button = uiDevice.wait(Until.findObject(By.res(getPackage(),
"start_dialog_themed_activity_btn")), FIND_TIMEOUT)
require(button != null) {
"Button not found, this usually happens when the device " +
"was left in an unknown state (e.g. Screen turned off)"
}
button.click()
wmHelper.waitForAppTransitionIdle()
wmHelper.waitForFullScreenApp(
ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent())
}
fun dismissDialog(wmHelper: WindowManagerStateHelper) {
val dialog = uiDevice.wait(
Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
// Tapping outside of the dialog to dismiss
if (dialog != null) {
val dialogBounds = dialog.visibleBounds
uiDevice.click(dialogBounds.left, dialogBounds.top - 300)
wmHelper.waitForAppTransitionIdle()
}
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2021 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.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
* To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
eachRun {
testApp.launchViaIntent(wmHelper)
wmHelper.waitImeShown()
testApp.startDialogThemedActivity(wmHelper)
}
}
teardown {
eachRun {
testApp.exit()
}
}
transitions {
testApp.dismissDialog(wmHelper)
}
}
}
/**
* Checks that [FlickerComponentName.IME] layer becomes visible during the transition
*/
@Presubmit
@Test
fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
/**
* Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
*/
@Presubmit
@Test
fun imeLayerExistsEnd() {
testSpec.assertLayersEnd {
this.isVisible(FlickerComponentName.IME)
}
}
/**
* Checks that [FlickerComponentName.IME_SNAPSHOT] layer is invisible always.
*/
@Presubmit
@Test
fun imeSnapshotNotVisible() {
testSpec.assertLayers {
this.isInvisible(FlickerComponentName.IME_SNAPSHOT)
}
}
companion object {
/**
* Creates the test configurations.
*
* See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
* repetitions, screen orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
repetitions = 3,
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
)
)
}
}
}

View File

@@ -97,5 +97,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".DialogThemedActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/DialogTheme"
android:label="DialogThemedActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -31,4 +31,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Finish activity" />
<Button
android:id="@+id/start_dialog_themed_activity_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start dialog themed activity" />
</LinearLayout>

View File

@@ -27,4 +27,14 @@
<style name="CutoutNever">
<item name="android:windowLayoutInDisplayCutoutMode">never</item>
</style>
</resources>
<style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@null</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
</resources>

View File

@@ -56,4 +56,8 @@ public class ActivityOptions {
public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".DialogThemedActivity");
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2021 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.flicker.testapp;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import android.app.Activity;
import android.app.AlertDialog;
import android.graphics.Color;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
public class DialogThemedActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT);
TextView textView = new TextView(this);
textView.setText("This is a test dialog");
textView.setTextColor(Color.BLACK);
LinearLayout layout = new LinearLayout(this);
layout.setBackgroundColor(Color.GREEN);
layout.addView(textView);
// Create a dialog with dialog-themed activity
AlertDialog dialog = new AlertDialog.Builder(this)
.setView(layout)
.setTitle("Dialog for test")
.create();
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(MATCH_PARENT,
MATCH_PARENT);
attrs.flags = FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM;
dialog.getWindow().getDecorView().setLayoutParams(attrs);
dialog.setCanceledOnTouchOutside(true);
dialog.show();
dialog.setOnDismissListener((d) -> finish());
}
}

View File

@@ -16,6 +16,8 @@
package com.android.server.wm.flicker.testapp;
import android.content.Intent;
import android.widget.Button;
import android.widget.EditText;
public class ImeActivityAutoFocus extends ImeActivity {
@@ -26,5 +28,9 @@ public class ImeActivityAutoFocus extends ImeActivity {
EditText editTextField = findViewById(R.id.plain_text_input);
editTextField.requestFocus();
Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn);
startThemedActivityButton.setOnClickListener(
button -> startActivity(new Intent(this, DialogThemedActivity.class)));
}
}