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

Commit 55a309f8 authored by Filip Gruszczynski's avatar Filip Gruszczynski
Browse files

Maximize animation when activity is relaunched.

This is the first step towards maximize operation animations. It
builds upon preserving old window and starts clip+translate animation
of the new window from the frame of the old window. It mostly uses the
existing infrastructure, expanding the idea of opening apps list to
include also relaunching apps and treating maximize operation as an app
transition.

Change-Id: I7be402bd329c2fe5bf7d53a2a910532286a8b194
parent c6c28731
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -3092,8 +3092,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
                    // to size change. The activity will first remove the old window and then add a
                    // new one. This call will tell window manager about this, so it can preserve
                    // the old window until the new one is drawn. This prevents having a gap between
                    // the removal and addition, in which no window is visible.
                    mWindowManager.setReplacingWindow(r.appToken);
                    // the removal and addition, in which no window is visible. If we also changed
                    // the stack to the fullscreen stack, i.e. maximized the window, we will animate
                    // the transition.
                    mWindowManager.setReplacingWindow(r.appToken,
                            stackId == FULLSCREEN_WORKSPACE_STACK_ID /* animate */);
                }
            }
        }
+23 −0
Original line number Diff line number Diff line
@@ -122,6 +122,8 @@ public class AppTransition implements Dump {
    public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
    /** A window in a task is being animated in-place. */
    public static final int TRANSIT_TASK_IN_PLACE = 17;
    /** An activity is being relaunched (e.g. due to configuration change). */
    public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;

    /** Fraction of animation at which the recents thumbnail stays completely transparent */
    private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
@@ -1004,6 +1006,22 @@ public class AppTransition implements Dump {
        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
    }

    private Animation createRelaunchAnimation(int appWidth, int appHeight) {
        getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
        final int left = mTmpFromClipRect.left;
        final int top = mTmpFromClipRect.top;
        mTmpFromClipRect.offset(-left, -top);
        mTmpToClipRect.set(0, 0, appWidth, appHeight);
        AnimationSet set = new AnimationSet(true);
        ClipRectAnimation clip = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
        TranslateAnimation translate = new TranslateAnimation(left, 0, top, 0);
        clip.setInterpolator(mDecelerateInterpolator);
        set.addAnimation(clip);
        set.addAnimation(translate);
        set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
        return set;
    }

    /**
     * @return true if and only if the first frame of the transition can be skipped, i.e. the first
     *         frame of the transition doesn't change the visuals on screen, so we can start
@@ -1040,6 +1058,8 @@ public class AppTransition implements Dump {
                    "applyAnimation voice:"
                    + " anim=" + a + " transit=" + appTransitionToString(transit)
                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
        } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
            a = createRelaunchAnimation(appWidth, appHeight);
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
            a = loadAnimationRes(mNextAppTransitionPackage, enter ?
                    mNextAppTransitionEnter : mNextAppTransitionExit);
@@ -1326,6 +1346,9 @@ public class AppTransition implements Dump {
            case TRANSIT_TASK_OPEN_BEHIND: {
                return "TRANSIT_TASK_OPEN_BEHIND";
            }
            case TRANSIT_ACTIVITY_RELAUNCH: {
                return "TRANSIT_ACTIVITY_RELAUNCH";
            }
            default: {
                return "<UNKNOWN>";
            }
+14 −2
Original line number Diff line number Diff line
@@ -116,6 +116,10 @@ class AppWindowToken extends WindowToken {
    // to differentiate between simple removal of a window and replacement. In the latter case it
    // will preserve the old window until the new one is drawn.
    boolean mReplacingWindow;
    // If true, the replaced window was already requested to be removed.
    boolean mReplacingRemoveRequested;
    // Whether the replacement of the window should trigger app transition animation.
    boolean mAnimateReplacingWindow;

    AppWindowToken(WindowManagerService _service, IApplicationToken _token,
            boolean _voiceInteraction) {
@@ -230,16 +234,24 @@ class AppWindowToken extends WindowToken {
    }

    WindowState findMainWindow() {
        WindowState candidate = null;
        int j = windows.size();
        while (j > 0) {
            j--;
            WindowState win = windows.get(j);
            if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
                    || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
                // In cases where there are multiple windows, we prefer the non-exiting window. This
                // happens for example when when replacing windows during an activity relaunch. When
                // constructing the animation, we want the new window, not the exiting one.
                if (win.mExiting) {
                    candidate = win;
                } else {
                    return win;
                }
            }
        return null;
        }
        return candidate;
    }

    boolean isVisible() {
+51 −7
Original line number Diff line number Diff line
@@ -125,7 +125,6 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
import com.android.server.input.InputManagerService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.power.ShutdownThread;
@@ -290,6 +289,10 @@ public class WindowManagerService extends IWindowManager.Stub

    private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";

    // Used to indicate that if there is already a transition set, it should be preserved when
    // trying to apply a new one.
    private static final boolean ALWAYS_KEEP_CURRENT = true;

    final private KeyguardDisableHandler mKeyguardDisableHandler;

    final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -1740,6 +1743,7 @@ public class WindowManagerService extends IWindowManager.Stub

            boolean addToken = false;
            WindowToken token = mTokenMap.get(attrs.token);
            AppWindowToken atoken = null;
            if (token == null) {
                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                    Slog.w(TAG, "Attempted to add application window with unknown token "
@@ -1774,7 +1778,7 @@ public class WindowManagerService extends IWindowManager.Stub
                token = new WindowToken(this, attrs.token, -1, false);
                addToken = true;
            } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                AppWindowToken atoken = token.appWindowToken;
                atoken = token.appWindowToken;
                if (atoken == null) {
                    Slog.w(TAG, "Attempted to add window with non-application token "
                          + token + ".  Aborting.");
@@ -1920,6 +1924,7 @@ public class WindowManagerService extends IWindowManager.Stub
            final WindowStateAnimator winAnimator = win.mWinAnimator;
            winAnimator.mEnterAnimationPending = true;
            winAnimator.mEnteringAnimation = true;
            prepareWindowReplacementTransition(atoken);

            if (displayContent.isDefaultDisplay) {
                mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets,
@@ -1977,6 +1982,33 @@ public class WindowManagerService extends IWindowManager.Stub
        return res;
    }

    private void prepareWindowReplacementTransition(AppWindowToken atoken) {
        if (atoken == null || !atoken.mReplacingWindow || !atoken.mAnimateReplacingWindow) {
            return;
        }
        atoken.allDrawn = false;
        WindowState replacedWindow = null;
        for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) {
            WindowState candidate = atoken.windows.get(i);
            if (candidate.mExiting) {
                replacedWindow = candidate;
            }
        }
        if (replacedWindow == null) {
            // We expect to already receive a request to remove the old window. If it did not
            // happen, let's just simply add a window.
            return;
        }
        Rect frame = replacedWindow.mFrame;
        // We treat this as if this activity was opening, so we can trigger the app transition
        // animation and piggy-back on existing transition animation infrastructure.
        mOpeningApps.add(atoken);
        prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT);
        mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
                frame.width(), frame.height());
        executeAppTransition();
    }

    /**
     * Returns whether screen capture is disabled for all windows of a specific user.
     */
@@ -2072,6 +2104,7 @@ public class WindowManagerService extends IWindowManager.Stub
                if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Preserving " + win + " until the new one is "
                        + "added");
                win.mExiting = true;
                appToken.mReplacingRemoveRequested = true;
                return;
            }
            // If we are not currently running the exit animation, we
@@ -2730,6 +2763,7 @@ public class WindowManagerService extends IWindowManager.Stub
        try {
            synchronized (mWindowMap) {
                WindowState win = windowForClientLocked(session, client, false);
                if (DEBUG_ADD_REMOVE) Slog.d(TAG, "finishDrawingWindow: " + win);
                if (win != null && win.mWinAnimator.finishDrawingLocked()) {
                    if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                        getDefaultDisplayContentLocked().pendingLayoutChanges |=
@@ -3426,6 +3460,12 @@ public class WindowManagerService extends IWindowManager.Stub
        }
    }

    /**
     * @param transit What kind of transition is happening. Use one of the constants
     *                AppTransition.TRANSIT_*.
     * @param alwaysKeepCurrent If true and a transition is already set, new transition will NOT
     *                          be set.
     */
    @Override
    public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
@@ -3822,10 +3862,13 @@ public class WindowManagerService extends IWindowManager.Stub
        }

        wtoken.willBeHidden = 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.
        if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting)) {
        // 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.
        if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
                (visible && wtoken.mReplacingWindow)) {
            boolean changed = false;
            if (DEBUG_APP_TRANSITIONS) Slog.v(
                TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden
@@ -9723,7 +9766,7 @@ public class WindowManagerService extends IWindowManager.Stub
        return mWindowMap;
    }

    public void setReplacingWindow(IBinder token) {
    public void setReplacingWindow(IBinder token, boolean animate) {
        synchronized (mWindowMap) {
            AppWindowToken appWindowToken = findAppWindowToken(token);
            if (appWindowToken == null) {
@@ -9731,6 +9774,7 @@ public class WindowManagerService extends IWindowManager.Stub
                return;
            }
            appWindowToken.mReplacingWindow = true;
            appWindowToken.mAnimateReplacingWindow = animate;
        }
    }

+10 −0
Original line number Diff line number Diff line
@@ -545,6 +545,14 @@ final class WindowState implements WindowManagerPolicy.WindowState {
    @Override
    public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
            Rect osf) {
        if (mAppToken != null && mAppToken.mReplacingWindow
                && (mExiting || !mAppToken.mReplacingRemoveRequested)) {
            // This window is being replaced and either already got information that it's being
            // removed or we are still waiting for some information. Because of this we don't
            // want to apply any more changes to it, so it remains in this state until new window
            // appears.
            return;
        }
        mHaveFrame = true;

        final Task task = mAppToken != null ? getTask() : null;
@@ -1274,6 +1282,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
        AppWindowToken token = mAppToken;
        if (token != null && token.mReplacingWindow) {
            token.mReplacingWindow = false;
            token.mAnimateReplacingWindow = false;
            token.mReplacingRemoveRequested = false;
            for (int i = token.allAppWindows.size() - 1; i >= 0; i--) {
                WindowState win = token.allAppWindows.get(i);
                if (win.mExiting) {
Loading