1 /*
2  * Copyright (C) 2023 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.server.wm;
18 
19 import static android.view.WindowManager.TRANSIT_CHANGE;
20 
21 import static com.android.server.wm.DeviceStateController.DeviceState.FOLDED;
22 import static com.android.server.wm.DeviceStateController.DeviceState.HALF_FOLDED;
23 import static com.android.server.wm.DeviceStateController.DeviceState.OPEN;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.ArgumentMatchers.anyInt;
29 import static org.mockito.ArgumentMatchers.eq;
30 import static org.mockito.ArgumentMatchers.isNull;
31 import static org.mockito.Mockito.clearInvocations;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
35 
36 import android.animation.ValueAnimator;
37 import android.content.Context;
38 import android.content.res.Resources;
39 import android.graphics.Rect;
40 import android.platform.test.annotations.Presubmit;
41 import android.window.TransitionRequestInfo.DisplayChange;
42 
43 import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
44 import static com.android.server.wm.DeviceStateController.DeviceState.REAR;
45 
46 import androidx.test.filters.SmallTest;
47 
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.ArgumentCaptor;
53 import org.mockito.Mock;
54 import org.mockito.MockitoAnnotations;
55 
56 /**
57  * Tests for the {@link WindowToken} class.
58  *
59  * Build/Install/Run:
60  * atest WmTests:PhysicalDisplaySwitchTransitionLauncherTest
61  */
62 @SmallTest
63 @Presubmit
64 @RunWith(WindowTestRunner.class)
65 public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase {
66 
67     @Mock
68     DisplayContent mDisplayContent;
69     @Mock
70     Context mContext;
71     @Mock
72     Resources mResources;
73     @Mock
74     ActivityTaskManagerService mActivityTaskManagerService;
75     @Mock
76     TransitionController mTransitionController;
77 
78     private PhysicalDisplaySwitchTransitionLauncher mTarget;
79     private float mOriginalAnimationScale;
80 
81     @Before
setUp()82     public void setUp() {
83         MockitoAnnotations.initMocks(this);
84         when(mContext.getResources()).thenReturn(mResources);
85         mTarget = new PhysicalDisplaySwitchTransitionLauncher(mDisplayContent,
86                 mActivityTaskManagerService, mContext, mTransitionController);
87         mOriginalAnimationScale = ValueAnimator.getDurationScale();
88     }
89 
90     @After
after()91     public void after() {
92         ValueAnimator.setDurationScale(mOriginalAnimationScale);
93     }
94 
95     @Test
testDisplaySwitchAfterUnfoldToOpen_animationsEnabled_requestsTransition()96     public void testDisplaySwitchAfterUnfoldToOpen_animationsEnabled_requestsTransition() {
97         givenAllAnimationsEnabled();
98         mTarget.foldStateChanged(FOLDED);
99 
100         mTarget.foldStateChanged(OPEN);
101         mTarget.requestDisplaySwitchTransitionIfNeeded(
102                 /* displayId= */ 123,
103                 /* oldDisplayWidth= */ 100,
104                 /* oldDisplayHeight= */ 150,
105                 /* newDisplayWidth= */ 200,
106                 /* newDisplayHeight= */ 250
107         );
108 
109         ArgumentCaptor<DisplayChange> displayChangeArgumentCaptor =
110                 ArgumentCaptor.forClass(DisplayChange.class);
111         verify(mTransitionController).requestTransitionIfNeeded(eq(TRANSIT_CHANGE), /* flags= */
112                 eq(0), eq(mDisplayContent), eq(mDisplayContent), /* remoteTransition= */ isNull(),
113                 displayChangeArgumentCaptor.capture());
114         assertThat(displayChangeArgumentCaptor.getValue().getDisplayId()).isEqualTo(123);
115         assertThat(displayChangeArgumentCaptor.getValue().getStartAbsBounds()).isEqualTo(
116                 new Rect(0, 0, 100, 150));
117         assertThat(displayChangeArgumentCaptor.getValue().getEndAbsBounds()).isEqualTo(
118                 new Rect(0, 0, 200, 250));
119     }
120 
121     @Test
testDisplaySwitchAfterFolding_animationEnabled_doesNotRequestTransition()122     public void testDisplaySwitchAfterFolding_animationEnabled_doesNotRequestTransition() {
123         givenAllAnimationsEnabled();
124         mTarget.foldStateChanged(OPEN);
125 
126         mTarget.foldStateChanged(FOLDED);
127         requestDisplaySwitch();
128 
129         assertTransitionNotRequested();
130     }
131 
132     @Test
testDisplaySwitchAfterUnfoldingToHalf_animationEnabled_requestsTransition()133     public void testDisplaySwitchAfterUnfoldingToHalf_animationEnabled_requestsTransition() {
134         givenAllAnimationsEnabled();
135         mTarget.foldStateChanged(FOLDED);
136 
137         mTarget.foldStateChanged(HALF_FOLDED);
138         requestDisplaySwitch();
139 
140         assertTransitionRequested();
141     }
142 
143     @Test
testDisplaySwitchSecondTimeAfterUnfolding_animationEnabled_noTransition()144     public void testDisplaySwitchSecondTimeAfterUnfolding_animationEnabled_noTransition() {
145         givenAllAnimationsEnabled();
146         mTarget.foldStateChanged(FOLDED);
147         mTarget.foldStateChanged(OPEN);
148         requestDisplaySwitch();
149         clearInvocations(mTransitionController);
150 
151         requestDisplaySwitch();
152 
153         assertTransitionNotRequested();
154     }
155 
156 
157     @Test
testDisplaySwitchAfterGoingToRearAndBack_animationEnabled_noTransition()158     public void testDisplaySwitchAfterGoingToRearAndBack_animationEnabled_noTransition() {
159         givenAllAnimationsEnabled();
160         mTarget.foldStateChanged(OPEN);
161 
162         mTarget.foldStateChanged(REAR);
163         mTarget.foldStateChanged(OPEN);
164         requestDisplaySwitch();
165 
166         assertTransitionNotRequested();
167     }
168 
169     @Test
testDisplaySwitchAfterUnfoldingAndFolding_animationEnabled_noTransition()170     public void testDisplaySwitchAfterUnfoldingAndFolding_animationEnabled_noTransition() {
171         givenAllAnimationsEnabled();
172         mTarget.foldStateChanged(FOLDED);
173         mTarget.foldStateChanged(OPEN);
174         // No request display switch event (simulate very fast fold after unfold, even before
175         // the displays switched)
176         mTarget.foldStateChanged(FOLDED);
177 
178         requestDisplaySwitch();
179 
180         assertTransitionNotRequested();
181     }
182 
183     @Test
testDisplaySwitch_whenShellTransitionsNotEnabled_noTransition()184     public void testDisplaySwitch_whenShellTransitionsNotEnabled_noTransition() {
185         givenAllAnimationsEnabled();
186         givenShellTransitionsEnabled(false);
187         mTarget.foldStateChanged(FOLDED);
188 
189         mTarget.foldStateChanged(OPEN);
190         requestDisplaySwitch();
191 
192         assertTransitionNotRequested();
193     }
194 
195     @Test
testDisplaySwitch_whenAnimationsDisabled_noTransition()196     public void testDisplaySwitch_whenAnimationsDisabled_noTransition() {
197         givenAllAnimationsEnabled();
198         givenAnimationsEnabled(false);
199         mTarget.foldStateChanged(FOLDED);
200 
201         mTarget.foldStateChanged(OPEN);
202         requestDisplaySwitch();
203 
204         assertTransitionNotRequested();
205     }
206 
207     @Test
testDisplaySwitch_whenUnfoldAnimationDisabled_noTransition()208     public void testDisplaySwitch_whenUnfoldAnimationDisabled_noTransition() {
209         givenAllAnimationsEnabled();
210         givenUnfoldTransitionEnabled(false);
211         mTarget.foldStateChanged(FOLDED);
212 
213         mTarget.foldStateChanged(OPEN);
214         requestDisplaySwitch();
215 
216         assertTransitionNotRequested();
217     }
218 
219     @Test
testDisplaySwitch_whenNoContentInDisplayContent_noTransition()220     public void testDisplaySwitch_whenNoContentInDisplayContent_noTransition() {
221         givenAllAnimationsEnabled();
222         givenDisplayContentHasContent(false);
223         mTarget.foldStateChanged(FOLDED);
224 
225         mTarget.foldStateChanged(OPEN);
226         requestDisplaySwitch();
227 
228         assertTransitionNotRequested();
229     }
230 
assertTransitionRequested()231     private void assertTransitionRequested() {
232         verify(mTransitionController).requestTransitionIfNeeded(anyInt(), anyInt(), any(), any(),
233                 any(), any());
234     }
235 
assertTransitionNotRequested()236     private void assertTransitionNotRequested() {
237         verify(mTransitionController, never()).requestTransitionIfNeeded(anyInt(), anyInt(), any(),
238                 any(), any(), any());
239     }
240 
requestDisplaySwitch()241     private void requestDisplaySwitch() {
242         mTarget.requestDisplaySwitchTransitionIfNeeded(
243                 /* displayId= */ 123,
244                 /* oldDisplayWidth= */ 100,
245                 /* oldDisplayHeight= */ 150,
246                 /* newDisplayWidth= */ 200,
247                 /* newDisplayHeight= */ 250
248         );
249     }
250 
givenAllAnimationsEnabled()251     private void givenAllAnimationsEnabled() {
252         givenAnimationsEnabled(true);
253         givenUnfoldTransitionEnabled(true);
254         givenShellTransitionsEnabled(true);
255         givenDisplayContentHasContent(true);
256     }
257 
givenUnfoldTransitionEnabled(boolean enabled)258     private void givenUnfoldTransitionEnabled(boolean enabled) {
259         when(mResources.getBoolean(config_unfoldTransitionEnabled)).thenReturn(enabled);
260     }
261 
givenAnimationsEnabled(boolean enabled)262     private void givenAnimationsEnabled(boolean enabled) {
263         ValueAnimator.setDurationScale(enabled ? 1.0f : 0.0f);
264     }
265 
givenShellTransitionsEnabled(boolean enabled)266     private void givenShellTransitionsEnabled(boolean enabled) {
267         when(mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled);
268     }
269 
givenDisplayContentHasContent(boolean hasContent)270     private void givenDisplayContentHasContent(boolean hasContent) {
271         when(mDisplayContent.getLastHasContent()).thenReturn(hasContent);
272     }
273 }
274