| /* |
| * Copyright (C) 2016 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 android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; |
| import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; |
| import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; |
| import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; |
| import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; |
| import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_1; |
| import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_2; |
| import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; |
| import static android.view.WindowManager.TRANSIT_CLOSE; |
| import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; |
| import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; |
| import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; |
| import static android.view.WindowManager.TRANSIT_OPEN; |
| import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; |
| |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; |
| import static com.android.server.wm.DisplayArea.Type.ANY; |
| import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; |
| import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; |
| import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION; |
| import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; |
| import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; |
| import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; |
| import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; |
| import static com.android.server.wm.WindowContainer.POSITION_TOP; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Mockito.clearInvocations; |
| |
| import android.content.pm.ActivityInfo; |
| import android.content.res.Configuration; |
| import android.graphics.Rect; |
| import android.os.RemoteException; |
| import android.platform.test.annotations.Presubmit; |
| import android.view.IRemoteAnimationFinishedCallback; |
| import android.view.IRemoteAnimationRunner; |
| import android.view.InsetsSource; |
| import android.view.RemoteAnimationAdapter; |
| import android.view.RemoteAnimationTarget; |
| import android.view.SurfaceControl; |
| import android.view.SurfaceSession; |
| import android.view.WindowManager; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mockito; |
| |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| |
| |
| /** |
| * Test class for {@link WindowContainer}. |
| * |
| * Build/Install/Run: |
| * atest WmTests:WindowContainerTests |
| */ |
| @SmallTest |
| @Presubmit |
| @RunWith(WindowTestRunner.class) |
| public class WindowContainerTests extends WindowTestsBase { |
| |
| @Test |
| public void testCreation() { |
| final TestWindowContainer w = new TestWindowContainerBuilder(mWm).setLayer(0).build(); |
| assertNull("window must have no parent", w.getParentWindow()); |
| assertEquals("window must have no children", 0, w.getChildrenCount()); |
| } |
| |
| @Test |
| public void testAdd() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer layer1 = root.addChildWindow(builder.setLayer(1)); |
| final TestWindowContainer secondLayer1 = root.addChildWindow(builder.setLayer(1)); |
| final TestWindowContainer layer2 = root.addChildWindow(builder.setLayer(2)); |
| final TestWindowContainer layerNeg1 = root.addChildWindow(builder.setLayer(-1)); |
| final TestWindowContainer layerNeg2 = root.addChildWindow(builder.setLayer(-2)); |
| final TestWindowContainer secondLayerNeg1 = root.addChildWindow(builder.setLayer(-1)); |
| final TestWindowContainer layer0 = root.addChildWindow(builder.setLayer(0)); |
| |
| assertEquals(7, root.getChildrenCount()); |
| |
| assertEquals(root, layer1.getParentWindow()); |
| assertEquals(root, secondLayer1.getParentWindow()); |
| assertEquals(root, layer2.getParentWindow()); |
| assertEquals(root, layerNeg1.getParentWindow()); |
| assertEquals(root, layerNeg2.getParentWindow()); |
| assertEquals(root, secondLayerNeg1.getParentWindow()); |
| assertEquals(root, layer0.getParentWindow()); |
| |
| assertEquals(layerNeg2, root.getChildAt(0)); |
| assertEquals(secondLayerNeg1, root.getChildAt(1)); |
| assertEquals(layerNeg1, root.getChildAt(2)); |
| assertEquals(layer0, root.getChildAt(3)); |
| assertEquals(layer1, root.getChildAt(4)); |
| assertEquals(secondLayer1, root.getChildAt(5)); |
| assertEquals(layer2, root.getChildAt(6)); |
| |
| assertTrue(layer1.mOnParentChangedCalled); |
| assertTrue(secondLayer1.mOnParentChangedCalled); |
| assertTrue(layer2.mOnParentChangedCalled); |
| assertTrue(layerNeg1.mOnParentChangedCalled); |
| assertTrue(layerNeg2.mOnParentChangedCalled); |
| assertTrue(secondLayerNeg1.mOnParentChangedCalled); |
| assertTrue(layer0.mOnParentChangedCalled); |
| } |
| |
| @Test |
| public void testAddChildSetsSurfacePosition() { |
| reset(mTransaction); |
| try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) { |
| WindowContainer child = new WindowContainer(mWm); |
| child.setBounds(1, 1, 10, 10); |
| |
| verify(mTransaction, never()).setPosition(any(), anyFloat(), anyFloat()); |
| top.addChild(child, 0); |
| verify(mTransaction, times(1)).setPosition(any(), eq(1.f), eq(1.f)); |
| } |
| } |
| |
| @Test |
| public void testAdd_AlreadyHasParent() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(); |
| final TestWindowContainer child2 = root.addChildWindow(); |
| |
| boolean gotException = false; |
| try { |
| child1.addChildWindow(child2); |
| } catch (IllegalArgumentException e) { |
| gotException = true; |
| } |
| assertTrue(gotException); |
| |
| gotException = false; |
| try { |
| root.addChildWindow(child2); |
| } catch (IllegalArgumentException e) { |
| gotException = true; |
| } |
| assertTrue(gotException); |
| } |
| |
| @Test |
| public void testHasChild() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(); |
| final TestWindowContainer child2 = root.addChildWindow(); |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child12 = child1.addChildWindow(); |
| final TestWindowContainer child21 = child2.addChildWindow(); |
| |
| assertEquals(2, root.getChildrenCount()); |
| assertEquals(2, child1.getChildrenCount()); |
| assertEquals(1, child2.getChildrenCount()); |
| |
| assertTrue(root.hasChild(child1)); |
| assertTrue(root.hasChild(child2)); |
| assertTrue(root.hasChild(child11)); |
| assertTrue(root.hasChild(child12)); |
| assertTrue(root.hasChild(child21)); |
| |
| assertTrue(child1.hasChild(child11)); |
| assertTrue(child1.hasChild(child12)); |
| assertFalse(child1.hasChild(child21)); |
| |
| assertTrue(child2.hasChild(child21)); |
| assertFalse(child2.hasChild(child11)); |
| assertFalse(child2.hasChild(child12)); |
| } |
| |
| @Test |
| public void testRemoveImmediately() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(); |
| final TestWindowContainer child2 = root.addChildWindow(); |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child12 = child1.addChildWindow(); |
| final TestWindowContainer child21 = child2.addChildWindow(); |
| |
| assertNotNull(child12.getParentWindow()); |
| child12.removeImmediately(); |
| assertNull(child12.getParentWindow()); |
| assertEquals(1, child1.getChildrenCount()); |
| assertFalse(child1.hasChild(child12)); |
| assertFalse(root.hasChild(child12)); |
| |
| assertTrue(root.hasChild(child2)); |
| assertNotNull(child2.getParentWindow()); |
| child2.removeImmediately(); |
| assertNull(child2.getParentWindow()); |
| assertNull(child21.getParentWindow()); |
| assertEquals(0, child2.getChildrenCount()); |
| assertEquals(1, root.getChildrenCount()); |
| assertFalse(root.hasChild(child2)); |
| assertFalse(root.hasChild(child21)); |
| |
| assertTrue(root.hasChild(child1)); |
| assertTrue(root.hasChild(child11)); |
| |
| root.removeImmediately(); |
| assertEquals(0, root.getChildrenCount()); |
| } |
| |
| @Test |
| public void testRemoveImmediatelyClearsLastSurfacePosition() { |
| reset(mTransaction); |
| try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) { |
| final WindowContainer<WindowContainer> child1 = new WindowContainer(mWm); |
| child1.setBounds(1, 1, 10, 10); |
| |
| top.addChild(child1, 0); |
| assertEquals(1, child1.getLastSurfacePosition().x); |
| assertEquals(1, child1.getLastSurfacePosition().y); |
| |
| WindowContainer child11 = new WindowContainer(mWm); |
| child1.addChild(child11, 0); |
| |
| child1.setBounds(2, 2, 20, 20); |
| assertEquals(2, child1.getLastSurfacePosition().x); |
| assertEquals(2, child1.getLastSurfacePosition().y); |
| |
| child1.removeImmediately(); |
| assertEquals(0, child1.getLastSurfacePosition().x); |
| assertEquals(0, child1.getLastSurfacePosition().y); |
| assertEquals(0, child11.getLastSurfacePosition().x); |
| assertEquals(0, child11.getLastSurfacePosition().y); |
| } |
| } |
| |
| @Test |
| public void testRemoveImmediatelyClearsLeash() { |
| final AnimationAdapter animAdapter = mock(AnimationAdapter.class); |
| final WindowToken token = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); |
| final SurfaceControl.Transaction t = token.getPendingTransaction(); |
| token.startAnimation(t, animAdapter, false /* hidden */, |
| SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION); |
| final ArgumentCaptor<SurfaceControl> leashCaptor = |
| ArgumentCaptor.forClass(SurfaceControl.class); |
| verify(animAdapter).startAnimation(leashCaptor.capture(), eq(t), anyInt(), any()); |
| assertTrue(token.mSurfaceAnimator.hasLeash()); |
| token.removeImmediately(); |
| assertFalse(token.mSurfaceAnimator.hasLeash()); |
| verify(t).remove(eq(leashCaptor.getValue())); |
| } |
| |
| @Test |
| public void testAddChildByIndex() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child = root.addChildWindow(); |
| |
| final TestWindowContainer child2 = builder.setLayer(1).build(); |
| final TestWindowContainer child3 = builder.setLayer(2).build(); |
| final TestWindowContainer child4 = builder.setLayer(3).build(); |
| |
| // Test adding at top. |
| root.addChild(child2, POSITION_TOP); |
| assertEquals(child2, root.getChildAt(root.getChildrenCount() - 1)); |
| |
| // Test adding at bottom. |
| root.addChild(child3, POSITION_BOTTOM); |
| assertEquals(child3, root.getChildAt(0)); |
| |
| // Test adding in the middle. |
| root.addChild(child4, 1); |
| assertEquals(child3, root.getChildAt(0)); |
| assertEquals(child4, root.getChildAt(1)); |
| assertEquals(child, root.getChildAt(2)); |
| assertEquals(child2, root.getChildAt(3)); |
| } |
| |
| @Test |
| public void testPositionChildAt() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(); |
| final TestWindowContainer child2 = root.addChildWindow(); |
| final TestWindowContainer child3 = root.addChildWindow(); |
| |
| // Test position at top. |
| root.positionChildAt(POSITION_TOP, child1, false /* includingParents */); |
| assertEquals(child1, root.getChildAt(root.getChildrenCount() - 1)); |
| |
| // Test position at bottom. |
| root.positionChildAt(POSITION_BOTTOM, child1, false /* includingParents */); |
| assertEquals(child1, root.getChildAt(0)); |
| |
| // Test position in the middle. |
| root.positionChildAt(1, child3, false /* includingParents */); |
| assertEquals(child1, root.getChildAt(0)); |
| assertEquals(child3, root.getChildAt(1)); |
| assertEquals(child2, root.getChildAt(2)); |
| } |
| |
| @Test |
| public void testPositionChildAtIncludeParents() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(); |
| final TestWindowContainer child2 = root.addChildWindow(); |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child12 = child1.addChildWindow(); |
| final TestWindowContainer child13 = child1.addChildWindow(); |
| final TestWindowContainer child21 = child2.addChildWindow(); |
| final TestWindowContainer child22 = child2.addChildWindow(); |
| final TestWindowContainer child23 = child2.addChildWindow(); |
| |
| // Test moving to top. |
| child1.positionChildAt(POSITION_TOP, child11, true /* includingParents */); |
| assertEquals(child12, child1.getChildAt(0)); |
| assertEquals(child13, child1.getChildAt(1)); |
| assertEquals(child11, child1.getChildAt(2)); |
| assertEquals(child2, root.getChildAt(0)); |
| assertEquals(child1, root.getChildAt(1)); |
| |
| // Test moving to bottom. |
| child1.positionChildAt(POSITION_BOTTOM, child11, true /* includingParents */); |
| assertEquals(child11, child1.getChildAt(0)); |
| assertEquals(child12, child1.getChildAt(1)); |
| assertEquals(child13, child1.getChildAt(2)); |
| assertEquals(child1, root.getChildAt(0)); |
| assertEquals(child2, root.getChildAt(1)); |
| |
| // Test moving to middle, includeParents shouldn't do anything. |
| child2.positionChildAt(1, child21, true /* includingParents */); |
| assertEquals(child11, child1.getChildAt(0)); |
| assertEquals(child12, child1.getChildAt(1)); |
| assertEquals(child13, child1.getChildAt(2)); |
| assertEquals(child22, child2.getChildAt(0)); |
| assertEquals(child21, child2.getChildAt(1)); |
| assertEquals(child23, child2.getChildAt(2)); |
| assertEquals(child1, root.getChildAt(0)); |
| assertEquals(child2, root.getChildAt(1)); |
| } |
| |
| @Test |
| public void testIsAnimating_TransitionFlag() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| final TestWindowContainer child1 = root.addChildWindow( |
| builder.setWaitForTransitionStart(true)); |
| |
| assertFalse(root.isAnimating(TRANSITION)); |
| assertTrue(child1.isAnimating(TRANSITION)); |
| } |
| |
| @Test |
| public void testIsAnimating_ParentsFlag() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| final TestWindowContainer child1 = root.addChildWindow(builder); |
| final TestWindowContainer child2 = root.addChildWindow(builder.setIsAnimating(true)); |
| final TestWindowContainer child21 = child2.addChildWindow(builder.setIsAnimating(false)); |
| |
| assertFalse(root.isAnimating()); |
| assertFalse(child1.isAnimating()); |
| assertFalse(child1.isAnimating(PARENTS)); |
| assertTrue(child2.isAnimating()); |
| assertTrue(child2.isAnimating(PARENTS)); |
| assertFalse(child21.isAnimating()); |
| assertTrue(child21.isAnimating(PARENTS)); |
| } |
| |
| @Test |
| public void testIsAnimating_ChildrenFlag() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| final TestWindowContainer child1 = root.addChildWindow(builder); |
| final TestWindowContainer child2 = root.addChildWindow(builder.setIsAnimating(true)); |
| final TestWindowContainer child11 = child1.addChildWindow(builder.setIsAnimating(true)); |
| |
| assertFalse(root.isAnimating()); |
| assertTrue(root.isAnimating(CHILDREN)); |
| assertFalse(child1.isAnimating()); |
| assertTrue(child1.isAnimating(CHILDREN)); |
| assertTrue(child2.isAnimating()); |
| assertTrue(child2.isAnimating(CHILDREN)); |
| assertTrue(child11.isAnimating()); |
| assertTrue(child11.isAnimating(CHILDREN)); |
| } |
| |
| @Test |
| public void testIsAnimating_combineFlags() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(builder.setIsAnimating(true)); |
| final TestWindowContainer child2 = root.addChildWindow(); |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true)); |
| final TestWindowContainer child21 = child2.addChildWindow(); |
| |
| assertFalse(root.isAnimating(TRANSITION | PARENTS)); |
| assertTrue(child1.isAnimating(TRANSITION | PARENTS)); |
| assertTrue(child11.isAnimating(TRANSITION | PARENTS)); |
| assertTrue(child12.isAnimating(TRANSITION | PARENTS)); |
| assertFalse(child2.isAnimating(TRANSITION | PARENTS)); |
| assertFalse(child21.isAnimating(TRANSITION | PARENTS)); |
| |
| assertTrue(root.isAnimating(TRANSITION | CHILDREN)); |
| assertTrue(child1.isAnimating(TRANSITION | CHILDREN)); |
| assertFalse(child11.isAnimating(TRANSITION | CHILDREN)); |
| assertTrue(child12.isAnimating(TRANSITION | CHILDREN)); |
| assertFalse(child2.isAnimating(TRANSITION | CHILDREN)); |
| assertFalse(child21.isAnimating(TRANSITION | CHILDREN)); |
| } |
| |
| @Test |
| public void testIsAnimating_typesToCheck() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer window = builder.setIsAnimating(true).setLayer(0).build(); |
| |
| assertTrue(window.isAnimating()); |
| assertFalse(window.isAnimating(0, ANIMATION_TYPE_SCREEN_ROTATION)); |
| assertTrue(window.isAnimating(0, ANIMATION_TYPE_APP_TRANSITION)); |
| assertFalse(window.isAnimating(0, ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_APP_TRANSITION)); |
| |
| final TestWindowContainer child = window.addChildWindow(); |
| assertFalse(child.isAnimating()); |
| assertTrue(child.isAnimating(PARENTS)); |
| assertTrue(child.isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION)); |
| assertFalse(child.isAnimating(PARENTS, ANIMATION_TYPE_SCREEN_ROTATION)); |
| |
| final WindowState windowState = createWindow(null /* parent */, TYPE_BASE_APPLICATION, |
| mDisplayContent, "TestWindowState"); |
| WindowContainer parent = windowState.getParent(); |
| spyOn(windowState.mSurfaceAnimator); |
| doReturn(true).when(windowState.mSurfaceAnimator).isAnimating(); |
| doReturn(ANIMATION_TYPE_APP_TRANSITION).when( |
| windowState.mSurfaceAnimator).getAnimationType(); |
| assertTrue(parent.isAnimating(CHILDREN)); |
| |
| windowState.setControllableInsetProvider(mock(WindowContainerInsetsSourceProvider.class)); |
| assertFalse(parent.isAnimating(CHILDREN)); |
| } |
| |
| @Test |
| public void testIsVisible() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(builder.setIsVisible(true)); |
| final TestWindowContainer child2 = root.addChildWindow(); |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child12 = child1.addChildWindow(builder.setIsVisible(true)); |
| final TestWindowContainer child21 = child2.addChildWindow(); |
| |
| assertFalse(root.isVisible()); |
| assertTrue(child1.isVisible()); |
| assertFalse(child11.isVisible()); |
| assertTrue(child12.isVisible()); |
| assertFalse(child2.isVisible()); |
| assertFalse(child21.isVisible()); |
| } |
| |
| @Test |
| public void testOverrideConfigurationAncestorNotification() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer grandparent = builder.setLayer(0).build(); |
| |
| final TestWindowContainer parent = grandparent.addChildWindow(); |
| final TestWindowContainer child = parent.addChildWindow(); |
| child.onRequestedOverrideConfigurationChanged(new Configuration()); |
| |
| assertTrue(grandparent.mOnDescendantOverrideCalled); |
| } |
| |
| @Test |
| public void testRemoveChild() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| final TestWindowContainer child1 = root.addChildWindow(); |
| final TestWindowContainer child2 = root.addChildWindow(); |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child21 = child2.addChildWindow(); |
| |
| assertTrue(root.hasChild(child2)); |
| assertTrue(root.hasChild(child21)); |
| root.removeChild(child2); |
| assertFalse(root.hasChild(child2)); |
| assertFalse(root.hasChild(child21)); |
| assertNull(child2.getParentWindow()); |
| |
| boolean gotException = false; |
| assertTrue(root.hasChild(child11)); |
| try { |
| // Can only detach our direct children. |
| root.removeChild(child11); |
| } catch (IllegalArgumentException e) { |
| gotException = true; |
| } |
| assertTrue(gotException); |
| } |
| |
| @Test |
| public void testGetOrientation_childSpecified() { |
| testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE, |
| SCREEN_ORIENTATION_LANDSCAPE); |
| testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET, |
| SCREEN_ORIENTATION_UNSPECIFIED); |
| } |
| |
| private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation, |
| int expectedOrientation) { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| root.setFillsParent(true); |
| |
| builder.setIsVisible(childVisible); |
| |
| if (childOrientation != SCREEN_ORIENTATION_UNSET) { |
| builder.setOrientation(childOrientation); |
| } |
| |
| final TestWindowContainer child1 = root.addChildWindow(builder); |
| child1.setFillsParent(true); |
| |
| assertEquals(expectedOrientation, root.getOrientation()); |
| } |
| |
| @Test |
| public void testGetOrientation_Unset() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); |
| // Unspecified well because we didn't specify anything... |
| assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation()); |
| } |
| |
| @Test |
| public void testGetOrientation_InvisibleParentUnsetVisibleChildren() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); |
| |
| builder.setIsVisible(false).setLayer(-1); |
| final TestWindowContainer invisible = root.addChildWindow(builder); |
| builder.setIsVisible(true).setLayer(-2); |
| final TestWindowContainer invisibleChild1VisibleAndSet = invisible.addChildWindow(builder); |
| invisibleChild1VisibleAndSet.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); |
| // Landscape well because the container is visible and that is what we set on it above. |
| assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisibleChild1VisibleAndSet.getOrientation()); |
| // Landscape because even though the container isn't visible it has a child that is |
| // specifying it can influence the orientation by being visible. |
| assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisible.getOrientation()); |
| // Landscape because the grandchild is visible and therefore can participate. |
| assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation()); |
| |
| builder.setIsVisible(true).setLayer(-3); |
| final TestWindowContainer visibleUnset = root.addChildWindow(builder); |
| visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET); |
| assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnset.getOrientation()); |
| assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation()); |
| } |
| |
| @Test |
| public void testGetOrientation_setBehind() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); |
| |
| builder.setIsVisible(true).setLayer(-1); |
| final TestWindowContainer visibleUnset = root.addChildWindow(builder); |
| visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET); |
| |
| builder.setIsVisible(true).setLayer(-2); |
| final TestWindowContainer visibleUnsetChild1VisibleSetBehind = |
| visibleUnset.addChildWindow(builder); |
| visibleUnsetChild1VisibleSetBehind.setOrientation(SCREEN_ORIENTATION_BEHIND); |
| // Setting to visible behind will be used by the parents if there isn't another other |
| // container behind this one that has an orientation set. |
| assertEquals(SCREEN_ORIENTATION_BEHIND, |
| visibleUnsetChild1VisibleSetBehind.getOrientation()); |
| assertEquals(SCREEN_ORIENTATION_BEHIND, visibleUnset.getOrientation()); |
| assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); |
| } |
| |
| @Test |
| public void testGetOrientation_fillsParent() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); |
| |
| builder.setIsVisible(true).setLayer(-1); |
| final TestWindowContainer visibleUnset = root.addChildWindow(builder); |
| visibleUnset.setOrientation(SCREEN_ORIENTATION_BEHIND); |
| |
| builder.setLayer(1).setIsVisible(true); |
| final TestWindowContainer visibleUnspecifiedRootChild = root.addChildWindow(builder); |
| visibleUnspecifiedRootChild.setFillsParent(false); |
| visibleUnspecifiedRootChild.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); |
| // Unset because the child doesn't fill the parent. May as well be invisible... |
| assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation()); |
| // The parent uses whatever orientation is set behind this container since it doesn't fill |
| // the parent. |
| assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); |
| |
| // Test case of child filling its parent, but its parent isn't filling its own parent. |
| builder.setLayer(2).setIsVisible(true); |
| final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent = |
| visibleUnspecifiedRootChild.addChildWindow(builder); |
| visibleUnspecifiedRootChildChildFillsParent.setOrientation(SCREEN_ORIENTATION_PORTRAIT); |
| assertEquals(SCREEN_ORIENTATION_PORTRAIT, |
| visibleUnspecifiedRootChildChildFillsParent.getOrientation()); |
| assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation()); |
| assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); |
| |
| |
| visibleUnspecifiedRootChild.setFillsParent(true); |
| assertEquals(SCREEN_ORIENTATION_PORTRAIT, visibleUnspecifiedRootChild.getOrientation()); |
| assertEquals(SCREEN_ORIENTATION_PORTRAIT, root.getOrientation()); |
| } |
| |
| @Test |
| public void testSetOrientation() { |
| final TestWindowContainer root = spy(new TestWindowContainerBuilder(mWm).build()); |
| final TestWindowContainer child = spy(root.addChildWindow()); |
| doReturn(true).when(root).handlesOrientationChangeFromDescendant(); |
| child.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN); |
| child.setOrientation(SCREEN_ORIENTATION_PORTRAIT); |
| // The ancestor should decide whether to dispatch the configuration change. |
| verify(child, never()).onConfigurationChanged(any()); |
| |
| doReturn(false).when(root).handlesOrientationChangeFromDescendant(); |
| child.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); |
| // The ancestor doesn't handle the request so the descendant applies the change directly. |
| verify(child).onConfigurationChanged(any()); |
| } |
| |
| @Test |
| public void testCompareTo() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.setLayer(0).build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(); |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child12 = child1.addChildWindow(); |
| |
| final TestWindowContainer child2 = root.addChildWindow(); |
| final TestWindowContainer child21 = child2.addChildWindow(); |
| final TestWindowContainer child22 = child2.addChildWindow(); |
| final TestWindowContainer child222 = child22.addChildWindow(); |
| final TestWindowContainer child223 = child22.addChildWindow(); |
| final TestWindowContainer child2221 = child222.addChildWindow(); |
| final TestWindowContainer child2222 = child222.addChildWindow(); |
| final TestWindowContainer child2223 = child222.addChildWindow(); |
| |
| final TestWindowContainer root2 = builder.setLayer(0).build(); |
| |
| assertEquals(0, root.compareTo(root)); |
| assertEquals(-1, child1.compareTo(child2)); |
| assertEquals(1, child2.compareTo(child1)); |
| |
| boolean inTheSameTree = true; |
| try { |
| root.compareTo(root2); |
| } catch (IllegalArgumentException e) { |
| inTheSameTree = false; |
| } |
| assertFalse(inTheSameTree); |
| |
| assertEquals(-1, child1.compareTo(child11)); |
| assertEquals(1, child21.compareTo(root)); |
| assertEquals(1, child21.compareTo(child12)); |
| assertEquals(-1, child11.compareTo(child2)); |
| assertEquals(1, child2221.compareTo(child11)); |
| assertEquals(-1, child2222.compareTo(child223)); |
| assertEquals(1, child2223.compareTo(child21)); |
| } |
| |
| @Test |
| public void testPrefixOrderIndex() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(); |
| |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child12 = child1.addChildWindow(); |
| |
| final TestWindowContainer child2 = root.addChildWindow(); |
| |
| final TestWindowContainer child21 = child2.addChildWindow(); |
| final TestWindowContainer child22 = child2.addChildWindow(); |
| |
| final TestWindowContainer child221 = child22.addChildWindow(); |
| final TestWindowContainer child222 = child22.addChildWindow(); |
| final TestWindowContainer child223 = child22.addChildWindow(); |
| |
| final TestWindowContainer child23 = child2.addChildWindow(); |
| |
| assertEquals(0, root.getPrefixOrderIndex()); |
| assertEquals(1, child1.getPrefixOrderIndex()); |
| assertEquals(2, child11.getPrefixOrderIndex()); |
| assertEquals(3, child12.getPrefixOrderIndex()); |
| assertEquals(4, child2.getPrefixOrderIndex()); |
| assertEquals(5, child21.getPrefixOrderIndex()); |
| assertEquals(6, child22.getPrefixOrderIndex()); |
| assertEquals(7, child221.getPrefixOrderIndex()); |
| assertEquals(8, child222.getPrefixOrderIndex()); |
| assertEquals(9, child223.getPrefixOrderIndex()); |
| assertEquals(10, child23.getPrefixOrderIndex()); |
| } |
| |
| @Test |
| public void testPrefixOrder_addEntireSubtree() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.build(); |
| final TestWindowContainer subtree = builder.build(); |
| final TestWindowContainer subtree2 = builder.build(); |
| |
| final TestWindowContainer child1 = subtree.addChildWindow(); |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child2 = subtree2.addChildWindow(); |
| final TestWindowContainer child3 = subtree2.addChildWindow(); |
| subtree.addChild(subtree2, 1); |
| root.addChild(subtree, 0); |
| |
| assertEquals(0, root.getPrefixOrderIndex()); |
| assertEquals(1, subtree.getPrefixOrderIndex()); |
| assertEquals(2, child1.getPrefixOrderIndex()); |
| assertEquals(3, child11.getPrefixOrderIndex()); |
| assertEquals(4, subtree2.getPrefixOrderIndex()); |
| assertEquals(5, child2.getPrefixOrderIndex()); |
| assertEquals(6, child3.getPrefixOrderIndex()); |
| } |
| |
| @Test |
| public void testPrefixOrder_remove() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.build(); |
| |
| final TestWindowContainer child1 = root.addChildWindow(); |
| |
| final TestWindowContainer child11 = child1.addChildWindow(); |
| final TestWindowContainer child12 = child1.addChildWindow(); |
| |
| final TestWindowContainer child2 = root.addChildWindow(); |
| |
| assertEquals(0, root.getPrefixOrderIndex()); |
| assertEquals(1, child1.getPrefixOrderIndex()); |
| assertEquals(2, child11.getPrefixOrderIndex()); |
| assertEquals(3, child12.getPrefixOrderIndex()); |
| assertEquals(4, child2.getPrefixOrderIndex()); |
| |
| root.removeChild(child1); |
| |
| assertEquals(1, child2.getPrefixOrderIndex()); |
| } |
| |
| /** |
| * Ensure children of a {@link WindowContainer} do not have |
| * {@link WindowContainer#onParentResize()} called when {@link WindowContainer#onParentResize()} |
| * is invoked with overridden bounds. |
| */ |
| @Test |
| public void testOnParentResizePropagation() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = builder.build(); |
| |
| final TestWindowContainer child = root.addChildWindow(); |
| child.setBounds(new Rect(1, 1, 2, 2)); |
| |
| final TestWindowContainer grandChild = mock(TestWindowContainer.class); |
| |
| child.addChildWindow(grandChild); |
| root.onResize(); |
| |
| // Make sure the child does not propagate resize through onParentResize when bounds are set. |
| verify(grandChild, never()).onParentResize(); |
| |
| child.removeChild(grandChild); |
| |
| child.setBounds(null); |
| child.addChildWindow(grandChild); |
| root.onResize(); |
| |
| // Make sure the child propagates resize through onParentResize when no bounds set. |
| verify(grandChild, times(1)).onParentResize(); |
| } |
| |
| @Test |
| public void testOnDescendantOrientationRequestChangedPropagation() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = spy(builder.build()); |
| |
| final ActivityRecord activityRecord = mock(ActivityRecord.class); |
| final TestWindowContainer child = root.addChildWindow(); |
| |
| child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, activityRecord); |
| verify(root).onDescendantOrientationChanged(activityRecord); |
| } |
| |
| @Test |
| public void testHandlesOrientationChangeFromDescendantProgation() { |
| final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); |
| final TestWindowContainer root = spy(builder.build()); |
| |
| final TestWindowContainer child = root.addChildWindow(); |
| assertFalse(child.handlesOrientationChangeFromDescendant()); |
| |
| Mockito.doReturn(true).when(root).handlesOrientationChangeFromDescendant(); |
| assertTrue(child.handlesOrientationChangeFromDescendant()); |
| } |
| |
| @Test |
| public void testOnDisplayChanged() { |
| final Task rootTask = createTask(mDisplayContent); |
| final Task task = createTaskInRootTask(rootTask, 0 /* userId */); |
| final ActivityRecord activity = createActivityRecord(mDisplayContent, task); |
| |
| final DisplayContent newDc = createNewDisplay(); |
| rootTask.getDisplayArea().removeRootTask(rootTask); |
| newDc.getDefaultTaskDisplayArea().addChild(rootTask, POSITION_TOP); |
| |
| verify(rootTask).onDisplayChanged(newDc); |
| verify(task).onDisplayChanged(newDc); |
| verify(activity).onDisplayChanged(newDc); |
| assertEquals(newDc, rootTask.mDisplayContent); |
| assertEquals(newDc, task.mDisplayContent); |
| assertEquals(newDc, activity.mDisplayContent); |
| } |
| |
| @Test |
| public void testHandleCompleteDeferredRemoval() { |
| final DisplayContent displayContent = createNewDisplay(); |
| // Do not reparent activity to default display when removing the display. |
| doReturn(true).when(displayContent).shouldDestroyContentOnRemove(); |
| |
| // An animating window with mRemoveOnExit can be removed by handleCompleteDeferredRemoval |
| // once it no longer animates. |
| final WindowState exitingWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, |
| displayContent, "exiting window"); |
| exitingWindow.startAnimation(exitingWindow.getPendingTransaction(), |
| mock(AnimationAdapter.class), false /* hidden */, |
| SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION); |
| exitingWindow.mRemoveOnExit = true; |
| exitingWindow.handleCompleteDeferredRemoval(); |
| // The animation has not finished so the window is not removed. |
| assertTrue(exitingWindow.isAnimating()); |
| assertTrue(exitingWindow.isAttached()); |
| exitingWindow.cancelAnimation(); |
| // The window is removed because the animation is gone. |
| exitingWindow.handleCompleteDeferredRemoval(); |
| assertFalse(exitingWindow.isAttached()); |
| |
| final ActivityRecord r = new TaskBuilder(mSupervisor).setCreateActivity(true) |
| .setDisplay(displayContent).build().getTopMostActivity(); |
| // Add a window and make the activity animating so the removal of activity is deferred. |
| createWindow(null, TYPE_BASE_APPLICATION, r, "win"); |
| doReturn(true).when(r).isAnimating(anyInt(), anyInt()); |
| |
| displayContent.remove(); |
| // Ensure that ActivityRecord#onRemovedFromDisplay is called. |
| r.destroyed("test"); |
| // The removal is deferred, so the activity is still in the display. |
| assertEquals(r, displayContent.getTopMostActivity()); |
| |
| // Assume the animation is done so the deferred removal can continue. |
| doReturn(false).when(r).isAnimating(anyInt(), anyInt()); |
| |
| assertFalse(displayContent.handleCompleteDeferredRemoval()); |
| assertFalse(displayContent.hasChild()); |
| assertFalse(r.hasChild()); |
| } |
| |
| @Test |
| public void testTaskCanApplyAnimation() { |
| final Task rootTask = createTask(mDisplayContent); |
| final Task task = createTaskInRootTask(rootTask, 0 /* userId */); |
| final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task); |
| final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task); |
| verifyWindowContainerApplyAnimation(task, activity1, activity2); |
| } |
| |
| @Test |
| public void testRootTaskCanApplyAnimation() { |
| final Task rootTask = createTask(mDisplayContent); |
| final ActivityRecord activity2 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(rootTask, 0 /* userId */)); |
| final ActivityRecord activity1 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(rootTask, 0 /* userId */)); |
| verifyWindowContainerApplyAnimation(rootTask, activity1, activity2); |
| } |
| |
| @Test |
| public void testGetDisplayArea() { |
| // WindowContainer |
| final WindowContainer windowContainer = new WindowContainer(mWm); |
| |
| assertNull(windowContainer.getDisplayArea()); |
| |
| // Task > WindowContainer |
| final Task task = createTask(mDisplayContent); |
| task.addChild(windowContainer, 0); |
| task.setParent(null); |
| |
| assertNull(windowContainer.getDisplayArea()); |
| assertNull(task.getDisplayArea()); |
| |
| // TaskDisplayArea > Task > WindowContainer |
| final TaskDisplayArea taskDisplayArea = new TaskDisplayArea( |
| mDisplayContent, mWm, "TaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER); |
| taskDisplayArea.addChild(task, 0); |
| |
| assertEquals(taskDisplayArea, windowContainer.getDisplayArea()); |
| assertEquals(taskDisplayArea, task.getDisplayArea()); |
| assertEquals(taskDisplayArea, taskDisplayArea.getDisplayArea()); |
| |
| // DisplayArea |
| final DisplayArea displayArea = new DisplayArea(mWm, ANY, "DisplayArea"); |
| |
| assertEquals(displayArea, displayArea.getDisplayArea()); |
| } |
| |
| private void verifyWindowContainerApplyAnimation(WindowContainer wc, ActivityRecord act, |
| ActivityRecord act2) { |
| // Initial remote animation for app transition. |
| final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( |
| new IRemoteAnimationRunner.Stub() { |
| @Override |
| public void onAnimationStart(@WindowManager.TransitionOldType int transit, |
| RemoteAnimationTarget[] apps, |
| RemoteAnimationTarget[] wallpapers, |
| RemoteAnimationTarget[] nonApps, |
| IRemoteAnimationFinishedCallback finishedCallback) { |
| try { |
| finishedCallback.onAnimationFinished(); |
| } catch (RemoteException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| @Override |
| public void onAnimationCancelled(boolean isKeyguardOccluded) { |
| } |
| }, 0, 0, false); |
| adapter.setCallingPidUid(123, 456); |
| wc.getDisplayContent().prepareAppTransition(TRANSIT_OPEN); |
| wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter); |
| spyOn(wc); |
| doReturn(true).when(wc).okToAnimate(); |
| |
| // Make sure animating state is as expected after applied animation. |
| |
| // Animation target is promoted from act to wc. act2 is a descendant of wc, but not a source |
| // of the animation. |
| ArrayList<WindowContainer<WindowState>> sources = new ArrayList<>(); |
| sources.add(act); |
| assertTrue(wc.applyAnimation(null, TRANSIT_OLD_TASK_OPEN, true, false, sources)); |
| |
| assertEquals(act, wc.getTopMostActivity()); |
| assertTrue(wc.isAnimating()); |
| assertTrue(wc.isAnimating(0, ANIMATION_TYPE_APP_TRANSITION)); |
| assertTrue(wc.getAnimationSources().contains(act)); |
| assertFalse(wc.getAnimationSources().contains(act2)); |
| assertTrue(act.isAnimating(PARENTS)); |
| assertTrue(act.isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION)); |
| assertEquals(wc, act.getAnimatingContainer(PARENTS, ANIMATION_TYPE_APP_TRANSITION)); |
| |
| // Make sure animation finish callback will be received and reset animating state after |
| // animation finish. |
| wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_OLD_TASK_OPEN, act); |
| verify(wc).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), any()); |
| assertFalse(wc.isAnimating()); |
| assertFalse(act.isAnimating(PARENTS)); |
| } |
| |
| @Test |
| public void testRegisterWindowContainerListener() { |
| final WindowContainer container = new WindowContainer(mWm); |
| container.mDisplayContent = mDisplayContent; |
| final TestWindowContainerListener listener = new TestWindowContainerListener(); |
| Configuration config = container.getConfiguration(); |
| Rect bounds = new Rect(0, 0, 10, 10); |
| config.windowConfiguration.setBounds(bounds); |
| config.densityDpi = 100; |
| container.onRequestedOverrideConfigurationChanged(config); |
| container.registerWindowContainerListener(listener); |
| |
| assertEquals(mDisplayContent, listener.mDisplayContent); |
| assertEquals(bounds, listener.mConfiguration.windowConfiguration.getBounds()); |
| assertEquals(100, listener.mConfiguration.densityDpi); |
| |
| container.onDisplayChanged(mDefaultDisplay); |
| assertEquals(listener.mDisplayContent, mDefaultDisplay); |
| |
| config = new Configuration(); |
| bounds = new Rect(0, 0, 20, 20); |
| config.windowConfiguration.setBounds(bounds); |
| config.densityDpi = 200; |
| |
| container.onRequestedOverrideConfigurationChanged(config); |
| |
| assertEquals(bounds, listener.mConfiguration.windowConfiguration.getBounds()); |
| assertEquals(200, listener.mConfiguration.densityDpi); |
| } |
| |
| @Test |
| public void testFreezeInsets() { |
| final Task task = createTask(mDisplayContent); |
| final ActivityRecord activity = createActivityRecord(mDisplayContent, task); |
| final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); |
| |
| // Set visibility to false, verify the main window of the task will be set the frozen |
| // insets state immediately. |
| activity.setVisibility(false); |
| assertNotNull(win.getFrozenInsetsState()); |
| |
| // Now make it visible again, verify that the insets are immediately unfrozen. |
| activity.setVisibility(true); |
| assertNull(win.getFrozenInsetsState()); |
| } |
| |
| @Test |
| public void testFreezeInsetsStateWhenAppTransition() { |
| final Task rootTask = createTask(mDisplayContent); |
| final Task task = createTaskInRootTask(rootTask, 0 /* userId */); |
| final ActivityRecord activity = createActivityRecord(mDisplayContent, task); |
| final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); |
| task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); |
| spyOn(win); |
| doReturn(true).when(task).okToAnimate(); |
| ArrayList<WindowContainer> sources = new ArrayList<>(); |
| sources.add(activity); |
| |
| // Simulate the task applying the exit transition, verify the main window of the task |
| // will be set the frozen insets state before the animation starts |
| activity.setVisibility(false); |
| task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */, |
| false /* isVoiceInteraction */, sources); |
| verify(win).freezeInsetsState(); |
| |
| // Simulate the task transition finished. |
| activity.commitVisibility(false, false); |
| task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, |
| task.mSurfaceAnimator.getAnimation()); |
| |
| // Now make it visible again, verify that the insets are immediately unfrozen even before |
| // transition starts. |
| activity.setVisibility(true); |
| verify(win).clearFrozenInsetsState(); |
| } |
| |
| @Test |
| public void testAssignRelativeLayer() { |
| final WindowContainer container = new WindowContainer(mWm); |
| container.mSurfaceControl = mock(SurfaceControl.class); |
| final SurfaceAnimator surfaceAnimator = container.mSurfaceAnimator; |
| final SurfaceControl relativeParent = mock(SurfaceControl.class); |
| final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); |
| spyOn(container); |
| spyOn(surfaceAnimator); |
| |
| // Trigger for first relative layer call. |
| container.assignRelativeLayer(t, relativeParent, 1 /* layer */); |
| verify(surfaceAnimator).setRelativeLayer(t, relativeParent, 1 /* layer */); |
| |
| // Not trigger for the same relative layer call. |
| clearInvocations(surfaceAnimator); |
| container.assignRelativeLayer(t, relativeParent, 1 /* layer */); |
| verify(surfaceAnimator, never()).setRelativeLayer(t, relativeParent, 1 /* layer */); |
| |
| // Trigger for the same relative layer call if forceUpdate=true |
| container.assignRelativeLayer(t, relativeParent, 1 /* layer */, true /* forceUpdate */); |
| verify(surfaceAnimator).setRelativeLayer(t, relativeParent, 1 /* layer */); |
| } |
| |
| @Test |
| public void testAssignAnimationLayer() { |
| final WindowContainer container = new WindowContainer(mWm); |
| container.mSurfaceControl = mock(SurfaceControl.class); |
| final SurfaceAnimator surfaceAnimator = container.mSurfaceAnimator; |
| final SurfaceFreezer surfaceFreezer = container.mSurfaceFreezer; |
| final SurfaceControl relativeParent = mock(SurfaceControl.class); |
| final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); |
| spyOn(container); |
| spyOn(surfaceAnimator); |
| spyOn(surfaceFreezer); |
| |
| container.setLayer(t, 1); |
| container.setRelativeLayer(t, relativeParent, 2); |
| |
| // Set through surfaceAnimator if surfaceFreezer doesn't have leash. |
| verify(surfaceAnimator).setLayer(t, 1); |
| verify(surfaceAnimator).setRelativeLayer(t, relativeParent, 2); |
| verify(surfaceFreezer, never()).setLayer(any(), anyInt()); |
| verify(surfaceFreezer, never()).setRelativeLayer(any(), any(), anyInt()); |
| |
| clearInvocations(surfaceAnimator); |
| clearInvocations(surfaceFreezer); |
| doReturn(true).when(surfaceFreezer).hasLeash(); |
| |
| container.setLayer(t, 1); |
| container.setRelativeLayer(t, relativeParent, 2); |
| |
| // Set through surfaceFreezer if surfaceFreezer has leash. |
| verify(surfaceFreezer).setLayer(t, 1); |
| verify(surfaceFreezer).setRelativeLayer(t, relativeParent, 2); |
| verify(surfaceAnimator, never()).setLayer(any(), anyInt()); |
| verify(surfaceAnimator, never()).setRelativeLayer(any(), any(), anyInt()); |
| } |
| |
| @Test |
| public void testStartChangeTransitionWhenPreviousIsNotFinished() { |
| final WindowContainer container = createTaskFragmentWithParentTask( |
| createTask(mDisplayContent), false); |
| container.mSurfaceControl = mock(SurfaceControl.class); |
| final SurfaceAnimator surfaceAnimator = container.mSurfaceAnimator; |
| final SurfaceFreezer surfaceFreezer = container.mSurfaceFreezer; |
| final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); |
| spyOn(container); |
| spyOn(surfaceAnimator); |
| mockSurfaceFreezerSnapshot(surfaceFreezer); |
| doReturn(t).when(container).getPendingTransaction(); |
| doReturn(t).when(container).getSyncTransaction(); |
| |
| // Leash and snapshot created for change transition. |
| container.initializeChangeTransition(new Rect(0, 0, 1000, 2000)); |
| |
| assertNotNull(surfaceFreezer.mLeash); |
| assertNotNull(surfaceFreezer.mSnapshot); |
| assertEquals(surfaceFreezer.mLeash, container.getAnimationLeash()); |
| |
| // Start animation: surfaceAnimator take over the leash and snapshot from surfaceFreezer. |
| container.applyAnimationUnchecked(null /* lp */, true /* enter */, |
| TRANSIT_OLD_TASK_FRAGMENT_CHANGE, false /* isVoiceInteraction */, |
| null /* sources */); |
| |
| assertNull(surfaceFreezer.mLeash); |
| assertNull(surfaceFreezer.mSnapshot); |
| assertNotNull(surfaceAnimator.mLeash); |
| assertNotNull(surfaceAnimator.mSnapshot); |
| final SurfaceControl prevLeash = surfaceAnimator.mLeash; |
| final SurfaceFreezer.Snapshot prevSnapshot = surfaceAnimator.mSnapshot; |
| |
| // Prepare another change transition. |
| container.initializeChangeTransition(new Rect(0, 0, 1000, 2000)); |
| |
| assertNotNull(surfaceFreezer.mLeash); |
| assertNotNull(surfaceFreezer.mSnapshot); |
| assertEquals(surfaceFreezer.mLeash, container.getAnimationLeash()); |
| assertNotEquals(prevLeash, container.getAnimationLeash()); |
| |
| // Start another animation before the previous one is finished, it should reset the previous |
| // one, but not change the current one. |
| container.applyAnimationUnchecked(null /* lp */, true /* enter */, |
| TRANSIT_OLD_TASK_FRAGMENT_CHANGE, false /* isVoiceInteraction */, |
| null /* sources */); |
| |
| verify(container, never()).onAnimationLeashLost(any()); |
| verify(surfaceFreezer, never()).unfreeze(any()); |
| assertNotNull(surfaceAnimator.mLeash); |
| assertNotNull(surfaceAnimator.mSnapshot); |
| assertEquals(surfaceAnimator.mLeash, container.getAnimationLeash()); |
| assertNotEquals(prevLeash, surfaceAnimator.mLeash); |
| assertNotEquals(prevSnapshot, surfaceAnimator.mSnapshot); |
| |
| // Clean up after animation finished. |
| surfaceAnimator.mInnerAnimationFinishedCallback.onAnimationFinished( |
| ANIMATION_TYPE_APP_TRANSITION, surfaceAnimator.getAnimation()); |
| |
| verify(container).onAnimationLeashLost(any()); |
| assertNull(surfaceAnimator.mLeash); |
| assertNull(surfaceAnimator.mSnapshot); |
| } |
| |
| @Test |
| public void testUnfreezeWindow_removeWindowFromChanging() { |
| final WindowContainer container = createTaskFragmentWithParentTask( |
| createTask(mDisplayContent), false); |
| mockSurfaceFreezerSnapshot(container.mSurfaceFreezer); |
| final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); |
| |
| container.initializeChangeTransition(new Rect(0, 0, 1000, 2000)); |
| |
| assertTrue(mDisplayContent.mChangingContainers.contains(container)); |
| |
| container.mSurfaceFreezer.unfreeze(t); |
| |
| assertFalse(mDisplayContent.mChangingContainers.contains(container)); |
| } |
| |
| @Test |
| public void testFailToTaskSnapshot_unfreezeWindow() { |
| final WindowContainer container = createTaskFragmentWithParentTask( |
| createTask(mDisplayContent), false); |
| final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); |
| spyOn(container.mSurfaceFreezer); |
| |
| container.initializeChangeTransition(new Rect(0, 0, 1000, 2000)); |
| |
| verify(container.mSurfaceFreezer).freeze(any(), any(), any(), any()); |
| verify(container.mSurfaceFreezer).unfreeze(any()); |
| assertTrue(mDisplayContent.mChangingContainers.isEmpty()); |
| } |
| |
| @Test |
| public void testRemoveUnstartedFreezeSurfaceWhenFreezeAgain() { |
| final WindowContainer container = createTaskFragmentWithParentTask( |
| createTask(mDisplayContent), false); |
| container.mSurfaceControl = mock(SurfaceControl.class); |
| final SurfaceFreezer surfaceFreezer = container.mSurfaceFreezer; |
| mockSurfaceFreezerSnapshot(surfaceFreezer); |
| final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); |
| spyOn(container); |
| doReturn(t).when(container).getPendingTransaction(); |
| doReturn(t).when(container).getSyncTransaction(); |
| |
| // Leash and snapshot created for change transition. |
| container.initializeChangeTransition(new Rect(0, 0, 1000, 2000)); |
| |
| assertNotNull(surfaceFreezer.mLeash); |
| assertNotNull(surfaceFreezer.mSnapshot); |
| |
| final SurfaceControl prevLeash = surfaceFreezer.mLeash; |
| final SurfaceFreezer.Snapshot prevSnapshot = surfaceFreezer.mSnapshot; |
| spyOn(prevSnapshot); |
| |
| container.initializeChangeTransition(new Rect(0, 0, 1500, 2500)); |
| |
| verify(t).remove(prevLeash); |
| verify(prevSnapshot).destroy(t); |
| } |
| |
| @Test |
| public void testAddLocalInsetsSourceProvider() { |
| /* |
| ___ rootTask _______________________________________________ |
| | | | | |
| activity0 container navigationBarInsetsProvider1 navigationBarInsetsProvider2 |
| / \ |
| activity1 activity2 |
| */ |
| final Task rootTask = createTask(mDisplayContent); |
| |
| final ActivityRecord activity0 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(rootTask, 0 /* userId */)); |
| final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( |
| TYPE_BASE_APPLICATION); |
| attrs.setTitle("AppWindow0"); |
| activity0.addWindow(createWindowState(attrs, activity0)); |
| |
| final Task container = createTaskInRootTask(rootTask, 0); |
| final ActivityRecord activity1 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(container, 0 /* userId */)); |
| final WindowManager.LayoutParams attrs1 = new WindowManager.LayoutParams( |
| TYPE_BASE_APPLICATION); |
| attrs1.setTitle("AppWindow1"); |
| activity1.addWindow(createWindowState(attrs1, activity1)); |
| |
| final ActivityRecord activity2 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(container, 0 /* userId */)); |
| final WindowManager.LayoutParams attrs2 = new WindowManager.LayoutParams( |
| TYPE_BASE_APPLICATION); |
| attrs2.setTitle("AppWindow2"); |
| activity2.addWindow(createWindowState(attrs2, activity2)); |
| Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700); |
| Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200); |
| |
| rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1, |
| new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1}); |
| container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2, |
| new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2}); |
| |
| InsetsSource navigationBarInsetsProvider1Source = new InsetsSource( |
| ITYPE_LOCAL_NAVIGATION_BAR_1); |
| navigationBarInsetsProvider1Source.setFrame(navigationBarInsetsRect1); |
| navigationBarInsetsProvider1Source.setVisible(true); |
| InsetsSource navigationBarInsetsProvider2Source = new InsetsSource( |
| ITYPE_LOCAL_NAVIGATION_BAR_2); |
| navigationBarInsetsProvider2Source.setFrame(navigationBarInsetsRect2); |
| navigationBarInsetsProvider2Source.setVisible(true); |
| |
| activity0.forAllWindows(window -> { |
| assertEquals(navigationBarInsetsRect1, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame()); |
| assertEquals(null, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2)); |
| }, true); |
| activity1.forAllWindows(window -> { |
| assertEquals(navigationBarInsetsRect1, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame()); |
| assertEquals(navigationBarInsetsRect2, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame()); |
| }, true); |
| activity2.forAllWindows(window -> { |
| assertEquals(navigationBarInsetsRect1, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame()); |
| assertEquals(navigationBarInsetsRect2, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame()); |
| }, true); |
| } |
| |
| @Test |
| public void testAddLocalInsetsSourceProvider_sameType_replacesInsets() { |
| /* |
| ___ rootTask ________________________________________ |
| | | | |
| activity0 navigationBarInsetsProvider1 navigationBarInsetsProvider2 |
| */ |
| final Task rootTask = createTask(mDisplayContent); |
| |
| final ActivityRecord activity0 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(rootTask, 0 /* userId */)); |
| final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( |
| TYPE_BASE_APPLICATION); |
| attrs.setTitle("AppWindow0"); |
| activity0.addWindow(createWindowState(attrs, activity0)); |
| |
| Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700); |
| Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200); |
| |
| rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1, |
| new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1}); |
| activity0.forAllWindows(window -> { |
| assertEquals(navigationBarInsetsRect1, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame()); |
| }, true); |
| |
| rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2, |
| new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1}); |
| |
| activity0.forAllWindows(window -> { |
| assertEquals(navigationBarInsetsRect2, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame()); |
| }, true); |
| } |
| |
| @Test |
| public void testRemoveLocalInsetsSourceProvider() { |
| /* |
| ___ rootTask _______________________________________________ |
| | | | | |
| activity0 container navigationBarInsetsProvider1 navigationBarInsetsProvider2 |
| / \ |
| activity1 activity2 |
| */ |
| final Task rootTask = createTask(mDisplayContent); |
| |
| final ActivityRecord activity0 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(rootTask, 0 /* userId */)); |
| final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( |
| TYPE_BASE_APPLICATION); |
| attrs.setTitle("AppWindow0"); |
| activity0.addWindow(createWindowState(attrs, activity0)); |
| |
| final Task container = createTaskInRootTask(rootTask, 0); |
| final ActivityRecord activity1 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(container, 0 /* userId */)); |
| final WindowManager.LayoutParams attrs1 = new WindowManager.LayoutParams( |
| TYPE_BASE_APPLICATION); |
| attrs1.setTitle("AppWindow1"); |
| activity1.addWindow(createWindowState(attrs1, activity1)); |
| |
| final ActivityRecord activity2 = createActivityRecord(mDisplayContent, |
| createTaskInRootTask(container, 0 /* userId */)); |
| final WindowManager.LayoutParams attrs2 = new WindowManager.LayoutParams( |
| TYPE_BASE_APPLICATION); |
| attrs2.setTitle("AppWindow2"); |
| activity2.addWindow(createWindowState(attrs2, activity2)); |
| |
| activity2.addWindow(createWindowState(attrs2, activity2)); |
| Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700); |
| Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200); |
| |
| rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1, |
| new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1}); |
| container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2, |
| new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2}); |
| mDisplayContent.getInsetsStateController().onPostLayout(); |
| rootTask.removeLocalInsetsSourceProvider(new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1}); |
| mDisplayContent.getInsetsStateController().onPostLayout(); |
| |
| activity0.forAllWindows(window -> { |
| assertEquals(null, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1)); |
| assertEquals(null, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2)); |
| }, true); |
| activity1.forAllWindows(window -> { |
| assertEquals(null, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1)); |
| assertEquals(navigationBarInsetsRect2, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame()); |
| }, true); |
| activity2.forAllWindows(window -> { |
| assertEquals(null, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1)); |
| assertEquals(navigationBarInsetsRect2, |
| window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame()); |
| }, true); |
| } |
| |
| /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ |
| private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { |
| private final int mLayer; |
| private boolean mIsAnimating; |
| private boolean mIsVisible; |
| private boolean mFillsParent; |
| private boolean mWaitForTransitStart; |
| private Integer mOrientation; |
| |
| private boolean mOnParentChangedCalled; |
| private boolean mOnDescendantOverrideCalled; |
| |
| /** |
| * Compares 2 window layers and returns -1 if the first is lesser than the second in terms |
| * of z-order and 1 otherwise. |
| */ |
| private static final Comparator<TestWindowContainer> SUBLAYER_COMPARATOR = (w1, w2) -> { |
| final int layer1 = w1.mLayer; |
| final int layer2 = w2.mLayer; |
| if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0)) { |
| // We insert the child window into the list ordered by the mLayer. For same layers, |
| // the negative one should go below others; the positive one should go above others. |
| return -1; |
| } |
| if (layer1 == layer2) return 0; |
| return 1; |
| }; |
| |
| TestWindowContainer(WindowManagerService wm, int layer, boolean isAnimating, |
| boolean isVisible, boolean waitTransitStart, Integer orientation) { |
| super(wm); |
| |
| mLayer = layer; |
| mIsAnimating = isAnimating; |
| mIsVisible = isVisible; |
| mFillsParent = true; |
| mOrientation = orientation; |
| mWaitForTransitStart = waitTransitStart; |
| spyOn(mSurfaceAnimator); |
| doReturn(mIsAnimating).when(mSurfaceAnimator).isAnimating(); |
| doReturn(ANIMATION_TYPE_APP_TRANSITION).when(mSurfaceAnimator).getAnimationType(); |
| } |
| |
| TestWindowContainer getParentWindow() { |
| return (TestWindowContainer) getParent(); |
| } |
| |
| int getChildrenCount() { |
| return mChildren.size(); |
| } |
| |
| TestWindowContainer addChildWindow(TestWindowContainer child) { |
| addChild(child, SUBLAYER_COMPARATOR); |
| return child; |
| } |
| |
| TestWindowContainer addChildWindow(TestWindowContainerBuilder childBuilder) { |
| TestWindowContainer child = childBuilder.build(); |
| addChild(child, SUBLAYER_COMPARATOR); |
| return child; |
| } |
| |
| TestWindowContainer addChildWindow() { |
| return addChildWindow(new TestWindowContainerBuilder(mWmService).setLayer(1)); |
| } |
| |
| @Override |
| void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { |
| mOnParentChangedCalled = true; |
| } |
| |
| @Override |
| void onDescendantOverrideConfigurationChanged() { |
| mOnDescendantOverrideCalled = true; |
| super.onDescendantOverrideConfigurationChanged(); |
| } |
| |
| @Override |
| boolean isVisible() { |
| return mIsVisible; |
| } |
| |
| @Override |
| int getOrientation(int candidate) { |
| return mOrientation != null ? mOrientation : super.getOrientation(candidate); |
| } |
| |
| @Override |
| int getOrientation() { |
| return getOrientation(super.mOrientation); |
| } |
| |
| @Override |
| boolean fillsParent() { |
| return mFillsParent; |
| } |
| |
| void setFillsParent(boolean fillsParent) { |
| mFillsParent = fillsParent; |
| } |
| |
| @Override |
| boolean isWaitingForTransitionStart() { |
| return mWaitForTransitStart; |
| } |
| } |
| |
| private static class TestWindowContainerBuilder { |
| private final WindowManagerService mWm; |
| private int mLayer; |
| private boolean mIsAnimating; |
| private boolean mIsVisible; |
| private boolean mIsWaitTransitStart; |
| private Integer mOrientation; |
| |
| TestWindowContainerBuilder(WindowManagerService wm) { |
| mWm = wm; |
| mLayer = 0; |
| mIsAnimating = false; |
| mIsVisible = false; |
| mOrientation = null; |
| } |
| |
| TestWindowContainerBuilder setLayer(int layer) { |
| mLayer = layer; |
| return this; |
| } |
| |
| TestWindowContainerBuilder setIsAnimating(boolean isAnimating) { |
| mIsAnimating = isAnimating; |
| return this; |
| } |
| |
| TestWindowContainerBuilder setIsVisible(boolean isVisible) { |
| mIsVisible = isVisible; |
| return this; |
| } |
| |
| TestWindowContainerBuilder setOrientation(int orientation) { |
| mOrientation = orientation; |
| return this; |
| } |
| |
| TestWindowContainerBuilder setWaitForTransitionStart(boolean waitTransitStart) { |
| mIsWaitTransitStart = waitTransitStart; |
| return this; |
| } |
| |
| TestWindowContainer build() { |
| return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible, |
| mIsWaitTransitStart, mOrientation); |
| } |
| } |
| |
| private static class MockSurfaceBuildingContainer extends WindowContainer<WindowContainer> |
| implements AutoCloseable { |
| private final SurfaceSession mSession = new SurfaceSession(); |
| |
| MockSurfaceBuildingContainer(WindowManagerService wm) { |
| super(wm); |
| } |
| |
| static class MockSurfaceBuilder extends SurfaceControl.Builder { |
| MockSurfaceBuilder(SurfaceSession ss) { |
| super(ss); |
| } |
| |
| @Override |
| public SurfaceControl build() { |
| return mock(SurfaceControl.class); |
| } |
| } |
| |
| @Override |
| SurfaceControl.Builder makeChildSurface(WindowContainer child) { |
| return new MockSurfaceBuilder(mSession); |
| } |
| |
| @Override |
| public void close() { |
| mSession.kill(); |
| } |
| } |
| |
| private static class TestWindowContainerListener implements WindowContainerListener { |
| private Configuration mConfiguration = new Configuration(); |
| private DisplayContent mDisplayContent; |
| |
| @Override |
| public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { |
| mConfiguration.setTo(overrideConfiguration); |
| } |
| |
| @Override |
| public void onDisplayChanged(DisplayContent dc) { |
| mDisplayContent = dc; |
| } |
| } |
| } |