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

Commit 67c74242 authored by Mady Mellor's avatar Mady Mellor Committed by Android (Google) Code Review
Browse files

Merge "Fix bubbles being empty" into udc-dev

parents e68bdefb 263a7a14
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -388,11 +388,6 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
        }
        // Sync Transactions can't operate simultaneously with shell transition collection.
        if (isUsingShellTransitions()) {
            if (mTaskViewTransitions.hasPending()) {
                // There is already a transition in-flight. The window bounds will be synced
                // once it is complete.
                return;
            }
            mTaskViewTransitions.setTaskBounds(this, boundsOnScreen);
            return;
        }
@@ -489,12 +484,14 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
            finishTransaction.reparent(mTaskLeash, mSurfaceControl)
                    .setPosition(mTaskLeash, 0, 0)
                    .apply();

            mTaskViewTransitions.updateBoundsState(this, mTaskViewBase.getCurrentBoundsOnScreen());
            mTaskViewTransitions.updateVisibilityState(this, true /* visible */);
            wct.setBounds(mTaskToken, mTaskViewBase.getCurrentBoundsOnScreen());
        } else {
            // The surface has already been destroyed before the task has appeared,
            // so go ahead and hide the task entirely
            wct.setHidden(mTaskToken, true /* hidden */);
            mTaskViewTransitions.updateVisibilityState(this, false /* visible */);
            // listener callback is below
        }
        if (newTask) {
+68 −25
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -33,10 +34,13 @@ import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;

import androidx.annotation.VisibleForTesting;

import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.TransitionUtil;

import java.util.ArrayList;
import java.util.Objects;

/**
 * Handles Shell Transitions that involve TaskView tasks.
@@ -44,7 +48,8 @@ import java.util.ArrayList;
public class TaskViewTransitions implements Transitions.TransitionHandler {
    static final String TAG = "TaskViewTransitions";

    private final ArrayList<TaskViewTaskController> mTaskViews = new ArrayList<>();
    private final ArrayMap<TaskViewTaskController, TaskViewRequestedState> mTaskViews =
            new ArrayMap<>();
    private final ArrayList<PendingTransition> mPending = new ArrayList<>();
    private final Transitions mTransitions;
    private final boolean[] mRegistered = new boolean[]{ false };
@@ -54,7 +59,8 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
     * in-flight (collecting) at a time (because otherwise, the operations could get merged into
     * a single transition). So, keep a queue here until we add a queue in server-side.
     */
    private static class PendingTransition {
    @VisibleForTesting
    static class PendingTransition {
        final @WindowManager.TransitionType int mType;
        final WindowContainerTransaction mWct;
        final @NonNull TaskViewTaskController mTaskView;
@@ -78,6 +84,14 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
        }
    }

    /**
     * Visibility and bounds state that has been requested for a {@link TaskViewTaskController}.
     */
    private static class TaskViewRequestedState {
        boolean mVisible;
        Rect mBounds = new Rect();
    }

    public TaskViewTransitions(Transitions transitions) {
        mTransitions = transitions;
        // Defer registration until the first TaskView because we want this to be the "first" in
@@ -92,7 +106,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
                mTransitions.addHandler(this);
            }
        }
        mTaskViews.add(tv);
        mTaskViews.put(tv, new TaskViewRequestedState());
    }

    void removeTaskView(TaskViewTaskController tv) {
@@ -105,25 +119,31 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
    }

    /**
     * Looks through the pending transitions for one matching `taskView`.
     * Looks through the pending transitions for a closing transaction that matches the provided
     * `taskView`.
     * @param taskView the pending transition should be for this.
     * @param closing When true, this only returns a pending transition of the close/hide type.
     *                Otherwise it selects open/show.
     * @param latest When true, this will only check the most-recent pending transition for the
     *               specified taskView. If it doesn't match `closing`, this will return null even
     *               if there is a match earlier. The idea behind this is to check the state of
     *               the taskviews "as if all transitions already happened".
     */
    private PendingTransition findPending(TaskViewTaskController taskView, boolean closing,
            boolean latest) {
    private PendingTransition findPendingCloseTransition(TaskViewTaskController taskView) {
        for (int i = mPending.size() - 1; i >= 0; --i) {
            if (mPending.get(i).mTaskView != taskView) continue;
            if (TransitionUtil.isClosingType(mPending.get(i).mType) == closing) {
            if (TransitionUtil.isClosingType(mPending.get(i).mType)) {
                return mPending.get(i);
            }
            if (latest) {
        }
        return null;
    }

    /**
     * Looks through the pending transitions for one matching `taskView`.
     * @param taskView the pending transition should be for this.
     * @param type the type of transition it's looking for
     */
    PendingTransition findPending(TaskViewTaskController taskView, int type) {
        for (int i = mPending.size() - 1; i >= 0; --i) {
            if (mPending.get(i).mTaskView != taskView) continue;
            if (mPending.get(i).mType == type) {
                return mPending.get(i);
            }
        }
        return null;
    }
@@ -152,7 +172,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
        if (taskView == null) return null;
        // Opening types should all be initiated by shell
        if (!TransitionUtil.isClosingType(request.getType())) return null;
        PendingTransition pending = findPending(taskView, true /* closing */, false /* latest */);
        PendingTransition pending = findPendingCloseTransition(taskView);
        if (pending == null) {
            pending = new PendingTransition(request.getType(), null, taskView, null /* cookie */);
        }
@@ -166,9 +186,9 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {

    private TaskViewTaskController findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
        for (int i = 0; i < mTaskViews.size(); ++i) {
            if (mTaskViews.get(i).getTaskInfo() == null) continue;
            if (taskInfo.token.equals(mTaskViews.get(i).getTaskInfo().token)) {
                return mTaskViews.get(i);
            if (mTaskViews.keyAt(i).getTaskInfo() == null) continue;
            if (taskInfo.token.equals(mTaskViews.keyAt(i).getTaskInfo().token)) {
                return mTaskViews.keyAt(i);
            }
        }
        return null;
@@ -176,30 +196,53 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {

    void startTaskView(@NonNull WindowContainerTransaction wct,
            @NonNull TaskViewTaskController taskView, @NonNull IBinder launchCookie) {
        updateVisibilityState(taskView, true /* visible */);
        mPending.add(new PendingTransition(TRANSIT_OPEN, wct, taskView, launchCookie));
        startNextTransition();
    }

    void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
        PendingTransition pending = findPending(taskView, !visible, true /* latest */);
        if (pending != null) {
            // Already opening or creating a task, so no need to do anything here.
            return;
        }
        if (mTaskViews.get(taskView).mVisible == visible) return;
        if (taskView.getTaskInfo() == null) {
            // Nothing to update, task is not yet available
            return;
        }
        mTaskViews.get(taskView).mVisible = visible;
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setHidden(taskView.getTaskInfo().token, !visible /* hidden */);
        pending = new PendingTransition(
        wct.setBounds(taskView.getTaskInfo().token, mTaskViews.get(taskView).mBounds);
        PendingTransition pending = new PendingTransition(
                visible ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK, wct, taskView, null /* cookie */);
        mPending.add(pending);
        startNextTransition();
        // visibility is reported in transition.
    }

    void updateBoundsState(TaskViewTaskController taskView, Rect boundsOnScreen) {
        TaskViewRequestedState state = mTaskViews.get(taskView);
        state.mBounds.set(boundsOnScreen);
    }

    void updateVisibilityState(TaskViewTaskController taskView, boolean visible) {
        TaskViewRequestedState state = mTaskViews.get(taskView);
        state.mVisible = visible;
    }

    void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) {
        TaskViewRequestedState state = mTaskViews.get(taskView);
        if (Objects.equals(boundsOnScreen, state.mBounds)) {
            return;
        }
        state.mBounds.set(boundsOnScreen);
        if (!state.mVisible) {
            // Task view isn't visible, the bounds will next visibility update.
            return;
        }
        if (hasPending()) {
            // There is already a transition in-flight, the window bounds will be set in
            // prepareOpenAnimation.
            return;
        }
        WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.setBounds(taskView.getTaskInfo().token, boundsOnScreen);
        mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */));
+49 −0
Original line number Diff line number Diff line
@@ -471,4 +471,53 @@ public class TaskViewTest extends ShellTestCase {
        assertThat(insetsInfo.touchableRegion.contains(20, 20)).isFalse();
        assertThat(insetsInfo.touchableRegion.contains(30, 30)).isFalse();
    }

    @Test
    public void testTaskViewPrepareOpenAnimationSetsBoundsAndVisibility() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);

        TaskViewBase taskViewBase = mock(TaskViewBase.class);
        Rect bounds = new Rect(0, 0, 100, 100);
        when(taskViewBase.getCurrentBoundsOnScreen()).thenReturn(bounds);
        mTaskViewTaskController.setTaskViewBase(taskViewBase);

        // Surface created, but task not available so bounds / visibility isn't set
        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
        verify(mTaskViewTransitions, never()).updateVisibilityState(
                eq(mTaskViewTaskController), eq(true));

        // Make the task available / start prepareOpen
        WindowContainerTransaction wct = mock(WindowContainerTransaction.class);
        mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
                new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
                mLeash, wct);

        // Bounds got set
        verify(wct).setBounds(any(WindowContainerToken.class), eq(bounds));
        // Visibility & bounds state got set
        verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController), eq(true));
        verify(mTaskViewTransitions).updateBoundsState(eq(mTaskViewTaskController), eq(bounds));
    }

    @Test
    public void testTaskViewPrepareOpenAnimationSetsVisibilityFalse() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);

        TaskViewBase taskViewBase = mock(TaskViewBase.class);
        Rect bounds = new Rect(0, 0, 100, 100);
        when(taskViewBase.getCurrentBoundsOnScreen()).thenReturn(bounds);
        mTaskViewTaskController.setTaskViewBase(taskViewBase);

        // Task is available, but the surface was never created
        WindowContainerTransaction wct = mock(WindowContainerTransaction.class);
        mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
                new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
                mLeash, wct);

        // Bounds do not get set as there is no surface
        verify(wct, never()).setBounds(any(WindowContainerToken.class), any());
        // Visibility is set to false, bounds aren't set
        verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController), eq(false));
        verify(mTaskViewTransitions, never()).updateBoundsState(eq(mTaskViewTaskController), any());
    }
}
+182 −0
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 com.android.wm.shell.taskview;

import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_FRONT;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.graphics.Rect;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;

import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.transition.Transitions;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class TaskViewTransitionsTest extends ShellTestCase {

    @Mock
    Transitions mTransitions;
    @Mock
    TaskViewTaskController mTaskViewTaskController;
    @Mock
    ActivityManager.RunningTaskInfo mTaskInfo;
    @Mock
    WindowContainerToken mToken;

    TaskViewTransitions mTaskViewTransitions;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            doReturn(true).when(mTransitions).isRegistered();
        }

        mTaskInfo = new ActivityManager.RunningTaskInfo();
        mTaskInfo.token = mToken;
        mTaskInfo.taskId = 314;
        mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);

        mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
        mTaskViewTransitions.addTaskView(mTaskViewTaskController);
        when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo);
    }

    @Test
    public void testSetTaskBounds_taskNotVisible_noTransaction() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);

        mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, false);
        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
                new Rect(0, 0, 100, 100));

        assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
                .isNull();
    }

    @Test
    public void testSetTaskBounds_taskVisible_boundsChangeTransaction() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);

        mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);

        // Consume the pending transaction from visibility change
        TaskViewTransitions.PendingTransition pending =
                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
        assertThat(pending).isNotNull();
        mTaskViewTransitions.startAnimation(pending.mClaimed,
                mock(TransitionInfo.class),
                new SurfaceControl.Transaction(),
                new SurfaceControl.Transaction(),
                mock(Transitions.TransitionFinishCallback.class));
        // Verify it was consumed
        TaskViewTransitions.PendingTransition pending2 =
                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
        assertThat(pending2).isNull();

        // Test that set bounds creates a new transaction
        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
                new Rect(0, 0, 100, 100));
        assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
                .isNotNull();
    }

    @Test
    public void testSetTaskBounds_taskVisibleWithPending_noTransaction() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);

        mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);

        TaskViewTransitions.PendingTransition pending =
                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
        assertThat(pending).isNotNull();

        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
                new Rect(0, 0, 100, 100));
        assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
                .isNull();
    }

    @Test
    public void testSetTaskBounds_sameBounds_noTransaction() {
        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);

        mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);

        // Consume the pending transaction from visibility change
        TaskViewTransitions.PendingTransition pending =
                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
        assertThat(pending).isNotNull();
        mTaskViewTransitions.startAnimation(pending.mClaimed,
                mock(TransitionInfo.class),
                new SurfaceControl.Transaction(),
                new SurfaceControl.Transaction(),
                mock(Transitions.TransitionFinishCallback.class));
        // Verify it was consumed
        TaskViewTransitions.PendingTransition pending2 =
                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
        assertThat(pending2).isNull();

        // Test that set bounds creates a new transaction
        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
                new Rect(0, 0, 100, 100));
        TaskViewTransitions.PendingTransition pendingBounds =
                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE);
        assertThat(pendingBounds).isNotNull();

        // Consume the pending bounds transaction
        mTaskViewTransitions.startAnimation(pendingBounds.mClaimed,
                mock(TransitionInfo.class),
                new SurfaceControl.Transaction(),
                new SurfaceControl.Transaction(),
                mock(Transitions.TransitionFinishCallback.class));
        // Verify it was consumed
        TaskViewTransitions.PendingTransition pendingBounds1 =
                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE);
        assertThat(pendingBounds1).isNull();

        // Test that setting the same bounds doesn't creates a new transaction
        mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
                new Rect(0, 0, 100, 100));
        TaskViewTransitions.PendingTransition pendingBounds2 =
                mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE);
        assertThat(pendingBounds2).isNull();
    }
}