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

Commit 2bcbd680 authored by Issei Suzuki's avatar Issei Suzuki
Browse files

Separate animation applying logic from commitVisibility

Test: Refactoring. Pass existing tests.
Bug: 131661052

Change-Id: Ic455bcf77d405410ed8639664b643564fb089338
parent a9cfb69c
Loading
Loading
Loading
Loading
+12 −18
Original line number Diff line number Diff line
@@ -79,12 +79,6 @@
      "group": "WM_ERROR",
      "at": "com\/android\/server\/wm\/WindowManagerService.java"
    },
    "-1976550065": {
      "message": "commitVisibility: %s: visible=%b visibleRequested=%b",
      "level": "VERBOSE",
      "group": "WM_DEBUG_APP_TRANSITIONS",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "-1963461591": {
      "message": "Removing %s from %s",
      "level": "VERBOSE",
@@ -283,6 +277,12 @@
      "group": "WM_ERROR",
      "at": "com\/android\/server\/wm\/WindowManagerService.java"
    },
    "-1521427940": {
      "message": "commitVisibility: %s: visible=%b mVisibleRequested=%b",
      "level": "VERBOSE",
      "group": "WM_DEBUG_APP_TRANSITIONS",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "-1515151503": {
      "message": ">>> OPEN TRANSACTION removeReplacedWindows",
      "level": "INFO",
@@ -697,12 +697,6 @@
      "group": "WM_DEBUG_SCREEN_ON",
      "at": "com\/android\/server\/wm\/DisplayContent.java"
    },
    "-633961578": {
      "message": "applyAnimation: transition animation is disabled or skipped. container=%s",
      "level": "VERBOSE",
      "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "-622997754": {
      "message": "postWindowRemoveCleanupLocked: %s",
      "level": "VERBOSE",
@@ -1477,12 +1471,6 @@
      "group": "WM_DEBUG_REMOTE_ANIMATIONS",
      "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
    },
    "841702299": {
      "message": "Changing app %s visible=%b performLayout=%b",
      "level": "VERBOSE",
      "group": "WM_DEBUG_APP_TRANSITIONS",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "845234215": {
      "message": "App is requesting an orientation, return %d for display id=%d",
      "level": "VERBOSE",
@@ -1993,6 +1981,12 @@
      "group": "WM_DEBUG_REMOTE_ANIMATIONS",
      "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
    },
    "1967975839": {
      "message": "Changing app %s visible=%b performLayout=%b",
      "level": "VERBOSE",
      "group": "WM_DEBUG_APP_TRANSITIONS",
      "at": "com\/android\/server\/wm\/AppTransitionController.java"
    },
    "1984470582": {
      "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
      "level": "DEBUG",
+158 −152
Original line number Diff line number Diff line
@@ -547,7 +547,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
            new WindowState.UpdateReportedVisibilityResults();

    private boolean mUseTransferredAnimation;
    boolean mUseTransferredAnimation;

    /**
     * @see #currentLaunchCanTurnScreenOn()
@@ -2190,7 +2190,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    }

    /**
     * Sets if this AWT is in the process of closing or entering PIP.
     * Sets if this {@link ActivityRecord} is in the process of closing or entering PIP.
     * {@link #mWillCloseOrEnterPip}}
     */
    void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
@@ -2198,7 +2198,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    }

    /**
     * Returns whether this AWT is considered closing. Conditions are either
     * Returns whether this {@link ActivityRecord} is considered closing. Conditions are either
     * 1. Is this app animating and was requested to be hidden
     * 2. App is delayed closing since it might enter PIP.
     */
@@ -3000,7 +3000,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A

        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app token: %s", this);

        boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
        commitVisibility(false /* visible */, true /* performLayout */);

        getDisplayContent().mOpeningApps.remove(this);
        getDisplayContent().mChangingApps.remove(this);
@@ -3008,6 +3008,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        mWmService.mTaskSnapshotController.onAppRemoved(this);
        mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
        waitingToShow = false;

        boolean delayed = isAnimating(TRANSITION | CHILDREN);
        if (getDisplayContent().mClosingApps.contains(this)) {
            delayed = true;
        } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
@@ -3152,16 +3154,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        updateLetterboxSurface(child);
    }

    private boolean waitingForReplacement() {
        for (int i = mChildren.size() - 1; i >= 0; i--) {
            final WindowState candidate = mChildren.get(i);
            if (candidate.waitingForReplacement()) {
                return true;
            }
        }
        return false;
    }

    void onWindowReplacementTimeout() {
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            (mChildren.get(i)).onWindowReplacementTimeout();
@@ -3417,7 +3409,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // before the non-exiting app tokens. So, we skip the exiting app tokens here.
        // TODO: Investigate if we need to continue to do this or if we can just process them
        // in-order.
        if (mIsExiting && !waitingForReplacement()) {
        if (mIsExiting && !forAllWindowsUnchecked(WindowState::waitingForReplacement, true)) {
            return false;
        }
        return forAllWindowsUnchecked(callback, traverseTopToBottom);
@@ -3861,6 +3853,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
    }

    /**
     * Set visibility on this {@link ActivityRecord}
     *
     * <p class="note"><strong>Note: </strong>This function might not update the visibility of
     * this {@link ActivityRecord} immediately. In case we are preparing an app transition, we
     * delay changing the visibility of this {@link ActivityRecord} until we execute that
     * transition.</p>
     *
     * @param visible {@code true} if the {@link ActivityRecord} should become visible, otherwise
     *                this should become invisible.
     */
    void setVisibility(boolean visible) {
        if (getParent() == null) {
            Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
@@ -3997,62 +4000,45 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            return;
        }

        commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
        commitVisibility(visible, true /* performLayout */);
        updateReportedVisibilityLocked();
    }

    boolean commitVisibility(WindowManager.LayoutParams lp,
            boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {

        boolean delayed = false;
        // Reset the state of mVisibleSetFromTransferredStartingWindow since visibility is actually
        // been set by the app now.
        mVisibleSetFromTransferredStartingWindow = false;

        // Allow for state changes and animation to be applied if:
        // * token is transitioning visibility state
        // * or the token was marked as hidden and is exiting before we had a chance to play the
        // transition animation
        // * or this is an opening app and windows are being replaced
        // * or the token is the opening app and visible while opening task behind existing one.
        final DisplayContent displayContent = getDisplayContent();
        boolean visibilityChanged = false;
        if (isVisible() != visible || (!isVisible() && mIsExiting)
                || (visible && waitingForReplacement())
                || (visible && displayContent.mOpeningApps.contains(this)
                && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND)) {
            final AccessibilityController accessibilityController =
                    mWmService.mAccessibilityController;
            boolean changed = false;
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                    "Changing app %s visible=%b performLayout=%b", this, isVisible(),
                    performLayout);

            boolean runningAppAnimation = false;

            if (transit != WindowManager.TRANSIT_UNSET) {
    @Override
    boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
            boolean isVoiceInteraction) {
        if (mUseTransferredAnimation) {
                    runningAppAnimation = isAnimating();
                } else if (applyAnimation(lp, transit, visible, isVoiceInteraction)) {
                    runningAppAnimation = true;
            return false;
        }
                delayed = runningAppAnimation;
                final WindowState window = findMainWindow();
                if (window != null && accessibilityController != null) {
                    accessibilityController.onAppWindowTransitionLocked(window, transit);
        return super.applyAnimation(lp, transit, enter, isVoiceInteraction);
    }
                changed = true;

    /**
     * Update visibility to this {@link ActivityRecord}.
     *
     * <p class="note"><strong>Note: </strong> Unlike {@link #setVisibility}, this immediately
     * updates the visibility without starting an app transition. Since this function may start
     * animation on {@link WindowState} depending on app transition animation status, an app
     * transition animation must be started before calling this function if necessary.</p>
     *
     * @param visible {@code true} if this {@link ActivityRecord} should become visible, otherwise
     *                this should become invisible.
     * @param performLayout if {@code true}, perform surface placement after committing visibility.
     */
    void commitVisibility(boolean visible, boolean performLayout) {
        // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
        // been set by the app now.
        mVisibleSetFromTransferredStartingWindow = false;
        if (visible == isVisible()) {
            return;
        }

        final int windowsCount = mChildren.size();
        for (int i = 0; i < windowsCount; i++) {
                final WindowState win = mChildren.get(i);
                changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
            mChildren.get(i).onAppVisibilityChanged(visible, isAnimating(PARENTS));
        }

        setVisible(visible);
        mVisibleRequested = visible;
            visibilityChanged = true;
        if (!visible) {
            stopFreezingScreen(true, true);
        } else {
@@ -4062,17 +4048,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
                startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
            }

            // We are becoming visible, so better freeze the screen with the windows that are
            // getting visible so we also wait for them.
            forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
        }

        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                    "commitVisibility: %s: visible=%b visibleRequested=%b", this,
                "commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
                isVisible(), mVisibleRequested);

            if (changed) {
        final DisplayContent displayContent = getDisplayContent();
        displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
        if (performLayout) {
            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4080,36 +4063,46 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            mWmService.mWindowPlacerLocked.performSurfacePlacement();
        }
        displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
            }
        }
        mUseTransferredAnimation = false;

        delayed = isAnimating(CHILDREN);
        postApplyAnimation(visible);
    }

    /**
     * Post process after applying an app transition animation.
     *
     * <p class="note"><strong>Note: </strong> This function must be called after the animations
     * have been applied and {@link #commitVisibility}.</p>
     *
     * @param visible {@code true} if this {@link ActivityRecord} has become visible, otherwise
     *                this has become invisible.
     */
    private void postApplyAnimation(boolean visible) {
        final boolean delayed = isAnimating(PARENTS | CHILDREN);
        if (!delayed) {
            // We aren't animating anything, but exiting windows rely on the animation finished
            // callback being called in case the ActivityRecord was pretending to be animating,
            // We aren't delayed anything, but exiting windows rely on the animation finished
            // callback being called in case the ActivityRecord was pretending to be delayed,
            // which we might have done because we were in closing/opening apps list.
            onAnimationFinished();
        }

        if (visibilityChanged) {
            if (visible && !delayed) {
            if (visible) {
                // The token was made immediately visible, there will be no entrance animation.
                // We need to inform the client the enter animation was finished.
                mEnteringAnimation = true;
                mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
                        token);
            }
        }

        // If we're becoming visible, immediately change client visibility as well. there seem
        // to be some edge cases where we change our visibility but client visibility never gets
        // updated.
        // If we're becoming invisible, update the client visibility if we are not running an
        // animation. Otherwise, we'll update client visibility in onAnimationFinished.
            if (visible || !isAnimating()) {
        if (visible || !isAnimating(PARENTS)) {
            setClientVisible(visible);
        }

        final DisplayContent displayContent = getDisplayContent();
        if (!displayContent.mClosingApps.contains(this)
                && !displayContent.mOpeningApps.contains(this)) {
            // The token is not closing nor opening, so even if there is an animation set, that
@@ -4136,14 +4129,40 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // can be synchronized with showing the next surface in the transition.
        if (!isVisible() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
            SurfaceControl.openTransaction();
                for (int i = mChildren.size() - 1; i >= 0; i--) {
                    mChildren.get(i).mWinAnimator.hide("immediately hidden");
                }
            try {
                forAllWindows(win -> {
                    win.mWinAnimator.hide("immediately hidden"); }, true);
            } finally {
                SurfaceControl.closeTransaction();
            }
        }
    }

        return delayed;
    /**
     * Check if visibility of this {@link ActivityRecord} should be updated as part of an app
     * transition.
     *
     * <p class="note><strong>Note:</strong> If the visibility of this {@link ActivityRecord} is
     * already set to {@link #visible}, we don't need to update the visibility. So {@code false} is
     * returned.</p>
     *
     * @param visible {@code true} if this {@link ActivityRecord} should become visible,
     *                {@code false} if this should become invisible.
     * @return {@code true} if visibility of this {@link ActivityRecord} should be updated, and
     *         an app transition animation should be run.
     */
    boolean shouldApplyAnimation(boolean visible) {
        // Allow for state update and animation to be applied if:
        // * token is transitioning visibility state
        // * or the token was marked as hidden and is exiting before we had a chance to play the
        // transition animation
        // * or this is an opening app and windows are being replaced
        // * or the token is the opening app and visible while opening task behind existing one.
        final DisplayContent displayContent = getDisplayContent();
        return isVisible() != visible || (!isVisible() && mIsExiting)
                || (visible && forAllWindows(WindowState::waitingForReplacement, true))
                || (visible && displayContent.mOpeningApps.contains(this)
                && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND);
    }

    /**
@@ -5634,19 +5653,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition();
    }

    @Override
    boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
            boolean isVoiceInteraction) {
        if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: transition animation is disabled or skipped. "
                            + "container=%s", this);
            cancelAnimation();
            return false;
        }
        return super.applyAnimation(lp, transit, enter, isVoiceInteraction);
    }

    /**
     * Creates a layer to apply crop to an animation.
     */
@@ -5913,14 +5919,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    protected void onAnimationFinished() {
        super.onAnimationFinished();

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#onAnimationFinished");
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
        mTransit = TRANSIT_UNSET;
        mTransitFlags = 0;
        mNeedsZBoost = false;
        mNeedsAnimationBoundsLayer = false;

        setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
                "AppWindowToken");
                "ActivityRecord");

        clearThumbnail();
        setClientVisible(isVisible() || mVisibleRequested);
+77 −35

File changed.

Preview size limit exceeded, changes collapsed.

+5 −14
Original line number Diff line number Diff line
@@ -1738,28 +1738,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
        super.onMovedByResize();
    }

    boolean onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
        boolean changed = false;

    void onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowState c = mChildren.get(i);
            changed |= c.onAppVisibilityChanged(visible, runningAppAnimation);
            mChildren.get(i).onAppVisibilityChanged(visible, runningAppAnimation);
        }

        final boolean isVisibleNow = isVisibleNow();
        if (mAttrs.type == TYPE_APPLICATION_STARTING) {
            // Starting window that's exiting will be removed when the animation finishes.
            // Mark all relevant flags for that onExitAnimationDone will proceed all the way
            // to actually remove it.
            if (!visible && isVisibleNow() && mActivityRecord.isAnimating(TRANSITION)) {
            if (!visible && isVisibleNow && mActivityRecord.isAnimating(TRANSITION)) {
                mAnimatingExit = true;
                mRemoveOnExit = true;
                mWindowRemovalAllowed = true;
            }
            return changed;
        }

        final boolean isVisibleNow = isVisibleNow();
        if (visible != isVisibleNow) {
        } else if (visible != isVisibleNow) {
            // Run exit animation if:
            // 1. App visibility and WS visibility are different
            // 2. App is not running an animation
@@ -1773,11 +1767,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
                    accessibilityController.onWindowTransitionLocked(this, winTransit);
                }
            }
            changed = true;
            setDisplayLayoutNeeded();
        }

        return changed;
    }

    boolean onSetAppExiting() {
+2 −3
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_UNSET;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

@@ -303,8 +302,8 @@ public class AppWindowTokenTests extends WindowTestsBase {
                "closingWindow");
        closingWindow.mAnimatingExit = true;
        closingWindow.mRemoveOnExit = true;
        closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                true /* performLayout */, false /* isVoiceInteraction */);
        closingWindow.mActivityRecord.commitVisibility(
                false /* visible */, true /* performLayout */);

        // We pretended that we were running an exit animation, but that should have been cleared up
        // by changing visibility of ActivityRecord
Loading