Loading services/core/java/com/android/server/wm/Task.java +0 −1 Original line number Diff line number Diff line Loading @@ -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; Loading services/core/java/com/android/server/wm/TaskFragment.java +57 −5 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -1654,6 +1656,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } @Override void onChildPositionChanged(WindowContainer child) { super.onChildPositionChanged(child); Loading Loading @@ -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 Loading @@ -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) { Loading services/core/java/com/android/server/wm/WindowContainer.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; } Loading services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java 0 → 100644 +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); } } Loading
services/core/java/com/android/server/wm/Task.java +0 −1 Original line number Diff line number Diff line Loading @@ -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; Loading
services/core/java/com/android/server/wm/TaskFragment.java +57 −5 Original line number Diff line number Diff line Loading @@ -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(); Loading Loading @@ -1654,6 +1656,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } @Override void onChildPositionChanged(WindowContainer child) { super.onChildPositionChanged(child); Loading Loading @@ -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 Loading @@ -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) { Loading
services/core/java/com/android/server/wm/WindowContainer.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; } Loading
services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java 0 → 100644 +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); } }