1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.pip.phone;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.times;
22 import static org.mockito.Mockito.verify;
23 
24 import android.graphics.Rect;
25 import android.testing.AndroidTestingRunner;
26 import android.testing.TestableLooper;
27 import android.util.Size;
28 
29 import androidx.test.filters.SmallTest;
30 
31 import com.android.wm.shell.ShellTestCase;
32 import com.android.wm.shell.common.DisplayLayout;
33 import com.android.wm.shell.common.FloatingContentCoordinator;
34 import com.android.wm.shell.common.ShellExecutor;
35 import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
36 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
37 import com.android.wm.shell.common.pip.PipBoundsState;
38 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
39 import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
40 import com.android.wm.shell.common.pip.PipSnapAlgorithm;
41 import com.android.wm.shell.common.pip.PipUiEventLogger;
42 import com.android.wm.shell.common.pip.SizeSpecSource;
43 import com.android.wm.shell.pip.PipTaskOrganizer;
44 import com.android.wm.shell.pip.PipTransitionController;
45 import com.android.wm.shell.sysui.ShellInit;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.mockito.Mock;
51 import org.mockito.Mockito;
52 import org.mockito.MockitoAnnotations;
53 
54 /**
55  * Unit tests against {@link PipTouchHandler}, including but not limited to:
56  * - Update movement bounds based on new bounds
57  * - Update movement bounds based on IME/shelf
58  * - Update movement bounds to PipResizeHandler
59  */
60 @RunWith(AndroidTestingRunner.class)
61 @SmallTest
62 @TestableLooper.RunWithLooper(setAsMainLooper = true)
63 public class PipTouchHandlerTest extends ShellTestCase {
64 
65     private static final int INSET = 10;
66     private static final int PIP_LENGTH = 100;
67 
68     private PipTouchHandler mPipTouchHandler;
69 
70     @Mock
71     private PhonePipMenuController mPhonePipMenuController;
72 
73     @Mock
74     private PipTaskOrganizer mPipTaskOrganizer;
75 
76     @Mock
77     private PipTransitionController mMockPipTransitionController;
78 
79     @Mock
80     private FloatingContentCoordinator mFloatingContentCoordinator;
81 
82     @Mock
83     private PipUiEventLogger mPipUiEventLogger;
84 
85     @Mock
86     private ShellInit mShellInit;
87 
88     @Mock
89     private ShellExecutor mMainExecutor;
90 
91     private PipBoundsState mPipBoundsState;
92     private PipBoundsAlgorithm mPipBoundsAlgorithm;
93     private PipSnapAlgorithm mPipSnapAlgorithm;
94     private PipMotionHelper mMotionHelper;
95     private PipResizeGestureHandler mPipResizeGestureHandler;
96     private SizeSpecSource mSizeSpecSource;
97     private PipDisplayLayoutState mPipDisplayLayoutState;
98 
99     private DisplayLayout mDisplayLayout;
100     private Rect mInsetBounds;
101     private Rect mPipBounds;
102     private Rect mCurBounds;
103     private boolean mFromImeAdjustment;
104     private boolean mFromShelfAdjustment;
105     private int mDisplayRotation;
106     private int mImeHeight;
107 
108     @Before
setUp()109     public void setUp() throws Exception {
110         MockitoAnnotations.initMocks(this);
111         mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
112         mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState);
113         mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState);
114         mPipSnapAlgorithm = new PipSnapAlgorithm();
115         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
116                 new PipKeepClearAlgorithmInterface() {}, mPipDisplayLayoutState, mSizeSpecSource);
117         PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
118                 mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
119                 mMockPipTransitionController, mFloatingContentCoordinator);
120         mPipTouchHandler = new PipTouchHandler(mContext, mShellInit, mPhonePipMenuController,
121                 mPipBoundsAlgorithm, mPipBoundsState, mSizeSpecSource, mPipTaskOrganizer,
122                 pipMotionHelper, mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
123         // We aren't actually using ShellInit, so just call init directly
124         mPipTouchHandler.onInit();
125         mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
126         mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
127         mPipTouchHandler.setPipMotionHelper(mMotionHelper);
128         mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
129 
130         mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
131         mPipDisplayLayoutState.setDisplayLayout(mDisplayLayout);
132         mInsetBounds = new Rect(mPipBoundsState.getDisplayBounds().left + INSET,
133                 mPipBoundsState.getDisplayBounds().top + INSET,
134                 mPipBoundsState.getDisplayBounds().right - INSET,
135                 mPipBoundsState.getDisplayBounds().bottom - INSET);
136         // minBounds of 100x100 bottom right corner
137         mPipBounds = new Rect(mPipBoundsState.getDisplayBounds().right - INSET - PIP_LENGTH,
138                 mPipBoundsState.getDisplayBounds().bottom - INSET - PIP_LENGTH,
139                 mPipBoundsState.getDisplayBounds().right - INSET,
140                 mPipBoundsState.getDisplayBounds().bottom - INSET);
141         mCurBounds = new Rect(mPipBounds);
142         mFromImeAdjustment = false;
143         mFromShelfAdjustment = false;
144         mDisplayRotation = 0;
145         mImeHeight = 100;
146     }
147 
148     @Test
instantiate_addInitCallback()149     public void instantiate_addInitCallback() {
150         verify(mShellInit, times(1)).addInitCallback(any(), any());
151     }
152 
153     @Test
updateMovementBounds_minMaxBounds()154     public void updateMovementBounds_minMaxBounds() {
155         final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
156                 mPipBoundsState.getDisplayBounds().height());
157         Rect expectedMovementBounds = new Rect();
158         mPipBoundsAlgorithm.getMovementBounds(mPipBounds, mInsetBounds, expectedMovementBounds,
159                 0);
160 
161         mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mPipBounds, mCurBounds,
162                 mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
163 
164         // getting the expected min and max size
165         float aspectRatio = (float) mPipBounds.width() / mPipBounds.height();
166         Size expectedMinSize = mSizeSpecSource.getMinSize(aspectRatio);
167         Size expectedMaxSize = mSizeSpecSource.getMaxSize(aspectRatio);
168 
169         assertEquals(expectedMovementBounds, mPipBoundsState.getNormalMovementBounds());
170         verify(mPipResizeGestureHandler, times(1))
171                 .updateMinSize(expectedMinSize.getWidth(), expectedMinSize.getHeight());
172 
173         verify(mPipResizeGestureHandler, times(1))
174                 .updateMaxSize(expectedMaxSize.getWidth(), expectedMaxSize.getHeight());
175     }
176 }
177