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

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

Merge "Track visibility in onTaskFragmentParentInfoChanged" into tm-qpr-dev

parents 2e95b5f6 a13ffe82
Loading
Loading
Loading
Loading
+25 −20
Original line number Diff line number Diff line
@@ -354,18 +354,27 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    @GuardedBy("mLock")
    void onTaskFragmentParentInfoChanged(@NonNull WindowContainerTransaction wct,
            int taskId, @NonNull TaskFragmentParentInfo parentInfo) {
        // TODO(b/241043111): handles displayId and visibility here.
        final Configuration parentConfig = parentInfo.getConfiguration();
        onTaskConfigurationChanged(taskId, parentConfig);
        if (isInPictureInPicture(parentConfig)) {
            // No need to update presentation in PIP until the Task exit PIP.
            return;
        }
        final TaskContainer taskContainer = getTaskContainer(taskId);
        if (taskContainer == null || taskContainer.isEmpty()) {
            Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
            return;
        }
        taskContainer.updateTaskFragmentParentInfo(parentInfo);
        if (!taskContainer.isVisible()) {
            // Don't update containers if the task is not visible. We only update containers when
            // parentInfo#isVisibleRequested is true.
            return;
        }
        onTaskContainerInfoChanged(taskContainer, parentInfo.getConfiguration());
        if (isInPictureInPicture(parentInfo.getConfiguration())) {
            // No need to update presentation in PIP until the Task exit PIP.
            return;
        }
        updateContainersInTask(wct, taskContainer);
    }

    private void updateContainersInTask(@NonNull WindowContainerTransaction wct,
            @NonNull TaskContainer taskContainer) {
        // Update all TaskFragments in the Task. Make a copy of the list since some may be
        // removed on updating.
        final List<TaskFragmentContainer> containers =
@@ -490,6 +499,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    }

    /** Called on receiving {@link #onTaskFragmentVanished} for cleanup. */
    @GuardedBy("mLock")
    private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
            final TaskContainer taskContainer = mTaskContainers.valueAt(i);
@@ -505,14 +515,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
    }

    private void onTaskConfigurationChanged(int taskId, @NonNull Configuration config) {
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        if (taskContainer == null) {
            return;
        }
    @GuardedBy("mLock")
    private void onTaskContainerInfoChanged(@NonNull TaskContainer taskContainer,
            @NonNull Configuration config) {
        final boolean wasInPip = taskContainer.isInPictureInPicture();
        final boolean isInPIp = isInPictureInPicture(config);
        taskContainer.setWindowingMode(config.windowConfiguration.getWindowingMode());

        // We need to check the animation override when enter/exit PIP or has bounds changed.
        boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
@@ -536,8 +543,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            // Animation will be handled by WM Shell with Shell transition enabled.
            return;
        }
        if (!taskContainer.isTaskBoundsInitialized()
                || !taskContainer.isWindowingModeInitialized()) {
        if (!taskContainer.isTaskBoundsInitialized()) {
            // We don't know about the Task bounds/windowingMode yet.
            return;
        }
@@ -1024,6 +1030,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    /**
     * Returns an empty expanded {@link TaskFragmentContainer} that we can launch an activity into.
     */
    @GuardedBy("mLock")
    @Nullable
    private TaskFragmentContainer createEmptyExpandedContainer(
            @NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@@ -1105,12 +1112,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        return newContainer(pendingAppearedActivity, pendingAppearedActivity, taskId);
    }

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

    @GuardedBy("mLock")
    TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
            @NonNull Activity activityInTask, int taskId) {
        return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
@@ -1134,7 +1143,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            throw new IllegalArgumentException("activityInTask must not be null,");
        }
        if (!mTaskContainers.contains(taskId)) {
            mTaskContainers.put(taskId, new TaskContainer(taskId));
            mTaskContainers.put(taskId, new TaskContainer(taskId, activityInTask));
        }
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
@@ -1146,10 +1155,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
            }
        }
        if (!taskContainer.isWindowingModeInitialized()) {
            taskContainer.setWindowingMode(activityInTask.getResources().getConfiguration()
                    .windowConfiguration.getWindowingMode());
        }
        updateAnimationOverride(taskContainer);
        return container;
    }
+58 −16
Original line number Diff line number Diff line
@@ -24,10 +24,13 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.Activity;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -42,13 +45,10 @@ class TaskContainer {
    /** The unique task id. */
    private final int mTaskId;

    // TODO(b/240219484): consolidate to mConfiguration
    /** Available window bounds of this Task. */
    private final Rect mTaskBounds = new Rect();

    /** Windowing mode of this Task. */
    @WindowingMode
    private int mWindowingMode = WINDOWING_MODE_UNDEFINED;

    /** Active TaskFragments in this Task. */
    @NonNull
    final List<TaskFragmentContainer> mContainers = new ArrayList<>();
@@ -57,24 +57,56 @@ class TaskContainer {
    @NonNull
    final List<SplitContainer> mSplitContainers = new ArrayList<>();

    @NonNull
    private final Configuration mConfiguration;

    private int mDisplayId;

    private boolean mIsVisible;

    /**
     * TaskFragments that the organizer has requested to be closed. They should be removed when
     * the organizer receives {@link SplitController#onTaskFragmentVanished(TaskFragmentInfo)} event
     * for them.
     * the organizer receives
     * {@link SplitController#onTaskFragmentVanished(WindowContainerTransaction, TaskFragmentInfo)}
     * event for them.
     */
    final Set<IBinder> mFinishedContainer = new ArraySet<>();

    TaskContainer(int taskId) {
    /**
     * The {@link TaskContainer} constructor
     *
     * @param taskId The ID of the Task, which must match {@link Activity#getTaskId()} with
     *               {@code activityInTask}.
     * @param activityInTask The {@link Activity} in the Task with {@code taskId}. It is used to
     *                       initialize the {@link TaskContainer} properties.
     *
     */
    TaskContainer(int taskId, @NonNull Activity activityInTask) {
        if (taskId == INVALID_TASK_ID) {
            throw new IllegalArgumentException("Invalid Task id");
        }
        mTaskId = taskId;
        // Make a copy in case the activity's config is updated, and updates the TaskContainer's
        // config unexpectedly.
        mConfiguration = new Configuration(activityInTask.getResources().getConfiguration());
        mDisplayId = activityInTask.getDisplayId();
        // Note that it is always called when there's a new Activity is started, which implies
        // the host task is visible.
        mIsVisible = true;
    }

    int getTaskId() {
        return mTaskId;
    }

    int getDisplayId() {
        return mDisplayId;
    }

    boolean isVisible() {
        return mIsVisible;
    }

    @NonNull
    Rect getTaskBounds() {
        return mTaskBounds;
@@ -94,13 +126,16 @@ class TaskContainer {
        return !mTaskBounds.isEmpty();
    }

    void setWindowingMode(int windowingMode) {
        mWindowingMode = windowingMode;
    @NonNull
    Configuration getConfiguration() {
        // Make a copy in case the config is updated unexpectedly.
        return new Configuration(mConfiguration);
    }

    /** Whether the Task windowing mode has been initialized. */
    boolean isWindowingModeInitialized() {
        return mWindowingMode != WINDOWING_MODE_UNDEFINED;
    void updateTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
        mConfiguration.setTo(info.getConfiguration());
        mDisplayId = info.getDisplayId();
        mIsVisible = info.isVisibleRequested();
    }

    /**
@@ -123,13 +158,20 @@ class TaskContainer {
        // DecorCaptionView won't work correctly. As a result, have the TaskFragment to be in the
        // Task windowing mode if the Task is in multi window.
        // TODO we won't need this anymore after we migrate Freeform caption to WM Shell.
        return WindowConfiguration.inMultiWindowMode(mWindowingMode)
                ? mWindowingMode
                : WINDOWING_MODE_MULTI_WINDOW;
        return isInMultiWindow() ? getWindowingMode() : WINDOWING_MODE_MULTI_WINDOW;
    }

    boolean isInPictureInPicture() {
        return mWindowingMode == WINDOWING_MODE_PINNED;
        return getWindowingMode() == WINDOWING_MODE_PINNED;
    }

    boolean isInMultiWindow() {
        return WindowConfiguration.inMultiWindowMode(getWindowingMode());
    }

    @WindowingMode
    private int getWindowingMode() {
        return getConfiguration().windowConfiguration.getWindowingMode();
    }

    /** Whether there is any {@link TaskFragmentContainer} below this Task. */
+14 −0
Original line number Diff line number Diff line
@@ -16,9 +16,12 @@

package androidx.window.extensions.embedding;

import static android.view.Display.DEFAULT_DISPLAY;

import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;

import android.annotation.NonNull;
@@ -26,6 +29,7 @@ import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Pair;
@@ -130,4 +134,14 @@ public class EmbeddingTestUtils {
                primaryBounds.width() + 1, primaryBounds.height() + 1);
        return aInfo;
    }

    static TaskContainer createTestTaskContainer() {
        Resources resources = mock(Resources.class);
        doReturn(new Configuration()).when(resources).getConfiguration();
        Activity activity = mock(Activity.class);
        doReturn(resources).when(activity).getResources();
        doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();

        return new TaskContainer(TASK_ID, activity);
    }
}
+8 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -26,12 +27,14 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;

import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentTransaction;
@@ -65,7 +68,10 @@ public class JetpackTaskFragmentOrganizerTest {
    private WindowContainerTransaction mTransaction;
    @Mock
    private JetpackTaskFragmentOrganizer.TaskFragmentCallback mCallback;
    @Mock
    private SplitController mSplitController;
    @Mock
    private Handler mHandler;
    private JetpackTaskFragmentOrganizer mOrganizer;

    @Before
@@ -73,9 +79,8 @@ public class JetpackTaskFragmentOrganizerTest {
        MockitoAnnotations.initMocks(this);
        mOrganizer = new JetpackTaskFragmentOrganizer(Runnable::run, mCallback);
        mOrganizer.registerOrganizer();
        mSplitController = new SplitController();
        spyOn(mOrganizer);
        spyOn(mSplitController);
        doReturn(mHandler).when(mSplitController).getHandler();
    }

    @Test
@@ -113,7 +118,7 @@ public class JetpackTaskFragmentOrganizerTest {

    @Test
    public void testExpandTaskFragment() {
        final TaskContainer taskContainer = new TaskContainer(TASK_ID);
        final TaskContainer taskContainer = createTestTaskContainer();
        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
                new Intent(), taskContainer, mSplitController);
        final TaskFragmentInfo info = createMockInfo(container);
+5 −2
Original line number Diff line number Diff line
@@ -34,9 +34,11 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;

@@ -140,7 +142,7 @@ public class SplitControllerTest {

    @Test
    public void testGetTopActiveContainer() {
        final TaskContainer taskContainer = new TaskContainer(TASK_ID);
        final TaskContainer taskContainer = createTestTaskContainer();
        // tf1 has no running activity so is not active.
        final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
                new Intent(), taskContainer, mSplitController);
@@ -200,6 +202,7 @@ public class SplitControllerTest {
    @Test
    public void testOnTaskFragmentAppearEmptyTimeout() {
        final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
        doCallRealMethod().when(mSplitController).onTaskFragmentAppearEmptyTimeout(any(), any());
        mSplitController.onTaskFragmentAppearEmptyTimeout(mTransaction, tf);

        verify(mSplitPresenter).cleanupContainer(mTransaction, tf,
@@ -310,7 +313,7 @@ public class SplitControllerTest {
    @Test
    public void testOnStartActivityResultError() {
        final Intent intent = new Intent();
        final TaskContainer taskContainer = new TaskContainer(TASK_ID);
        final TaskContainer taskContainer = createTestTaskContainer();
        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
                intent, taskContainer, mSplitController);
        final SplitController.ActivityStartMonitor monitor =
Loading