Merge "Fix the IME flicker when the launching activity in fixed-rotation" into udc-dev
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.window;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Rect;
|
||||
@@ -23,6 +24,9 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Information when removing a starting window of a particular task.
|
||||
* @hide
|
||||
@@ -55,11 +59,28 @@ public final class StartingWindowRemovalInfo implements Parcelable {
|
||||
*/
|
||||
public boolean playRevealAnimation;
|
||||
|
||||
/** The mode is no need to defer removing the starting window for IME */
|
||||
public static final int DEFER_MODE_NONE = 0;
|
||||
|
||||
/** The mode to defer removing the starting window until IME has drawn */
|
||||
public static final int DEFER_MODE_NORMAL = 1;
|
||||
|
||||
/** The mode to defer the starting window removal until IME drawn and finished the rotation */
|
||||
public static final int DEFER_MODE_ROTATION = 2;
|
||||
|
||||
@IntDef(prefix = { "DEFER_MODE_" }, value = {
|
||||
DEFER_MODE_NONE,
|
||||
DEFER_MODE_NORMAL,
|
||||
DEFER_MODE_ROTATION,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface DeferMode {}
|
||||
|
||||
/**
|
||||
* Whether need to defer removing the starting window for IME.
|
||||
* @hide
|
||||
*/
|
||||
public boolean deferRemoveForIme;
|
||||
public @DeferMode int deferRemoveForImeMode;
|
||||
|
||||
/**
|
||||
* The rounded corner radius
|
||||
@@ -95,7 +116,7 @@ public final class StartingWindowRemovalInfo implements Parcelable {
|
||||
windowAnimationLeash = source.readTypedObject(SurfaceControl.CREATOR);
|
||||
mainFrame = source.readTypedObject(Rect.CREATOR);
|
||||
playRevealAnimation = source.readBoolean();
|
||||
deferRemoveForIme = source.readBoolean();
|
||||
deferRemoveForImeMode = source.readInt();
|
||||
roundedCornerRadius = source.readFloat();
|
||||
windowlessSurface = source.readBoolean();
|
||||
removeImmediately = source.readBoolean();
|
||||
@@ -107,7 +128,7 @@ public final class StartingWindowRemovalInfo implements Parcelable {
|
||||
dest.writeTypedObject(windowAnimationLeash, flags);
|
||||
dest.writeTypedObject(mainFrame, flags);
|
||||
dest.writeBoolean(playRevealAnimation);
|
||||
dest.writeBoolean(deferRemoveForIme);
|
||||
dest.writeInt(deferRemoveForImeMode);
|
||||
dest.writeFloat(roundedCornerRadius);
|
||||
dest.writeBoolean(windowlessSurface);
|
||||
dest.writeBoolean(removeImmediately);
|
||||
@@ -119,7 +140,7 @@ public final class StartingWindowRemovalInfo implements Parcelable {
|
||||
+ " frame=" + mainFrame
|
||||
+ " playRevealAnimation=" + playRevealAnimation
|
||||
+ " roundedCornerRadius=" + roundedCornerRadius
|
||||
+ " deferRemoveForIme=" + deferRemoveForIme
|
||||
+ " deferRemoveForImeMode=" + deferRemoveForImeMode
|
||||
+ " windowlessSurface=" + windowlessSurface
|
||||
+ " removeImmediately=" + removeImmediately + "}";
|
||||
}
|
||||
|
||||
@@ -477,15 +477,15 @@ class SplashscreenWindowCreator extends AbsSplashWindowCreator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
||||
public boolean removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
||||
if (mRootView == null) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (mSplashView == null) {
|
||||
// shouldn't happen, the app window may be drawn earlier than starting window?
|
||||
Slog.e(TAG, "Found empty splash screen, remove!");
|
||||
removeWindowInner(mRootView, false);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
clearSystemBarColor();
|
||||
if (immediately
|
||||
@@ -503,6 +503,7 @@ class SplashscreenWindowCreator extends AbsSplashWindowCreator {
|
||||
removeWindowInner(mRootView, true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.wm.shell.startingsurface;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.window.StartingWindowRemovalInfo.DEFER_MODE_NORMAL;
|
||||
import static android.window.StartingWindowRemovalInfo.DEFER_MODE_ROTATION;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
import android.app.TaskInfo;
|
||||
@@ -216,7 +218,17 @@ public class StartingSurfaceDrawer {
|
||||
}
|
||||
abstract static class StartingWindowRecord {
|
||||
protected int mBGColor;
|
||||
abstract void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately);
|
||||
|
||||
/**
|
||||
* Remove the starting window with the given {@link StartingWindowRemovalInfo} if possible.
|
||||
* @param info The removal info sent from the task organizer controller in the WM core.
|
||||
* @param immediately {@code true} means removing the starting window immediately,
|
||||
* {@code false} otherwise.
|
||||
* @return {@code true} means {@link StartingWindowRecordManager} can safely remove the
|
||||
* record itself. {@code false} means {@link StartingWindowRecordManager} requires
|
||||
* to manage the record reference and remove it later.
|
||||
*/
|
||||
abstract boolean removeIfPossible(StartingWindowRemovalInfo info, boolean immediately);
|
||||
int getBGColor() {
|
||||
return mBGColor;
|
||||
}
|
||||
@@ -231,6 +243,15 @@ public class StartingSurfaceDrawer {
|
||||
* {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
|
||||
*/
|
||||
private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600;
|
||||
|
||||
/**
|
||||
* The max delay time in milliseconds for removing the task snapshot window with IME
|
||||
* visible after the fixed rotation finished.
|
||||
* Ideally the delay time will be shorter when receiving
|
||||
* {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
|
||||
*/
|
||||
private static final long MAX_DELAY_REMOVAL_TIME_FIXED_ROTATION = 3000;
|
||||
|
||||
private final Runnable mScheduledRunnable = this::removeImmediately;
|
||||
|
||||
@WindowConfiguration.ActivityType protected final int mActivityType;
|
||||
@@ -242,24 +263,34 @@ public class StartingSurfaceDrawer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
||||
public final boolean removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
||||
if (immediately) {
|
||||
removeImmediately();
|
||||
} else {
|
||||
scheduleRemove(info.deferRemoveForIme);
|
||||
scheduleRemove(info.deferRemoveForImeMode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void scheduleRemove(boolean deferRemoveForIme) {
|
||||
void scheduleRemove(@StartingWindowRemovalInfo.DeferMode int deferRemoveForImeMode) {
|
||||
// Show the latest content as soon as possible for unlocking to home.
|
||||
if (mActivityType == ACTIVITY_TYPE_HOME) {
|
||||
removeImmediately();
|
||||
return;
|
||||
}
|
||||
mRemoveExecutor.removeCallbacks(mScheduledRunnable);
|
||||
final long delayRemovalTime = hasImeSurface() && deferRemoveForIme
|
||||
? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
|
||||
: DELAY_REMOVAL_TIME_GENERAL;
|
||||
final long delayRemovalTime;
|
||||
switch (deferRemoveForImeMode) {
|
||||
case DEFER_MODE_ROTATION:
|
||||
delayRemovalTime = MAX_DELAY_REMOVAL_TIME_FIXED_ROTATION;
|
||||
break;
|
||||
case DEFER_MODE_NORMAL:
|
||||
delayRemovalTime = MAX_DELAY_REMOVAL_TIME_IME_VISIBLE;
|
||||
break;
|
||||
default:
|
||||
delayRemovalTime = DELAY_REMOVAL_TIME_GENERAL;
|
||||
}
|
||||
mRemoveExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||
"Defer removing snapshot surface in %d", delayRemovalTime);
|
||||
@@ -297,8 +328,10 @@ public class StartingSurfaceDrawer {
|
||||
final int taskId = removeInfo.taskId;
|
||||
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
|
||||
if (record != null) {
|
||||
record.removeIfPossible(removeInfo, immediately);
|
||||
mStartingWindowRecords.remove(taskId);
|
||||
final boolean canRemoveRecord = record.removeIfPossible(removeInfo, immediately);
|
||||
if (canRemoveRecord) {
|
||||
mStartingWindowRecords.remove(taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ class WindowlessSplashWindowCreator extends AbsSplashWindowCreator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
||||
public boolean removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
||||
if (!immediately) {
|
||||
mSplashscreenContentDrawer.applyExitAnimation(mSplashView,
|
||||
info.windowAnimationLeash, info.mainFrame,
|
||||
@@ -132,6 +132,7 @@ class WindowlessSplashWindowCreator extends AbsSplashWindowCreator {
|
||||
} else {
|
||||
release();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void release() {
|
||||
|
||||
@@ -265,17 +265,17 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
|
||||
mStartingSurfaceDrawer.mWindowRecords.addRecord(taskId,
|
||||
new StartingSurfaceDrawer.StartingWindowRecord() {
|
||||
@Override
|
||||
public void removeIfPossible(StartingWindowRemovalInfo info,
|
||||
public boolean removeIfPossible(StartingWindowRemovalInfo info,
|
||||
boolean immediately) {
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mStartingSurfaceDrawer.mWindowlessRecords.addRecord(taskId,
|
||||
new StartingSurfaceDrawer.StartingWindowRecord() {
|
||||
@Override
|
||||
public void removeIfPossible(StartingWindowRemovalInfo info,
|
||||
public boolean removeIfPossible(StartingWindowRemovalInfo info,
|
||||
boolean immediately) {
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mStartingSurfaceDrawer.clearAllWindows();
|
||||
|
||||
@@ -5220,6 +5220,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
logAppCompatState();
|
||||
if (!visible) {
|
||||
final InputTarget imeInputTarget = mDisplayContent.getImeInputTarget();
|
||||
mLastImeShown = imeInputTarget != null && imeInputTarget.getWindowState() != null
|
||||
&& imeInputTarget.getWindowState().mActivityRecord == this
|
||||
&& mDisplayContent.mInputMethodWindow != null
|
||||
&& mDisplayContent.mInputMethodWindow.isVisible();
|
||||
finishOrAbortReplacingWindow();
|
||||
}
|
||||
return true;
|
||||
@@ -5609,11 +5614,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
|
||||
if (!visible) {
|
||||
final InputTarget imeInputTarget = mDisplayContent.getImeInputTarget();
|
||||
mLastImeShown = imeInputTarget != null && imeInputTarget.getWindowState() != null
|
||||
&& imeInputTarget.getWindowState().mActivityRecord == this
|
||||
&& mDisplayContent.mInputMethodWindow != null
|
||||
&& mDisplayContent.mInputMethodWindow.isVisible();
|
||||
mImeInsetsFrozenUntilStartInput = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -267,7 +267,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume
|
||||
op.mDrawTransaction = null;
|
||||
if (DEBUG) Slog.d(TAG, "finishOp merge transaction " + windowToken.getTopChild());
|
||||
}
|
||||
if (op.mAction == Operation.ACTION_FADE) {
|
||||
if (op.mAction == Operation.ACTION_TOGGLE_IME) {
|
||||
if (DEBUG) Slog.d(TAG, "finishOp fade-in IME " + windowToken.getTopChild());
|
||||
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM,
|
||||
(type, anim) -> mDisplayContent.getInsetsStateController()
|
||||
.getImeSourceProvider().reportImeDrawnForOrganizer());
|
||||
} else if (op.mAction == Operation.ACTION_FADE) {
|
||||
if (DEBUG) Slog.d(TAG, "finishOp fade-in " + windowToken.getTopChild());
|
||||
// The previous animation leash will be dropped when preparing fade-in animation, so
|
||||
// simply apply new animation without restoring the transformation.
|
||||
@@ -344,7 +349,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
|
||||
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
|
||||
final WindowToken windowToken = mTargetWindowTokens.keyAt(i);
|
||||
final Operation op = mTargetWindowTokens.valueAt(i);
|
||||
if (op.mAction == Operation.ACTION_FADE) {
|
||||
if (op.mAction == Operation.ACTION_FADE || op.mAction == Operation.ACTION_TOGGLE_IME) {
|
||||
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
|
||||
op.mLeash = windowToken.getAnimationLeash();
|
||||
if (DEBUG) Slog.d(TAG, "Start fade-out " + windowToken.getTopChild());
|
||||
@@ -374,17 +379,19 @@ class AsyncRotationController extends FadeAnimationController implements Consume
|
||||
WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION);
|
||||
}
|
||||
|
||||
/** Hides the window immediately until it is drawn in new rotation. */
|
||||
void hideImmediately(WindowToken windowToken) {
|
||||
if (isTargetToken(windowToken)) return;
|
||||
/** Hides the IME window immediately until it is drawn in new rotation. */
|
||||
void hideImeImmediately() {
|
||||
if (mDisplayContent.mInputMethodWindow == null) return;
|
||||
final WindowToken imeWindowToken = mDisplayContent.mInputMethodWindow.mToken;
|
||||
if (isTargetToken(imeWindowToken)) return;
|
||||
final boolean original = mHideImmediately;
|
||||
mHideImmediately = true;
|
||||
final Operation op = new Operation(Operation.ACTION_FADE);
|
||||
mTargetWindowTokens.put(windowToken, op);
|
||||
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
|
||||
op.mLeash = windowToken.getAnimationLeash();
|
||||
final Operation op = new Operation(Operation.ACTION_TOGGLE_IME);
|
||||
mTargetWindowTokens.put(imeWindowToken, op);
|
||||
fadeWindowToken(false /* show */, imeWindowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
|
||||
op.mLeash = imeWindowToken.getAnimationLeash();
|
||||
mHideImmediately = original;
|
||||
if (DEBUG) Slog.d(TAG, "hideImmediately " + windowToken.getTopChild());
|
||||
if (DEBUG) Slog.d(TAG, "hideImeImmediately " + imeWindowToken.getTopChild());
|
||||
}
|
||||
|
||||
/** Returns {@code true} if the window will rotate independently. */
|
||||
@@ -586,11 +593,13 @@ class AsyncRotationController extends FadeAnimationController implements Consume
|
||||
/** The operation to control the rotation appearance associated with window token. */
|
||||
private static class Operation {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(value = { ACTION_SEAMLESS, ACTION_FADE })
|
||||
@IntDef(value = { ACTION_SEAMLESS, ACTION_FADE, ACTION_TOGGLE_IME })
|
||||
@interface Action {}
|
||||
|
||||
static final int ACTION_SEAMLESS = 1;
|
||||
static final int ACTION_FADE = 2;
|
||||
/** The action to toggle the IME window appearance */
|
||||
static final int ACTION_TOGGLE_IME = 3;
|
||||
final @Action int mAction;
|
||||
/** The leash of window token. It can be animation leash or the token itself. */
|
||||
SurfaceControl mLeash;
|
||||
|
||||
@@ -1896,7 +1896,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
case SOFT_INPUT_STATE_HIDDEN:
|
||||
return false;
|
||||
}
|
||||
return r.mLastImeShown;
|
||||
final boolean useIme = r.getWindow(
|
||||
w -> WindowManager.LayoutParams.mayUseInputMethod(w.mAttrs.flags)) != null;
|
||||
if (!useIme) {
|
||||
return false;
|
||||
}
|
||||
return r.mLastImeShown || (r.mStartingData != null && r.mStartingData.hasImeSurface());
|
||||
}
|
||||
|
||||
/** Returns {@code true} if the top activity is transformed with the new rotation of display. */
|
||||
@@ -4219,7 +4224,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
// Hide the window until the rotation is done to avoid intermediate artifacts if the
|
||||
// parent surface of IME container is changed.
|
||||
if (mAsyncRotationController != null) {
|
||||
mAsyncRotationController.hideImmediately(mInputMethodWindow.mToken);
|
||||
mAsyncRotationController.hideImeImmediately();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,14 +57,21 @@ public class FadeAnimationController {
|
||||
return AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
|
||||
}
|
||||
|
||||
/** Run the fade in/out animation for a window token. */
|
||||
public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
|
||||
fadeWindowToken(show, windowToken, animationType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the fade in/out animation for a window token.
|
||||
*
|
||||
* @param show true for fade-in, otherwise for fade-out.
|
||||
* @param windowToken the window token to run the animation.
|
||||
* @param animationType the animation type defined in SurfaceAnimator.
|
||||
* @param finishedCallback the callback after the animation finished.
|
||||
*/
|
||||
public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
|
||||
public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType,
|
||||
SurfaceAnimator.OnAnimationFinishedCallback finishedCallback) {
|
||||
if (windowToken == null || windowToken.getParent() == null) {
|
||||
return;
|
||||
}
|
||||
@@ -75,9 +82,8 @@ public class FadeAnimationController {
|
||||
if (animationAdapter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
|
||||
show /* hidden */, animationType, null /* finishedCallback */);
|
||||
show /* hidden */, animationType, finishedCallback);
|
||||
}
|
||||
|
||||
protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
|
||||
|
||||
@@ -145,18 +145,44 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
|
||||
}
|
||||
boolean changed = super.updateClientVisibility(caller);
|
||||
if (changed && caller.isRequestedVisible(mSource.getType())) {
|
||||
reportImeDrawnForOrganizer(caller);
|
||||
reportImeDrawnForOrganizerIfNeeded(caller);
|
||||
}
|
||||
changed |= mDisplayContent.onImeInsetsClientVisibilityUpdate();
|
||||
return changed;
|
||||
}
|
||||
|
||||
private void reportImeDrawnForOrganizer(InsetsControlTarget caller) {
|
||||
if (caller.getWindow() != null && caller.getWindow().getTask() != null) {
|
||||
if (caller.getWindow().getTask().isOrganized()) {
|
||||
mWindowContainer.mWmService.mAtmService.mTaskOrganizerController
|
||||
.reportImeDrawnOnTask(caller.getWindow().getTask());
|
||||
}
|
||||
private void reportImeDrawnForOrganizerIfNeeded(@NonNull InsetsControlTarget caller) {
|
||||
final WindowState callerWindow = caller.getWindow();
|
||||
if (callerWindow == null) {
|
||||
return;
|
||||
}
|
||||
WindowToken imeToken = mWindowContainer.asWindowState() != null
|
||||
? mWindowContainer.asWindowState().mToken : null;
|
||||
if (mDisplayContent.getAsyncRotationController() != null
|
||||
&& mDisplayContent.getAsyncRotationController().isTargetToken(imeToken)) {
|
||||
// Skip reporting IME drawn state when the control target is in fixed
|
||||
// rotation, AsyncRotationController will report after the animation finished.
|
||||
return;
|
||||
}
|
||||
reportImeDrawnForOrganizer(caller);
|
||||
}
|
||||
|
||||
private void reportImeDrawnForOrganizer(@NonNull InsetsControlTarget caller) {
|
||||
final WindowState callerWindow = caller.getWindow();
|
||||
if (callerWindow == null || callerWindow.getTask() == null) {
|
||||
return;
|
||||
}
|
||||
if (callerWindow.getTask().isOrganized()) {
|
||||
mWindowContainer.mWmService.mAtmService.mTaskOrganizerController
|
||||
.reportImeDrawnOnTask(caller.getWindow().getTask());
|
||||
}
|
||||
}
|
||||
|
||||
/** Report the IME has drawn on the current IME control target for its task organizer */
|
||||
void reportImeDrawnForOrganizer() {
|
||||
final InsetsControlTarget imeControlTarget = getControlTarget();
|
||||
if (imeControlTarget != null) {
|
||||
reportImeDrawnForOrganizer(imeControlTarget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ package com.android.server.wm;
|
||||
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.app.TaskInfo.cameraCompatControlStateToString;
|
||||
import static android.window.StartingWindowRemovalInfo.DEFER_MODE_NONE;
|
||||
import static android.window.StartingWindowRemovalInfo.DEFER_MODE_NORMAL;
|
||||
import static android.window.StartingWindowRemovalInfo.DEFER_MODE_ROTATION;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
|
||||
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
|
||||
@@ -686,8 +689,19 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
final boolean playShiftUpAnimation = !task.inMultiWindowMode();
|
||||
final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
|
||||
if (topActivity != null) {
|
||||
removalInfo.deferRemoveForIme = topActivity.mDisplayContent
|
||||
.mayImeShowOnLaunchingActivity(topActivity);
|
||||
// Set defer remove mode for IME
|
||||
final DisplayContent dc = topActivity.getDisplayContent();
|
||||
final WindowState imeWindow = dc.mInputMethodWindow;
|
||||
if (topActivity.isVisibleRequested() && imeWindow != null
|
||||
&& dc.mayImeShowOnLaunchingActivity(topActivity)
|
||||
&& dc.isFixedRotationLaunchingApp(topActivity)) {
|
||||
removalInfo.deferRemoveForImeMode = DEFER_MODE_ROTATION;
|
||||
} else if (dc.mayImeShowOnLaunchingActivity(topActivity)) {
|
||||
removalInfo.deferRemoveForImeMode = DEFER_MODE_NORMAL;
|
||||
} else {
|
||||
removalInfo.deferRemoveForImeMode = DEFER_MODE_NONE;
|
||||
}
|
||||
|
||||
final WindowState mainWindow =
|
||||
topActivity.findMainWindow(false/* includeStartingApp */);
|
||||
// No app window for this activity, app might be crashed.
|
||||
|
||||
@@ -18,6 +18,11 @@ package com.android.server.wm.flicker.ime
|
||||
|
||||
import android.platform.test.annotations.Presubmit
|
||||
import android.tools.common.Rotation
|
||||
import android.platform.test.annotations.Postsubmit
|
||||
import android.tools.common.Timestamp
|
||||
import android.tools.common.traces.component.ComponentNameMatcher
|
||||
import android.tools.common.flicker.subject.exceptions.ExceptionMessageBuilder
|
||||
import android.tools.common.flicker.subject.exceptions.InvalidPropertyException
|
||||
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
|
||||
import android.tools.device.flicker.legacy.FlickerBuilder
|
||||
import android.tools.device.flicker.legacy.FlickerTest
|
||||
@@ -36,7 +41,7 @@ import org.junit.runners.Parameterized
|
||||
/**
|
||||
* Test IME window layer will become visible when switching from the fixed orientation activity
|
||||
* (e.g. Launcher activity). To run this test: `atest
|
||||
* FlickerTests:OpenImeWindowFromFixedOrientationAppTest`
|
||||
* FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
|
||||
*/
|
||||
@RequiresDevice
|
||||
@RunWith(Parameterized::class)
|
||||
@@ -77,6 +82,49 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Fl
|
||||
flicker.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp)
|
||||
}
|
||||
|
||||
@Postsubmit
|
||||
@Test
|
||||
fun imeLayerAlphaOneAfterSnapshotStartingWindowRemoval() {
|
||||
// Check if the snapshot appeared during the trace
|
||||
var imeSnapshotRemovedTimestamp: Timestamp? = null
|
||||
|
||||
val layerTrace = flicker.reader.readLayersTrace()
|
||||
val layerTraceEntries = layerTrace?.entries?.toList() ?: emptyList()
|
||||
|
||||
layerTraceEntries.zipWithNext { prev, next ->
|
||||
val prevSnapshotLayerVisible =
|
||||
ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers)
|
||||
val nextSnapshotLayerVisible =
|
||||
ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers)
|
||||
|
||||
if (imeSnapshotRemovedTimestamp == null &&
|
||||
(prevSnapshotLayerVisible && !nextSnapshotLayerVisible)) {
|
||||
imeSnapshotRemovedTimestamp = next.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
// if so, make an assertion
|
||||
imeSnapshotRemovedTimestamp?.let { timestamp ->
|
||||
val stateAfterSnapshot = layerTrace?.getEntryAt(timestamp)
|
||||
?: error("State not found for $timestamp")
|
||||
|
||||
val imeLayers = ComponentNameMatcher.IME
|
||||
.filterLayers(stateAfterSnapshot.visibleLayers.toList())
|
||||
|
||||
require(imeLayers.isNotEmpty()) { "IME layer not found" }
|
||||
if (imeLayers.any { it.color.a != 1.0f }) {
|
||||
val errorMsgBuilder = ExceptionMessageBuilder()
|
||||
.setTimestamp(timestamp)
|
||||
.forInvalidProperty("IME layer alpha")
|
||||
.setExpected("is 1.0")
|
||||
.setActual("not 1.0")
|
||||
.addExtraDescription("Filter",
|
||||
ComponentNameMatcher.IME.toLayerIdentifier())
|
||||
throw InvalidPropertyException(errorMsgBuilder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates the test configurations.
|
||||
|
||||
Reference in New Issue
Block a user