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

Commit d2acad3f authored by Louis Chang's avatar Louis Chang Committed by Android (Google) Code Review
Browse files

Merge "Restores the exit-pip activity back to overlay container" into main

parents c94dfe89 1557e297
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -189,6 +189,10 @@ public final class TaskFragmentTransaction implements Parcelable {
        @Nullable
        private IBinder mActivityToken;

        /** @see #setOtherActivityToken(IBinder) */
        @Nullable
        private IBinder mOtherActivityToken;

        @Nullable
        private TaskFragmentParentInfo mTaskFragmentParentInfo;

@@ -210,6 +214,7 @@ public final class TaskFragmentTransaction implements Parcelable {
            mActivityToken = in.readStrongBinder();
            mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR);
            mSurfaceControl = in.readTypedObject(SurfaceControl.CREATOR);
            mOtherActivityToken = in.readStrongBinder();
        }

        @Override
@@ -224,6 +229,7 @@ public final class TaskFragmentTransaction implements Parcelable {
            dest.writeStrongBinder(mActivityToken);
            dest.writeTypedObject(mTaskFragmentParentInfo, flags);
            dest.writeTypedObject(mSurfaceControl, flags);
            dest.writeStrongBinder(mOtherActivityToken);
        }

        /** The change is related to the TaskFragment created with this unique token. */
@@ -291,6 +297,21 @@ public final class TaskFragmentTransaction implements Parcelable {
            return this;
        }

        /**
         * Token of another activity.
         * <p>For {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}, it is the next activity (behind the
         * reparented one) that fills the Task and occludes other activities. It will be the
         * actual activity token if the activity belongs to the same process as the organizer.
         * Otherwise, it is {@code null}.
         *
         * @hide
         */
        @NonNull
        public Change setOtherActivityToken(@NonNull IBinder activityToken) {
            mOtherActivityToken = requireNonNull(activityToken);
            return this;
        }

        /**
         * Sets info of the parent Task of the embedded TaskFragment.
         * @see TaskFragmentParentInfo
@@ -350,6 +371,12 @@ public final class TaskFragmentTransaction implements Parcelable {
            return mActivityToken;
        }

        /** @hide */
        @Nullable
        public IBinder getOtherActivityToken() {
            return mOtherActivityToken;
        }

        /**
         * Obtains the {@link TaskFragmentParentInfo} for this transaction.
         */
+4 −1
Original line number Diff line number Diff line
@@ -124,7 +124,10 @@ flag {

flag {
    namespace: "windowing_sdk"
    name: "pip_restore_to_overlay"
    name: "fix_pip_restore_to_overlay"
    description: "Restore exit-pip activity back to ActivityEmbedding overlay"
    bug: "297887697"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
 No newline at end of file
+77 −2
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import static androidx.window.extensions.embedding.SplitPresenter.getActivityInt
import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
import static androidx.window.extensions.embedding.TaskFragmentContainer.OverlayContainerRestoreParams;

import android.annotation.CallbackExecutor;
import android.app.Activity;
@@ -132,6 +133,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    @GuardedBy("mLock")
    private final List<EmbeddingRule> mSplitRules = new ArrayList<>();

    /**
     * Stores the token of the associated Activity that maps to the
     * {@link OverlayContainerRestoreParams} of the most recent created overlay container.
     */
    @GuardedBy("mLock")
    final ArrayMap<IBinder, OverlayContainerRestoreParams> mOverlayRestoreParams = new ArrayMap<>();

    /**
     * A developer-defined {@link SplitAttributes} calculator to compute the current
     * {@link SplitAttributes} with the current device and window states.
@@ -686,11 +694,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                                exception);
                        break;
                    case TYPE_ACTIVITY_REPARENTED_TO_TASK:
                        final IBinder candidateAssociatedActToken, lastOverlayToken;
                        if (Flags.fixPipRestoreToOverlay()) {
                            candidateAssociatedActToken = change.getOtherActivityToken();
                            lastOverlayToken = change.getTaskFragmentToken();
                        } else {
                            candidateAssociatedActToken = lastOverlayToken = null;
                        }
                        onActivityReparentedToTask(
                                wct,
                                taskId,
                                change.getActivityIntent(),
                                change.getActivityToken());
                                change.getActivityToken(),
                                candidateAssociatedActToken,
                                lastOverlayToken);
                        break;
                    default:
                        throw new IllegalArgumentException(
@@ -917,11 +934,28 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     *                       different process, the server will generate a temporary token that
     *                       the organizer can use to reparent the activity through
     *                       {@link WindowContainerTransaction} if needed.
     * @param candidateAssociatedActToken The token of the candidate associated-activity.
     * @param lastOverlayToken The last parent overlay container token.
     */
    @VisibleForTesting
    @GuardedBy("mLock")
    void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct,
            int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken) {
            int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken,
            @Nullable IBinder candidateAssociatedActToken, @Nullable IBinder lastOverlayToken) {
        // Reparent the activity to an overlay container if needed.
        final OverlayContainerRestoreParams params = getOverlayContainerRestoreParams(
                candidateAssociatedActToken, lastOverlayToken);
        if (params != null) {
            final Activity associatedActivity = getActivity(candidateAssociatedActToken);
            final TaskFragmentContainer targetContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
                    wct, params.mOptions, params.mIntent, associatedActivity);
            if (targetContainer != null) {
                wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(),
                        activityToken);
                return;
            }
        }

        // If the activity belongs to the current app process, we treat it as a new activity
        // launch.
        final Activity activity = getActivity(activityToken);
@@ -965,6 +999,43 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        // onTaskFragmentAppeared to trigger updateCallbackIfNecessary().
    }

    /**
     * Returns the {@link OverlayContainerRestoreParams} that stored last time the {@code
     * associatedActivityToken} associated with and only if data matches the {@code overlayToken}.
     * Otherwise, return {@code null}.
     */
    @VisibleForTesting
    @GuardedBy("mLock")
    @Nullable
    OverlayContainerRestoreParams getOverlayContainerRestoreParams(
            @Nullable IBinder associatedActivityToken, @Nullable IBinder overlayToken) {
        if (!Flags.fixPipRestoreToOverlay()) {
            return null;
        }

        if (associatedActivityToken == null || overlayToken == null) {
            return null;
        }

        final TaskFragmentContainer.OverlayContainerRestoreParams params =
                mOverlayRestoreParams.get(associatedActivityToken);
        if (params == null) {
            return null;
        }

        if (params.mOverlayToken != overlayToken) {
            // Not the same overlay container, no need to restore.
            return null;
        }

        final Activity associatedActivity = getActivity(associatedActivityToken);
        if (associatedActivity == null || associatedActivity.isFinishing()) {
            return null;
        }

        return params;
    }

    /**
     * Called when the {@link WindowContainerTransaction} created with
     * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
@@ -1433,6 +1504,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
            mTaskContainers.valueAt(i).onFinishingActivityPaused(wct, activityToken);
        }

        mOverlayRestoreParams.remove(activity.getActivityToken());
        updateCallbackIfNecessary();
    }

@@ -1450,6 +1523,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
            mTaskContainers.valueAt(i).onActivityDestroyed(wct, activityToken);
        }

        mOverlayRestoreParams.remove(activity.getActivityToken());
        // We didn't trigger the callback if there were any pending appeared activities, so check
        // again after the pending is removed.
        updateCallbackIfNecessary();
+31 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;

import java.util.ArrayList;
import java.util.Collections;
@@ -274,6 +275,15 @@ class TaskFragmentContainer {
            addPendingAppearedActivity(pendingAppearedActivity);
        }
        mPendingAppearedIntent = pendingAppearedIntent;

        // Save the information necessary for restoring the overlay when needed.
        if (Flags.fixPipRestoreToOverlay() && overlayTag != null && pendingAppearedIntent != null
                && associatedActivity != null && !associatedActivity.isFinishing()) {
            final IBinder associatedActivityToken = associatedActivity.getActivityToken();
            final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken,
                    launchOptions, pendingAppearedIntent);
            mController.mOverlayRestoreParams.put(associatedActivityToken, params);
        }
    }

    /**
@@ -1105,4 +1115,25 @@ class TaskFragmentContainer {
        }
        return sb.append("]").toString();
    }

    static class OverlayContainerRestoreParams {
        /** The token of the overlay container */
        @NonNull
        final IBinder mOverlayToken;

        /** The launch options to create this container. */
        @NonNull
        final Bundle mOptions;

        /** The Intent that used to be started in the overlay container. */
        @NonNull
        final Intent mIntent;

        OverlayContainerRestoreParams(@NonNull IBinder overlayToken, @NonNull Bundle options,
                @NonNull Intent intent) {
            mOverlayToken = overlayToken;
            mOptions = options;
            mIntent = intent;
        }
    }
}
+24 −0
Original line number Diff line number Diff line
@@ -836,6 +836,30 @@ public class OverlayPresentationTest {
                any());
    }

    @Test
    public void testOnActivityReparentedToTask_overlayRestoration() {
        mSetFlagRule.enableFlags(Flags.FLAG_FIX_PIP_RESTORE_TO_OVERLAY);

        // Prepares and mock the data necessary for the test.
        final IBinder activityToken = mActivity.getActivityToken();
        final Intent intent = new Intent();
        final IBinder fillTaskActivityToken = new Binder();
        final IBinder lastOverlayToken = new Binder();
        final TaskFragmentContainer overlayContainer = mSplitController.newContainer(intent,
                mActivity, TASK_ID);
        final TaskFragmentContainer.OverlayContainerRestoreParams params = mock(
                TaskFragmentContainer.OverlayContainerRestoreParams.class);
        doReturn(params).when(mSplitController).getOverlayContainerRestoreParams(any(), any());
        doReturn(overlayContainer).when(mSplitController).createOrUpdateOverlayTaskFragmentIfNeeded(
                any(), any(), any(), any());

        // Verify the activity should be reparented to the overlay container.
        mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, intent, activityToken,
                fillTaskActivityToken, lastOverlayToken);
        verify(mTransaction).reparentActivityToTaskFragment(
                eq(overlayContainer.getTaskFragmentToken()), eq(activityToken));
    }

    /**
     * A simplified version of {@link SplitController#createOrUpdateOverlayTaskFragmentIfNeeded}
     */
Loading