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

Commit d8dac94e authored by Chris Li's avatar Chris Li
Browse files

Set TaskFragment surface window crop

We don't want the child window to show outside of the TaskFragment.

Bug: 201261613
Bug: 196173550
Test: atest WmTests:TaskFragmentTest
Change-Id: I8350c5c57a79be9b2c7804847ebb1a5ffc4af015
parent 4ffcb718
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -480,7 +480,6 @@ class Task extends TaskFragment {

    private Dimmer mDimmer = new Dimmer(this);
    private final Rect mTmpDimBoundsRect = new Rect();
    private final Point mLastSurfaceSize = new Point();

    /** @see #setCanAffectSystemUiFlags */
    private boolean mCanAffectSystemUiFlags = true;
+57 −5
Original line number Diff line number Diff line
@@ -240,6 +240,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
     */
    private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID;

    final Point mLastSurfaceSize = new Point();

    private final Rect mTmpInsets = new Rect();
    private final Rect mTmpBounds = new Rect();
    private final Rect mTmpFullBounds = new Rect();
@@ -1654,6 +1656,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        }
    }

    @Override
    void onChildPositionChanged(WindowContainer child) {
        super.onChildPositionChanged(child);

@@ -2049,14 +2052,58 @@ class TaskFragment extends WindowContainer<WindowContainer> {
        if (shouldStartChangeTransition(mTmpPrevBounds)) {
            initializeChangeTransition(mTmpPrevBounds);
        } else if (mTaskFragmentOrganizer != null) {
            // Update the surface position here instead of in the organizer so that we can make sure
            // Update the surface here instead of in the organizer so that we can make sure
            // it can be synced with the surface freezer.
            updateSurfacePosition(getSyncTransaction());
            final SurfaceControl.Transaction t = getSyncTransaction();
            updateSurfacePosition(t);
            updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
        }

        sendTaskFragmentInfoChanged();
    }

    /** Updates the surface size so that the sub windows cannot be shown out of bounds. */
    private void updateOrganizedTaskFragmentSurfaceSize(SurfaceControl.Transaction t,
            boolean forceUpdate) {
        if (mTaskFragmentOrganizer == null) {
            // We only want to update for organized TaskFragment. Task will handle itself.
            return;
        }
        if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) {
            return;
        }

        final Rect bounds = getBounds();
        final int width = bounds.width();
        final int height = bounds.height();
        if (!forceUpdate && width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
            return;
        }
        t.setWindowCrop(mSurfaceControl, width, height);
        mLastSurfaceSize.set(width, height);
    }

    @Override
    public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
        super.onAnimationLeashCreated(t, leash);
        // Reset surface bounds for animation. It will be taken care by the animation leash, and
        // reset again onAnimationLeashLost.
        if (mTaskFragmentOrganizer != null
                && (mLastSurfaceSize.x != 0 || mLastSurfaceSize.y != 0)) {
            t.setWindowCrop(mSurfaceControl, 0, 0);
            mLastSurfaceSize.set(0, 0);
        }
    }

    @Override
    public void onAnimationLeashLost(SurfaceControl.Transaction t) {
        super.onAnimationLeashLost(t);
        // Update the surface bounds after animation.
        if (mTaskFragmentOrganizer != null) {
            updateOrganizedTaskFragmentSurfaceSize(t, true /* forceUpdate */);
        }
    }

    /** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
    private boolean shouldStartChangeTransition(Rect startBounds) {
        if (mWmService.mDisableTransitionAnimation
@@ -2075,10 +2122,15 @@ class TaskFragment extends WindowContainer<WindowContainer> {
    @Override
    void setSurfaceControl(SurfaceControl sc) {
        super.setSurfaceControl(sc);
        if (mTaskFragmentOrganizer != null) {
            final SurfaceControl.Transaction t = getSyncTransaction();
            updateSurfacePosition(t);
            updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
            // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to
            // emit the callbacks now.
            sendTaskFragmentAppeared();
        }
    }

    void sendTaskFragmentInfoChanged() {
        if (mTaskFragmentOrganizer != null) {
+1 −1
Original line number Diff line number Diff line
@@ -3159,7 +3159,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
    void updateSurfacePosition(Transaction t) {
        if (mSurfaceControl == null || mSurfaceAnimator.hasLeash()) {
        if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) {
            return;
        }

+105 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.server.wm;

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

import static org.mockito.Mockito.clearInvocations;

import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;

import androidx.test.filters.MediumTest;

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

/**
 * Test class for {@link TaskFragment}.
 *
 * Build/Install/Run:
 *  atest WmTests:TaskFragmentTest
 */
@MediumTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class TaskFragmentTest extends WindowTestsBase {

    private TaskFragmentOrganizer mOrganizer;
    private TaskFragment mTaskFragment;
    private SurfaceControl mLeash;
    @Mock
    private SurfaceControl.Transaction mTransaction;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mOrganizer = new TaskFragmentOrganizer(Runnable::run);
        final ITaskFragmentOrganizer iOrganizer =
                ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken().asBinder());
        mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController
                .registerOrganizer(iOrganizer);
        mTaskFragment = new TaskFragmentBuilder(mAtm)
                .setCreateParentTask()
                .setOrganizer(mOrganizer)
                .build();
        mLeash = mTaskFragment.getSurfaceControl();
        spyOn(mTaskFragment);
        doReturn(mTransaction).when(mTaskFragment).getSyncTransaction();
        doReturn(mTransaction).when(mTaskFragment).getPendingTransaction();
    }

    @Test
    public void testOnConfigurationChanged_updateSurface() {
        final Rect bounds = new Rect(100, 100, 1100, 1100);
        mTaskFragment.setBounds(bounds);

        verify(mTransaction).setPosition(mLeash, 100, 100);
        verify(mTransaction).setWindowCrop(mLeash, 1000, 1000);
    }

    @Test
    public void testStartChangeTransition_resetSurface() {
        final Rect startBounds = new Rect(0, 0, 1000, 1000);
        final Rect endBounds = new Rect(500, 500, 1000, 1000);
        mTaskFragment.setBounds(startBounds);
        doReturn(true).when(mTaskFragment).isVisible();

        clearInvocations(mTransaction);
        mTaskFragment.setBounds(endBounds);

        // Surface reset when prepare transition.
        verify(mTaskFragment).initializeChangeTransition(startBounds);
        verify(mTransaction).setPosition(mLeash, 0, 0);
        verify(mTransaction).setWindowCrop(mLeash, 0, 0);

        clearInvocations(mTransaction);
        mTaskFragment.mSurfaceFreezer.unfreeze(mTransaction);

        // Update surface after animation.
        verify(mTransaction).setPosition(mLeash, 500, 500);
        verify(mTransaction).setWindowCrop(mLeash, 500, 500);
    }
}