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

Commit 50b843da authored by Evan Rosky's avatar Evan Rosky Committed by Android (Google) Code Review
Browse files

Merge "Report Activity as independent change when config-at-end" into main

parents 21754b02 20659908
Loading
Loading
Loading
Loading
+68 −10
Original line number Diff line number Diff line
@@ -176,27 +176,85 @@ object PipUtils {
        return (if (`val` >= 0f) ceil(`val`) else floor(`val`)).toInt()
    }

    /**
     * Calculates the transform to apply on a UNTRANSFORMED (config-at-end) Activity surface in
     * order for it's hint-rect to occupy the same task-relative position/dimensions as it would
     * have at the end of the transition (post-configuration).
     *
     * This is intended to be used in tandem with [calcStartTransform] below applied to the parent
     * task. Applying both transforms simultaneously should result in the appearance of nothing
     * having happened yet.
     *
     * Only the task should be animated (into it's identity state) and then WMCore will reset the
     * activity transform in sync with its new configuration upon finish.
     *
     * Usage example:
     *     calcEndTransform(pipActivity, pipTask, scale, pos);
     *     t.setScale(pipActivity.getLeash(), scale.x, scale.y);
     *     t.setPosition(pipActivity.getLeash(), pos.x, pos.y);
     *
     * @see calcStartTransform
     */
    @JvmStatic
    fun calcEndTransform(pipActivity: TransitionInfo.Change, pipTask: TransitionInfo.Change,
        outScale: PointF, outPos: PointF) {
        val actStartBounds = pipActivity.startAbsBounds
        val actEndBounds = pipActivity.endAbsBounds
        val taskEndBounds = pipTask.endAbsBounds

        var hintRect = pipTask.taskInfo?.pictureInPictureParams?.sourceRectHint
        if (hintRect == null) {
            hintRect = Rect(actStartBounds)
            hintRect.offsetTo(0, 0)
        }

        // FA = final activity bounds (absolute)
        // FT = final task bounds (absolute)
        // SA = start activity bounds (absolute)
        // H = source hint (relative to start activity bounds)
        // We want to transform the activity so that when the task is at FT, H overlaps with FA

        // This scales the activity such that the hint rect has the same dimensions
        // as the final activity bounds.
        val hintToEndScaleX = (actEndBounds.width().toFloat()) / (hintRect.width().toFloat())
        val hintToEndScaleY = (actEndBounds.height().toFloat()) / (hintRect.height().toFloat())
        // top-left needs to be (FA.tl - FT.tl) - H.tl * hintToEnd . H is relative to the
        // activity; so, for example, if shrinking H to FA (hintToEnd < 1), then the tl of the
        // shrunk SA is closer to H than expected, so we need to reduce how much we offset SA
        // to get H.tl to match.
        val startActPosInTaskEndX =
            (actEndBounds.left - taskEndBounds.left) - hintRect.left * hintToEndScaleX
        val startActPosInTaskEndY =
            (actEndBounds.top - taskEndBounds.top) - hintRect.top * hintToEndScaleY
        outScale.set(hintToEndScaleX, hintToEndScaleY)
        outPos.set(startActPosInTaskEndX, startActPosInTaskEndY)
    }

    /**
     * Calculates the transform and crop to apply on a Task surface in order for the config-at-end
     * activity inside it (original-size activity transformed to match it's hint rect to the final
     * Task bounds) to occupy the same world-space position/dimensions as it had before the
     * transition.
     *
     * Intended to be used in tandem with [calcEndTransform].
     *
     * Usage example:
     *     calcStartTransform(pipChange, scale, pos, crop);
     *     t.setScale(pipChange.getLeash(), scale.x, scale.y);
     *     t.setPosition(pipChange.getLeash(), pos.x, pos.y);
     *     t.setCrop(pipChange.getLeash(), crop);
     *     calcStartTransform(pipTask, scale, pos, crop);
     *     t.setScale(pipTask.getLeash(), scale.x, scale.y);
     *     t.setPosition(pipTask.getLeash(), pos.x, pos.y);
     *     t.setCrop(pipTask.getLeash(), crop);
     *
     * @see calcEndTransform
     */
    @JvmStatic
    fun calcStartTransform(pipChange: TransitionInfo.Change, outScale: PointF,
    fun calcStartTransform(pipTask: TransitionInfo.Change, outScale: PointF,
        outPos: PointF, outCrop: Rect) {
        val startBounds = pipChange.startAbsBounds
        val taskEndBounds = pipChange.endAbsBounds
        val startBounds = pipTask.startAbsBounds
        val taskEndBounds = pipTask.endAbsBounds
        // For now, pip activity bounds always matches task bounds. If this ever changes, we'll
        // need to get the activity offset.
        val endBounds = taskEndBounds
        var hintRect = pipChange.taskInfo?.pictureInPictureParams?.sourceRectHint
        var hintRect = pipTask.taskInfo?.pictureInPictureParams?.sourceRectHint
        if (hintRect == null) {
            hintRect = Rect(startBounds)
            hintRect.offsetTo(0, 0)
@@ -226,8 +284,8 @@ object PipUtils {
                + startBounds.left + hintRect.left)
        val endTaskPosForStartY = (-(endBounds.top - taskEndBounds.top) * endToHintScaleY
                + startBounds.top + hintRect.top)
        outScale[endToHintScaleX] = endToHintScaleY
        outPos[endTaskPosForStartX] = endTaskPosForStartY
        outScale.set(endToHintScaleX, endToHintScaleY)
        outPos.set(endTaskPosForStartX, endTaskPosForStartY)

        // now need to set crop to reveal the non-hint stuff. Again, hintrect is relative, so
        // we must apply outsets to reveal the *activity* content which is *inside* the task
+7 −52
Original line number Diff line number Diff line
@@ -547,13 +547,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
            if (!ar.isVisible() || !ar.isVisibleRequested()) return;
            if (mConfigAtEndActivities == null) {
                mConfigAtEndActivities = new ArrayList<>();
            }
            if (mConfigAtEndActivities.contains(ar)) {
            } else if (mConfigAtEndActivities.contains(ar)) {
                return;
            }
            mConfigAtEndActivities.add(ar);
            ar.pauseConfigurationDispatch();
            snapshotStartState(ar);
            collect(ar);
            mChanges.get(ar).mFlags |= ChangeInfo.FLAG_CHANGE_CONFIG_AT_END;
        });
    }
@@ -1705,54 +1704,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
        change.mFlags |= ChangeInfo.FLAG_CHANGE_NO_ANIMATION;
    }

    void prepareConfigAtEnd(SurfaceControl.Transaction transact, ArrayList<ChangeInfo> targets) {
        if (mConfigAtEndActivities == null) return;
        for (int i = 0; i < mConfigAtEndActivities.size(); ++i) {
            final ActivityRecord ar = mConfigAtEndActivities.get(i);
            if (!ar.isVisibleRequested()) continue;
            final SurfaceControl sc = ar.getSurfaceControl();
            if (sc == null) continue;
            final Task task = ar.getTask();
            if (task == null) continue;
            // If task isn't animating, then it means shell is animating activity directly (within
            // task), so don't do any setup.
            if (!containsChangeFor(task, targets)) continue;
            final ChangeInfo change = mChanges.get(ar);
            final Rect startBounds = change.mAbsoluteBounds;
            Rect hintRect = null;
            if (ar.getWindowingMode() == WINDOWING_MODE_PINNED && ar.pictureInPictureArgs != null
                    && ar.pictureInPictureArgs.getSourceRectHint() != null) {
                hintRect = ar.pictureInPictureArgs.getSourceRectHint();
            }
            if (hintRect == null) {
                hintRect = new Rect(startBounds);
                hintRect.offsetTo(0, 0);
            }
            final Rect endBounds = ar.getBounds();
            final Rect taskEndBounds = task.getBounds();
            // FA = final activity bounds (absolute)
            // FT = final task bounds (absolute)
            // SA = start activity bounds (absolute)
            // H = source hint (relative to start activity bounds)
            // We want to transform the activity so that when the task is at FT, H overlaps with FA

            // This scales the activity such that the hint rect has the same dimensions
            // as the final activity bounds.
            float hintToEndScaleX = ((float) endBounds.width()) / ((float) hintRect.width());
            float hintToEndScaleY = ((float) endBounds.height()) / ((float) hintRect.height());
            // top-left needs to be (FA.tl - FT.tl) - H.tl * hintToEnd . H is relative to the
            // activity; so, for example, if shrinking H to FA (hintToEnd < 1), then the tl of the
            // shrunk SA is closer to H than expected, so we need to reduce how much we offset SA
            // to get H.tl to match.
            float startActPosInTaskEndX =
                    (endBounds.left - taskEndBounds.left) - hintRect.left * hintToEndScaleX;
            float startActPosInTaskEndY =
                    (endBounds.top - taskEndBounds.top) - hintRect.top * hintToEndScaleY;
            transact.setScale(sc, hintToEndScaleX, hintToEndScaleY);
            transact.setPosition(sc, startActPosInTaskEndX, startActPosInTaskEndY);
        }
    }

    static boolean containsChangeFor(WindowContainer wc, ArrayList<ChangeInfo> list) {
        for (int i = list.size() - 1; i >= 0; --i) {
            if (list.get(i).mContainer == wc) return true;
@@ -1833,7 +1784,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {

        // Resolve the animating targets from the participants.
        mTargets = calculateTargets(mParticipants, mChanges);
        prepareConfigAtEnd(transaction, mTargets);

        // Check whether the participants were animated from back navigation.
        mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets,
@@ -2669,6 +2619,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
            if (reportIfNotTop(target)) {
                ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
                        "        keep as target %s", target);
            } else if ((targetChange.mFlags & ChangeInfo.FLAG_CHANGE_CONFIG_AT_END) != 0) {
                // config-at-end activities do not match the end-state, so they should be treated
                // as independent.
                ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
                        "        keep as cfg-at-end target %s", target);
            } else {
                ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
                        "        remove from targets %s", target);
+10 −0
Original line number Diff line number Diff line
@@ -2933,6 +2933,11 @@ public class TransitionTests extends WindowTestsBase {

        controller.requestStartTransition(transit, task, null, null);
        player.start();
        // always include config-at-end activity since it is considered "independent" due to
        // changing at a different time.
        assertTrue(player.mLastReady.getChanges().stream()
                .anyMatch((change -> change.getActivityComponent() != null
                        && (change.getFlags() & TransitionInfo.FLAG_CONFIG_AT_END) != 0)));
        assertTrue(activity.isConfigurationDispatchPaused());
        player.finish();
        assertFalse(activity.isConfigurationDispatchPaused());
@@ -2962,6 +2967,11 @@ public class TransitionTests extends WindowTestsBase {

        controller.requestStartTransition(transit, task, null, null);
        player.start();
        // always include config-at-end activity since it is considered "independent" due to
        // changing at a different time.
        assertTrue(player.mLastReady.getChanges().stream()
                .anyMatch((change -> change.getActivityComponent() != null
                        && (change.getFlags() & TransitionInfo.FLAG_CONFIG_AT_END) != 0)));
        assertTrue(activity.isConfigurationDispatchPaused());
        player.finish();
        assertFalse(activity.isConfigurationDispatchPaused());