1 /*
2  * Copyright (C) 2021 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.systemui.shared.system;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
21 import static android.view.RemoteAnimationTarget.MODE_CHANGING;
22 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
23 import static android.view.RemoteAnimationTarget.MODE_OPENING;
24 import static android.view.WindowManager.TRANSIT_CHANGE;
25 import static android.view.WindowManager.TRANSIT_CLOSE;
26 import static android.view.WindowManager.TRANSIT_OPEN;
27 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
28 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
29 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
30 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
31 
32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
33 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
34 
35 import static org.junit.Assert.assertEquals;
36 import static org.junit.Assert.assertTrue;
37 import static org.junit.Assert.fail;
38 import static org.mockito.Mockito.mock;
39 
40 import android.app.ActivityManager;
41 import android.graphics.Rect;
42 import android.testing.AndroidTestingRunner;
43 import android.testing.TestableLooper;
44 import android.view.RemoteAnimationTarget;
45 import android.view.SurfaceControl;
46 import android.view.WindowManager;
47 import android.window.TransitionInfo;
48 
49 import androidx.test.filters.SmallTest;
50 
51 import com.android.systemui.SysuiTestCase;
52 import com.android.systemui.animation.RemoteAnimationTargetCompat;
53 import com.android.wm.shell.shared.TransitionUtil;
54 
55 import org.junit.Before;
56 import org.junit.Test;
57 import org.junit.runner.RunWith;
58 import org.mockito.MockitoAnnotations;
59 
60 @SmallTest
61 @RunWith(AndroidTestingRunner.class)
62 @TestableLooper.RunWithLooper
63 public class RemoteTransitionTest extends SysuiTestCase {
64 
65     @Before
setUp()66     public void setUp() {
67         MockitoAnnotations.initMocks(this);
68     }
69 
70     @Test
testLegacyTargetExtract()71     public void testLegacyTargetExtract() {
72         TransitionInfo combined = new TransitionInfoBuilder(TRANSIT_CLOSE)
73                 .addChange(TRANSIT_CHANGE, FLAG_SHOW_WALLPAPER,
74                         createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_STANDARD))
75                 // Embedded TaskFragment should be excluded when animated with Task.
76                 .addChange(TRANSIT_CLOSE, FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, null /* taskInfo */)
77                 .addChange(TRANSIT_CLOSE, 0 /* flags */,
78                         createTaskInfo(2 /* taskId */, ACTIVITY_TYPE_STANDARD))
79                 .addChange(TRANSIT_OPEN, FLAG_IS_WALLPAPER, null /* taskInfo */)
80                 .addChange(TRANSIT_CHANGE, FLAG_IS_DIVIDER_BAR, null /* taskInfo */)
81                 .build();
82         // Check apps extraction
83         RemoteAnimationTarget[] wrapped = RemoteAnimationTargetCompat.wrapApps(combined,
84                 mock(SurfaceControl.Transaction.class), null /* leashes */);
85         assertEquals(2, wrapped.length);
86         int changeLayer = -1;
87         int closeLayer = -1;
88         for (RemoteAnimationTarget t : wrapped) {
89             if (t.mode == MODE_CHANGING) {
90                 changeLayer = t.prefixOrderIndex;
91             } else if (t.mode == MODE_CLOSING) {
92                 closeLayer = t.prefixOrderIndex;
93             } else {
94                 fail();
95             }
96         }
97         // verify ordering
98         assertTrue(closeLayer < changeLayer);
99 
100         // Check wallpaper extraction
101         RemoteAnimationTarget[] wallps = RemoteAnimationTargetCompat.wrapNonApps(combined,
102                 true /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
103         assertEquals(1, wallps.length);
104         assertTrue(wallps[0].prefixOrderIndex < closeLayer);
105         assertEquals(MODE_OPENING, wallps[0].mode);
106 
107         // Check non-apps extraction
108         RemoteAnimationTarget[] nonApps = RemoteAnimationTargetCompat.wrapNonApps(combined,
109                 false /* wallpapers */, mock(SurfaceControl.Transaction.class), null /* leashes */);
110         assertEquals(1, nonApps.length);
111         assertTrue(nonApps[0].prefixOrderIndex == Integer.MAX_VALUE);
112         assertEquals(MODE_CHANGING, nonApps[0].mode);
113     }
114 
115     @Test
116     public void testLegacyTargetWrapper() {
117         TransitionInfo tinfo = new TransitionInfoBuilder(TRANSIT_CLOSE)
118                 .addChange(TRANSIT_CHANGE, FLAG_TRANSLUCENT,
119                         createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_STANDARD)).build();
120         final TransitionInfo.Change change = tinfo.getChanges().get(0);
121         final Rect endBounds = new Rect(40, 60, 140, 200);
122         change.setTaskInfo(createTaskInfo(1 /* taskId */, ACTIVITY_TYPE_HOME));
123         change.setEndAbsBounds(endBounds);
124         change.setEndRelOffset(0, 0);
125         RemoteAnimationTarget wrapped = TransitionUtil.newTarget(
126                 change, 0 /* order */, tinfo, mock(SurfaceControl.Transaction.class), null);
127         assertEquals(ACTIVITY_TYPE_HOME, wrapped.windowConfiguration.getActivityType());
128         assertEquals(new Rect(0, 0, 100, 140), wrapped.localBounds);
129         assertEquals(endBounds, wrapped.screenSpaceBounds);
130         assertTrue(wrapped.isTranslucent);
131     }
132 
133     class TransitionInfoBuilder {
134         final TransitionInfo mInfo;
135 
136         TransitionInfoBuilder(@WindowManager.TransitionType int type) {
137             mInfo = new TransitionInfo(type, 0 /* flags */);
138             mInfo.addRootLeash(0, createMockSurface(true /* valid */), 0, 0);
139         }
140 
141         TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
142                 @TransitionInfo.ChangeFlags int flags, ActivityManager.RunningTaskInfo taskInfo) {
143             final TransitionInfo.Change change =
144                     new TransitionInfo.Change(null /* token */, createMockSurface(true));
145             change.setMode(mode);
146             change.setFlags(flags);
147             change.setTaskInfo(taskInfo);
148             change.setDisplayId(0, 0);
149             mInfo.addChange(change);
150             return this;
151         }
152 
153         TransitionInfo build() {
154             return mInfo;
155         }
156     }
157 
158     private static SurfaceControl createMockSurface(boolean valid) {
159         SurfaceControl sc = mock(SurfaceControl.class);
160         if (valid) {
161             doReturn(true).when(sc).isValid();
162             doReturn("TestSurface").when(sc).toString();
163         }
164         return sc;
165     }
166 
167     private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int activityType) {
168         ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
169         taskInfo.taskId = taskId;
170         taskInfo.configuration.windowConfiguration.setActivityType(activityType);
171         return taskInfo;
172     }
173 
174 }
175