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

Commit 02ecb0b4 authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Calculate launched overlay bounds by calculator" into main

parents 5080367a 219afd3b
Loading
Loading
Loading
Loading
+0 −119
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 androidx.window.extensions.embedding;

import static java.util.Objects.requireNonNull;

import android.graphics.Rect;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

/**
 * The parameter to create an overlay container that retrieved from
 * {@link android.app.ActivityOptions} bundle.
 */
class OverlayCreateParams {

    // TODO(b/295803704): Move them to WM Extensions so that we can reuse in WM Jetpack.
    @VisibleForTesting
    static final String KEY_OVERLAY_CREATE_PARAMS =
            "androidx.window.extensions.OverlayCreateParams";

    @VisibleForTesting
    static final String KEY_OVERLAY_CREATE_PARAMS_TASK_ID =
            "androidx.window.extensions.OverlayCreateParams.taskId";

    @VisibleForTesting
    static final String KEY_OVERLAY_CREATE_PARAMS_TAG =
            "androidx.window.extensions.OverlayCreateParams.tag";

    @VisibleForTesting
    static final String KEY_OVERLAY_CREATE_PARAMS_BOUNDS =
            "androidx.window.extensions.OverlayCreateParams.bounds";

    private final int mTaskId;

    @NonNull
    private final String mTag;

    @NonNull
    private final Rect mBounds;

    OverlayCreateParams(int taskId, @NonNull String tag, @NonNull Rect bounds) {
        mTaskId = taskId;
        mTag = requireNonNull(tag);
        mBounds = requireNonNull(bounds);
    }

    int getTaskId() {
        return mTaskId;
    }

    @NonNull
    String getTag() {
        return mTag;
    }

    @NonNull
    Rect getBounds() {
        return mBounds;
    }

    @Override
    public int hashCode() {
        int result = mTaskId;
        result = 31 * result + mTag.hashCode();
        result = 31 * result + mBounds.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof OverlayCreateParams thatParams)) return false;
        return mTaskId == thatParams.mTaskId
                && mTag.equals(thatParams.mTag)
                && mBounds.equals(thatParams.mBounds);
    }

    @Override
    public String toString() {
        return OverlayCreateParams.class.getSimpleName() + ": {"
                + "taskId=" + mTaskId
                + ", tag=" + mTag
                + ", bounds=" + mBounds
                + "}";
    }

    /** Retrieves the {@link OverlayCreateParams} from {@link android.app.ActivityOptions} bundle */
    @Nullable
    static OverlayCreateParams fromBundle(@NonNull Bundle bundle) {
        final Bundle paramsBundle = bundle.getBundle(KEY_OVERLAY_CREATE_PARAMS);
        if (paramsBundle == null) {
            return null;
        }
        final int taskId = paramsBundle.getInt(KEY_OVERLAY_CREATE_PARAMS_TASK_ID);
        final String tag = requireNonNull(paramsBundle.getString(KEY_OVERLAY_CREATE_PARAMS_TAG));
        final Rect bounds = requireNonNull(paramsBundle.getParcelable(
                KEY_OVERLAY_CREATE_PARAMS_BOUNDS, Rect.class));

        return new OverlayCreateParams(taskId, tag, bounds);
    }
}
+65 −31
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHA
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;

import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
@@ -135,6 +136,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    @Nullable
    private Function<SplitAttributesCalculatorParams, SplitAttributes> mSplitAttributesCalculator;

    /**
     * A calculator function to compute {@link ActivityStack} attributes in a task, which is called
     * when there's {@link #onTaskFragmentParentInfoChanged} or folding state changed.
     */
    @GuardedBy("mLock")
    @Nullable
    private Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
            mActivityStackAttributesCalculator;

    /**
     * Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
     * below it.
@@ -319,6 +329,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
    }

    @Override
    public void setActivityStackAttributesCalculator(
            @NonNull Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
                    calculator) {
        synchronized (mLock) {
            mActivityStackAttributesCalculator = calculator;
        }
    }

    @Override
    public void clearActivityStackAttributesCalculator() {
        synchronized (mLock) {
            mActivityStackAttributesCalculator = null;
        }
    }

    @GuardedBy("mLock")
    @Nullable
    Function<SplitAttributesCalculatorParams, SplitAttributes> getSplitAttributesCalculator() {
@@ -1412,7 +1438,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            @NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
            @Nullable Activity launchingActivity) {
        return createEmptyContainer(wct, intent, taskId, new Rect(), launchingActivity,
                null /* overlayTag */);
                null /* overlayTag */, null /* launchOptions */);
    }

    /**
@@ -1426,7 +1452,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    TaskFragmentContainer createEmptyContainer(
            @NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
            @NonNull Rect bounds, @Nullable Activity launchingActivity,
            @Nullable String overlayTag) {
            @Nullable String overlayTag, @Nullable Bundle launchOptions) {
        // We need an activity in the organizer process in the same Task to use as the owner
        // activity, as well as to get the Task window info.
        final Activity activityInTask;
@@ -1443,7 +1469,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            return null;
        }
        final TaskFragmentContainer container = newContainer(null /* pendingAppearedActivity */,
                intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag);
                intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag,
                launchOptions);
        final IBinder taskFragmentToken = container.getTaskFragmentToken();
        // Note that taskContainer will not exist before calling #newContainer if the container
        // is the first embedded TF in the task.
@@ -1570,14 +1597,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
            @NonNull Activity activityInTask, int taskId) {
        return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
                activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */);
                activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
                null /* launchOptions */);
    }

    @GuardedBy("mLock")
    TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
            @NonNull Activity activityInTask, int taskId) {
        return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
                activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */);
                activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
                null /* launchOptions */);
    }

    @GuardedBy("mLock")
@@ -1585,7 +1614,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                                       @NonNull Activity activityInTask, int taskId,
                                       @NonNull TaskFragmentContainer pairedPrimaryContainer) {
        return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
                activityInTask, taskId, pairedPrimaryContainer, null /* tag */);
                activityInTask, taskId, pairedPrimaryContainer, null /* tag */,
                null /* launchOptions */);
    }

    /**
@@ -1602,11 +1632,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     * @param overlayTag                The tag for the new created overlay container. It must be
     *                                  needed if {@code isOverlay} is {@code true}. Otherwise,
     *                                  it should be {@code null}.
     * @param launchOptions             The launch options bundle to create a container. Must be
     *                                  specified for overlay container.
     */
    @GuardedBy("mLock")
    TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
            @Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId,
            @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag) {
            @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
            @Nullable Bundle launchOptions) {
        if (activityInTask == null) {
            throw new IllegalArgumentException("activityInTask must not be null,");
        }
@@ -1615,7 +1648,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
                pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer, overlayTag);
                pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer, overlayTag,
                launchOptions);
        return container;
    }

@@ -2345,28 +2379,28 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    @GuardedBy("mLock")
    @Nullable
    TaskFragmentContainer createOrUpdateOverlayTaskFragmentIfNeeded(
            @NonNull WindowContainerTransaction wct,
            @NonNull OverlayCreateParams overlayCreateParams, int launchTaskId,
            @NonNull WindowContainerTransaction wct, @NonNull Bundle options,
            @NonNull Intent intent, @NonNull Activity launchActivity) {
        final int taskId = overlayCreateParams.getTaskId();
        if (taskId != launchTaskId) {
            // The task ID doesn't match the launch activity's. Cannot determine the host task
            // to launch the overlay.
            throw new IllegalArgumentException("The task ID of "
                    + "OverlayCreateParams#launchingActivity must match the task ID of "
                    + "the activity to #startActivity with the activity options that takes "
                    + "OverlayCreateParams.");
        }
        final List<TaskFragmentContainer> overlayContainers =
                getAllOverlayTaskFragmentContainers();
        final String overlayTag = overlayCreateParams.getTag();
        final String overlayTag = Objects.requireNonNull(options.getString(KEY_OVERLAY_TAG));

        // If the requested bounds of OverlayCreateParams are smaller than minimum dimensions
        // specified by Intent, expand the overlay container to fill the parent task instead.
        final Rect bounds = overlayCreateParams.getBounds();
        final Size minDimensions = getMinDimensions(intent);
        final boolean shouldExpandContainer = boundsSmallerThanMinDimensions(bounds,
                minDimensions);
        final ActivityStackAttributesCalculatorParams params =
                new ActivityStackAttributesCalculatorParams(mPresenter.toParentContainerInfo(
                        mPresenter.getTaskProperties(launchActivity)), overlayTag, options);
        // Fallback to expand the bounds if there's no activityStackAttributes calculator.
        final Rect relativeBounds = mActivityStackAttributesCalculator != null
                ? new Rect(mActivityStackAttributesCalculator.apply(params).getRelativeBounds())
                : new Rect();
        final boolean shouldExpandContainer = boundsSmallerThanMinDimensions(relativeBounds,
                getMinDimensions(intent));
        // Expand the bounds if the requested bounds are smaller than minimum dimensions.
        if (shouldExpandContainer) {
            relativeBounds.setEmpty();
        }
        final int taskId = getTaskId(launchActivity);
        if (!overlayContainers.isEmpty()) {
            for (final TaskFragmentContainer overlayContainer : overlayContainers) {
                if (!overlayTag.equals(overlayContainer.getOverlayTag())
@@ -2390,7 +2424,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                    final Rect taskBounds = overlayContainer.getTaskContainer().getTaskProperties()
                            .getTaskMetrics().getBounds();
                    final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
                    final Rect sanitizedBounds = sanitizeBounds(bounds, intent, taskBounds);
                    final Rect sanitizedBounds = sanitizeBounds(relativeBounds, intent, taskBounds);
                    mPresenter.resizeTaskFragment(wct, overlayToken, sanitizedBounds);
                    mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayToken,
                            !sanitizedBounds.isEmpty());
@@ -2402,8 +2436,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                }
            }
        }
        return createEmptyContainer(wct, intent, taskId,
                (shouldExpandContainer ? new Rect() : bounds), launchActivity, overlayTag);
        // Launch the overlay container to the task with taskId.
        return createEmptyContainer(wct, intent, taskId, relativeBounds, launchActivity, overlayTag,
                options);
    }

    private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
@@ -2568,12 +2603,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                final TaskFragmentContainer launchedInTaskFragment;
                if (launchingActivity != null) {
                    final int taskId = getTaskId(launchingActivity);
                    final OverlayCreateParams overlayCreateParams =
                            OverlayCreateParams.fromBundle(options);
                    final String overlayTag = options.getString(KEY_OVERLAY_TAG);
                    if (Flags.activityEmbeddingOverlayPresentationFlag()
                            && overlayCreateParams != null) {
                            && overlayTag != null) {
                        launchedInTaskFragment = createOrUpdateOverlayTaskFragmentIfNeeded(wct,
                                overlayCreateParams, taskId, intent, launchingActivity);
                                options, intent, launchingActivity);
                    } else {
                        launchedInTaskFragment = resolveStartActivityIntent(wct, taskId, intent,
                                launchingActivity);
+10 −0
Original line number Diff line number Diff line
@@ -1084,4 +1084,14 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    WindowMetrics getTaskWindowMetrics(@NonNull Activity activity) {
        return getTaskProperties(activity).getTaskMetrics();
    }

    @NonNull
    ParentContainerInfo toParentContainerInfo(@NonNull TaskProperties taskProperties) {
        final Configuration configuration = taskProperties.getConfiguration();
        final WindowLayoutInfo windowLayoutInfo = mWindowLayoutComponent
                .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
                        configuration.windowConfiguration);
        return new ParentContainerInfo(taskProperties.getTaskMetrics(), configuration,
                windowLayoutInfo);
    }
}
+21 −5
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.app.WindowConfiguration.WindowingMode;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Size;
import android.window.TaskFragmentAnimationParams;
@@ -105,6 +106,13 @@ class TaskFragmentContainer {
    @Nullable
    private final String mOverlayTag;

    /**
     * The launch options that was used to create this container. Must not be {@code null} for
     * {@link #isOverlay()} container.
     */
    @Nullable
    private final Bundle mLaunchOptions;

    /** Indicates whether the container was cleaned up after the last activity was removed. */
    private boolean mIsFinished;

@@ -165,7 +173,7 @@ class TaskFragmentContainer {

    /**
     * @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController,
     * TaskFragmentContainer, String)
     * TaskFragmentContainer, String, Bundle)
     */
    TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
                          @Nullable Intent pendingAppearedIntent,
@@ -173,7 +181,8 @@ class TaskFragmentContainer {
                          @NonNull SplitController controller,
                          @Nullable TaskFragmentContainer pairedPrimaryContainer) {
        this(pendingAppearedActivity, pendingAppearedIntent, taskContainer,
                controller, pairedPrimaryContainer, null /* overlayTag */);
                controller, pairedPrimaryContainer, null /* overlayTag */,
                null /* launchOptions */);
    }

    /**
@@ -181,11 +190,14 @@ class TaskFragmentContainer {
     * container transaction.
     * @param pairedPrimaryContainer    when it is set, the new container will be add right above it
     * @param overlayTag                Sets to indicate this taskFragment is an overlay container
     * @param launchOptions             The launch options to create this container. Must not be
     *                                  {@code null} for an overlay container
     */
    TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
            @Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
            @NonNull SplitController controller,
            @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag) {
            @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
            @Nullable Bundle launchOptions) {
        if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
                || (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
            throw new IllegalArgumentException(
@@ -195,6 +207,10 @@ class TaskFragmentContainer {
        mToken = new Binder("TaskFragmentContainer");
        mTaskContainer = taskContainer;
        mOverlayTag = overlayTag;
        if (overlayTag != null) {
            Objects.requireNonNull(launchOptions);
        }
        mLaunchOptions = launchOptions;

        if (pairedPrimaryContainer != null) {
            // The TaskFragment will be positioned right above the paired container.
@@ -903,8 +919,8 @@ class TaskFragmentContainer {
    }

    /**
     * Returns the tag specified in {@link OverlayCreateParams#getTag()}. {@code null} if this
     * taskFragment container is not an overlay container.
     * Returns the tag specified in launch options. {@code null} if this taskFragment container is
     * not an overlay container.
     */
    @Nullable
    String getOverlayTag() {
+47 −73

File changed.

Preview size limit exceeded, changes collapsed.

Loading