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

Commit 8b139a41 authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Fix the IME flicker when the launching activity in fixed-rotation

Previously, when synchronizing IME visibility in between removing
tasksnapshot with IME starting window and showing the real IME during
the activity launching, we schedule a runnable for removing the starting
window with a timeout (600ms) if IME will be shown when
AR#onFirstWindowDrawn comes, and then remove it immediately once
the IME has actually drawn before that timeout.

But this approch doesn't accurate when the activity was in fixed
rotation. Since the IME client visiblity change from InsetsController
does not aware the IME orientation was still in changing (portrait ->
landcape), in this case, starting window will be removed in earlier
stage and seeing IME fade-out / fade-in animation during
AsyncRotationController controlling IME surface in fixed rotation.

To more accurate deciding whether to defer the starting window
removal during the rotation, in this CL
1) Modified StartingWindowRemovalInfo#deferRemoveForIme with
deferRemoveForImeMode to diffenciate scenarios for giving adaquate
waiting IME drawn timeout:

- DEFER_MODE_NONE: no need wait for IME drawn.
- DEFER_MODE_NORMAL: wait IME drawn at most 600ms
- DEFER_MODE_ROTATION: wait IME rotation and drawn at most 3 secs.

2) Fix StartingWindowRecordManager#removeStartingWindow removes the
   record from mStartingWindowRecords too early before calling
   onImeDrawnOnTask leads to the the IME snapshot starting window
   always be defered to remove after the timeout.

3) Modified ImeInsetsSourceProvider and AsyncRotationController to
skip reporting IME drawn state to WMShell when the system was in
fixed-rotation, and report it once AsyncRotationController receiving
onAnimationFinished callback from IME token's surfaceAnimator.

4) Added a flicker test to verify this CL works as expected for this
use case.

Fix: 268627602
Test: atest FlickerTests:\
        ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest
Test: atest ActivityRecordTests
Change-Id: Ie476e89a57f2f64d4d66e722fedeeb1719d9de55
parent cf97449a
Loading
Loading
Loading
Loading
+25 −4
Original line number Diff line number Diff line
@@ -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 + "}";
    }
+4 −3
Original line number Diff line number Diff line
@@ -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;
        }
    }
}
+42 −9
Original line number Diff line number Diff line
@@ -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,10 +328,12 @@ public class StartingSurfaceDrawer {
            final int taskId = removeInfo.taskId;
            final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
            if (record != null) {
                record.removeIfPossible(removeInfo, immediately);
                final boolean canRemoveRecord = record.removeIfPossible(removeInfo, immediately);
                if (canRemoveRecord) {
                    mStartingWindowRecords.remove(taskId);
                }
            }
        }

        void removeWindow(int taskId, boolean immediately) {
            mTmpRemovalInfo.taskId = taskId;
+2 −1
Original line number Diff line number Diff line
@@ -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() {
+4 −4
Original line number Diff line number Diff line
@@ -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();
Loading