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

Commit 0dd1586b authored by Evan Rosky's avatar Evan Rosky
Browse files

Scaffolding for Shell Transitions

Initial rough implementation of Shell Transitions.

Shell Transitions can be enabled by setting ENABLE_SHELL_TRANSITIONS
via `adb shell setprop persist.debug.shell_transit 1`

The main structure behind this is:

- TransitionController in Core responsible for handling the
collection of transition participants and communicating with
the TransitionPlayer in Shell.

- Transitions in Shell responsible for starting transitions
and playing associated animations.

This initial version only supports task open/show/close and does
a simple fade. The flow is:
1. activityStarter calls to Transitions.requestStartTransition
2. Transitions will prepare to start and then call
   WindowOrganizerController.startTransition().
2. Various operations in Core call TransitionController.collect()
   to track participants. This collect() acts as a replacement for
   adding to mOpening/ClosingApps.
3. The legacy executeAppTransition() is wired into
   TransitionController.setReady() for now. This signal
   is used to ready a BLASTSyncEngine.
4. When all participants are done drawing, TransitionController
   will commit any showing visibility changes and then will construct
   a TransitionInfo object summarizing all the changes needed for
   animation. This gets sent to Transitions.onTransitionReady()
5. Transitions then plays the transition animation and on
   completion will call WindowOrganizerController.finishTransition()
   which will tell TransitionController to do any finishing
   work (like commiting deferred hide changes).

Bug: 161980187
Test: enable the flag and try opening/closing tasks. Also added
      TransitionControllerTests
Change-Id: I80c3a241fc12004a894c26523a1e5828e0c533ff
parent 282c68d3
Loading
Loading
Loading
Loading
+63 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.window;

import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.WindowContainerTransaction;

/**
 * Implemented by WMShell to initiate and play transition animations.
 * The flow (with {@link IWindowOrganizerController}) looks like this:
 * <p><ol>
 *  <li>Core starts an activity and calls {@link #requestStartTransition}
 *  <li>This TransitionPlayer impl does whatever, then calls
 *      {@link IWindowOrganizerController#startTransition} to tell Core to formally start (until
 *      this happens, Core will collect changes on the transition, but won't consider it ready to
 *      animate).
 *  <li>Once all collected changes on the transition have finished drawing, Core will then call
 *      {@link #onTransitionReady} here to delegate the actual animation.
 *  <li>Once this TransitionPlayer impl finishes animating, it notifies Core via
 *      {@link IWindowOrganizerController#finishTransition}. At this point, ITransitionPlayer's
 *      responsibilities end.
 * </ul>
 *
 * {@hide}
 */
oneway interface ITransitionPlayer {

    /**
     * Called when all participants of a transition are ready to animate. This is in response to
     * {@link IWindowOrganizerController#startTransition}.
     *
     * @param transitionToken An identifying token for the transition that is now ready to animate.
     * @param info A collection of all the changes encapsulated by this transition.
     * @param t A surface transaction containing the surface state prior to animating.
     */
    void onTransitionReady(in IBinder transitionToken, in TransitionInfo info,
            in SurfaceControl.Transaction t);

    /**
     * Called when something in WMCore requires a transition to play -- for example when an Activity
     * is started in a new Task.
     *
     * @param type The {@link WindowManager#TransitionType} of the transition to start.
     * @param transitionToken An identifying token for the transition that needs to be started.
     *                        Pass this to {@link IWindowOrganizerController#startTransition}.
     */
    void requestStartTransition(int type, in IBinder transitionToken);
}
+32 −0
Original line number Diff line number Diff line
@@ -18,8 +18,10 @@ package android.window;

import android.view.SurfaceControl;

import android.os.IBinder;
import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -45,6 +47,30 @@ interface IWindowOrganizerController {
    int applySyncTransaction(in WindowContainerTransaction t,
            in IWindowContainerTransactionCallback callback);

    /**
     * Starts a transition.
     * @param type The transition type.
     * @param transitionToken A token associated with the transition to start. If null, a new
     *                        transition will be created of the provided type.
     * @param t Operations that are part of the transition.
     * @return a token representing the transition. This will just be transitionToken if it was
     *         non-null.
     */
    IBinder startTransition(int type, in @nullable IBinder transitionToken,
            in @nullable WindowContainerTransaction t);

    /**
     * Finishes a transition. This must be called for all created transitions.
     * @param transitionToken Which transition to finish
     * @param t Changes to make before finishing but in the same SF Transaction. Can be null.
     * @param callback Called when t is finished applying.
     * @return An ID for the sync operation (see {@link #applySyncTransaction}. This will be
     *         negative if no sync transaction was attached (null t or callback)
     */
    int finishTransition(in IBinder transitionToken,
            in @nullable WindowContainerTransaction t,
            in IWindowContainerTransactionCallback callback);

    /** @return An interface enabling the management of task organizers. */
    ITaskOrganizerController getTaskOrganizerController();

@@ -61,4 +87,10 @@ interface IWindowOrganizerController {
     * @return true if the screenshot was successful, false otherwise.
     */
    boolean takeScreenshot(in WindowContainerToken token, out SurfaceControl outSurfaceControl);

    /**
     * Registers a transition player with Core. There is only one of these at a time and calling
     * this will replace the existing one if set.
     */
    void registerTransitionPlayer(in ITransitionPlayer player);
}
+20 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.window;

parcelable TransitionInfo;
parcelable TransitionInfo.Change;
+290 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 android.window;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.SurfaceControl;
import android.view.WindowManager;

import java.util.ArrayList;
import java.util.List;

/**
 * Used to communicate information about what is changing during a transition to a TransitionPlayer.
 * @hide
 */
public final class TransitionInfo implements Parcelable {

    /** No transition mode. This is a placeholder, don't use this as an actual mode. */
    public static final int TRANSIT_NONE = 0;

    /** The container didn't exist before but will exist and be visible after. */
    public static final int TRANSIT_OPEN = 1;

    /** The container existed and was visible before but won't exist after. */
    public static final int TRANSIT_CLOSE = 2;

    /** The container existed before but was invisible and will be visible after. */
    public static final int TRANSIT_SHOW = 3;

    /** The container is going from visible to invisible but it will still exist after. */
    public static final int TRANSIT_HIDE = 4;

    /** The container exists and is visible before and after but it changes. */
    public static final int TRANSIT_CHANGE = 5;

    /** @hide */
    @IntDef(prefix = { "TRANSIT_" }, value = {
            TRANSIT_NONE,
            TRANSIT_OPEN,
            TRANSIT_CLOSE,
            TRANSIT_SHOW,
            TRANSIT_HIDE,
            TRANSIT_CHANGE
    })
    public @interface TransitionMode {}

    private final @WindowManager.TransitionType int mType;
    private final ArrayList<Change> mChanges = new ArrayList<>();

    /** @hide */
    public TransitionInfo(@WindowManager.TransitionType int type) {
        mType = type;
    }

    private TransitionInfo(Parcel in) {
        mType = in.readInt();
        in.readList(mChanges, null /* classLoader */);
    }

    @Override
    /** @hide */
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mType);
        dest.writeList(mChanges);
    }

    @NonNull
    public static final Creator<TransitionInfo> CREATOR =
            new Creator<TransitionInfo>() {
                @Override
                public TransitionInfo createFromParcel(Parcel in) {
                    return new TransitionInfo(in);
                }

                @Override
                public TransitionInfo[] newArray(int size) {
                    return new TransitionInfo[size];
                }
            };

    @Override
    /** @hide */
    public int describeContents() {
        return 0;
    }

    public int getType() {
        return mType;
    }

    @NonNull
    public List<Change> getChanges() {
        return mChanges;
    }

    /**
     * @return the Change that a window is undergoing or {@code null} if not directly
     * represented.
     */
    @Nullable
    public Change getChange(@NonNull WindowContainerToken token) {
        for (int i = mChanges.size() - 1; i >= 0; --i) {
            if (mChanges.get(i).mContainer == token) {
                return mChanges.get(i);
            }
        }
        return null;
    }

    /**
     * Add a {@link Change} to this transition.
     */
    public void addChange(@NonNull Change change) {
        mChanges.add(change);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{t=" + mType + " c=[");
        for (int i = 0; i < mChanges.size(); ++i) {
            if (i > 0) {
                sb.append(',');
            }
            sb.append(mChanges.get(i));
        }
        sb.append("]}");
        return sb.toString();
    }

    /** Converts a transition mode/action to its string representation. */
    @NonNull
    public static String modeToString(@TransitionMode int mode) {
        switch(mode) {
            case TRANSIT_NONE: return "NONE";
            case TRANSIT_OPEN: return "OPEN";
            case TRANSIT_CLOSE: return "CLOSE";
            case TRANSIT_SHOW: return "SHOW";
            case TRANSIT_HIDE: return "HIDE";
            case TRANSIT_CHANGE: return "CHANGE";
            default: return "<unknown:" + mode + ">";
        }
    }

    /** Represents the change a WindowContainer undergoes during a transition */
    public static final class Change implements Parcelable {
        private final WindowContainerToken mContainer;
        private WindowContainerToken mParent;
        private final SurfaceControl mLeash;
        private int mMode = TRANSIT_NONE;
        private final Rect mStartBounds = new Rect();
        private final Rect mEndBounds = new Rect();

        public Change(@NonNull WindowContainerToken container, @NonNull SurfaceControl leash) {
            mContainer = container;
            mLeash = leash;
        }

        private Change(Parcel in) {
            mContainer = WindowContainerToken.CREATOR.createFromParcel(in);
            mParent = in.readParcelable(WindowContainerToken.class.getClassLoader());
            mLeash = new SurfaceControl();
            mLeash.readFromParcel(in);
            mMode = in.readInt();
            mStartBounds.readFromParcel(in);
            mEndBounds.readFromParcel(in);
        }

        /** Sets the parent of this change's container. The parent must be a participant or null. */
        public void setParent(@Nullable WindowContainerToken parent) {
            mParent = parent;
        }

        /** Sets the transition mode for this change */
        public void setMode(@TransitionMode int mode) {
            mMode = mode;
        }

        /** Sets the bounds this container occupied before the change */
        public void setStartBounds(@Nullable Rect rect) {
            mStartBounds.set(rect);
        }

        /** Sets the bounds this container will occupy after the change */
        public void setEndBounds(@Nullable Rect rect) {
            mEndBounds.set(rect);
        }

        /** @return the container that is changing */
        @NonNull
        public WindowContainerToken getContainer() {
            return mContainer;
        }

        /**
         * @return the parent of the changing container. This is the parent within the participants,
         * not necessarily the actual parent.
         */
        @Nullable
        public WindowContainerToken getParent() {
            return mParent;
        }

        /** @return which action this change represents. */
        public @TransitionMode int getMode() {
            return mMode;
        }

        /**
         * @return the bounds of the container before the change. It may be empty if the container
         * is coming into existence.
         */
        @NonNull
        public Rect getStartBounds() {
            return mStartBounds;
        }

        /**
         * @return the bounds of the container after the change. It may be empty if the container
         * is disappearing.
         */
        @NonNull
        public Rect getEndBounds() {
            return mEndBounds;
        }

        /** @return the leash or surface to animate for this container */
        @NonNull
        public SurfaceControl getLeash() {
            return mLeash;
        }

        @Override
        /** @hide */
        public void writeToParcel(@NonNull Parcel dest, int flags) {
            mContainer.writeToParcel(dest, flags);
            dest.writeParcelable(mParent, 0);
            mLeash.writeToParcel(dest, flags);
            dest.writeInt(mMode);
            mStartBounds.writeToParcel(dest, flags);
            mEndBounds.writeToParcel(dest, flags);
        }

        @NonNull
        public static final Creator<Change> CREATOR =
                new Creator<Change>() {
                    @Override
                    public Change createFromParcel(Parcel in) {
                        return new Change(in);
                    }

                    @Override
                    public Change[] newArray(int size) {
                        return new Change[size];
                    }
                };

        @Override
        /** @hide */
        public int describeContents() {
            return 0;
        }

        @Override
        public String toString() {
            return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
                    + " m=" + modeToString(mMode) + " sb=" + mStartBounds
                    + " eb=" + mEndBounds + "}";
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -77,6 +77,11 @@ public final class WindowContainerToken implements Parcelable {
        return mRealToken.asBinder().hashCode();
    }

    @Override
    public String toString() {
        return "WCT{" + mRealToken + "}";
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof WindowContainerToken)) {
Loading