• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 androidx.window.extensions.embedding;
18 
19 import static android.view.Display.DEFAULT_DISPLAY;
20 
21 import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
22 import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER;
23 
24 import static org.junit.Assert.assertFalse;
25 import static org.mockito.Mockito.doReturn;
26 import static org.mockito.Mockito.mock;
27 
28 import android.annotation.NonNull;
29 import android.app.Activity;
30 import android.content.Intent;
31 import android.content.pm.ActivityInfo;
32 import android.content.res.Configuration;
33 import android.content.res.Resources;
34 import android.graphics.Point;
35 import android.graphics.Rect;
36 import android.util.Pair;
37 import android.view.WindowMetrics;
38 import android.window.TaskFragmentInfo;
39 import android.window.WindowContainerToken;
40 
41 import androidx.window.extensions.core.util.function.Predicate;
42 import androidx.window.extensions.embedding.SplitAttributes.SplitType;
43 import androidx.window.extensions.layout.DisplayFeature;
44 import androidx.window.extensions.layout.FoldingFeature;
45 import androidx.window.extensions.layout.WindowLayoutInfo;
46 
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.List;
50 
51 // Suppress GuardedBy warning on unit tests
52 @SuppressWarnings("GuardedBy")
53 public class EmbeddingTestUtils {
54     static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200);
55     static final int TASK_ID = 10;
56     static final SplitType SPLIT_TYPE = SplitType.RatioSplitType.splitEqually();
57     static final SplitAttributes SPLIT_ATTRIBUTES = new SplitAttributes.Builder().build();
58     static final String TEST_TAG = "test";
59     /** Default finish behavior in Jetpack. */
60     static final int DEFAULT_FINISH_PRIMARY_WITH_SECONDARY = FINISH_NEVER;
61     static final int DEFAULT_FINISH_SECONDARY_WITH_PRIMARY = FINISH_ALWAYS;
62     private static final float SPLIT_RATIO = 0.5f;
63 
EmbeddingTestUtils()64     private EmbeddingTestUtils() {}
65 
66     /** Gets the bounds of a TaskFragment that is in split. */
getSplitBounds(boolean isPrimary)67     static Rect getSplitBounds(boolean isPrimary) {
68         return getSplitBounds(isPrimary, false /* shouldSplitHorizontally */);
69     }
70 
71     /** Gets the bounds of a TaskFragment that is in split. */
getSplitBounds(boolean isPrimary, boolean shouldSplitHorizontally)72     static Rect getSplitBounds(boolean isPrimary, boolean shouldSplitHorizontally) {
73         final int dimension = (int) (
74                 (shouldSplitHorizontally ? TASK_BOUNDS.height() : TASK_BOUNDS.width())
75                         * SPLIT_RATIO);
76         if (shouldSplitHorizontally) {
77             return isPrimary
78                     ? new Rect(
79                             TASK_BOUNDS.left,
80                             TASK_BOUNDS.top,
81                             TASK_BOUNDS.right,
82                             TASK_BOUNDS.top + dimension)
83                     : new Rect(
84                             TASK_BOUNDS.left,
85                             TASK_BOUNDS.top + dimension,
86                             TASK_BOUNDS.right,
87                             TASK_BOUNDS.bottom);
88         }
89         return isPrimary
90                 ? new Rect(
91                         TASK_BOUNDS.left,
92                         TASK_BOUNDS.top,
93                         TASK_BOUNDS.left + dimension,
94                         TASK_BOUNDS.bottom)
95                 : new Rect(
96                         TASK_BOUNDS.left + dimension,
97                         TASK_BOUNDS.top,
98                         TASK_BOUNDS.right,
99                         TASK_BOUNDS.bottom);
100     }
101 
102     /** Creates a rule to always split the given activity and the given intent. */
createSplitRule(@onNull Activity primaryActivity, @NonNull Intent secondaryIntent)103     static SplitRule createSplitRule(@NonNull Activity primaryActivity,
104             @NonNull Intent secondaryIntent) {
105         return createSplitRule(primaryActivity, secondaryIntent, true /* clearTop */);
106     }
107 
108     /** Creates a rule to always split the given activity and the given intent. */
createSplitRule(@onNull Activity primaryActivity, @NonNull Intent secondaryIntent, boolean clearTop)109     static SplitRule createSplitRule(@NonNull Activity primaryActivity,
110             @NonNull Intent secondaryIntent, boolean clearTop) {
111         final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent);
112         return createSplitPairRuleBuilder(
113                 activityPair -> false,
114                 targetPair::equals,
115                 w -> true)
116                 .setDefaultSplitAttributes(
117                         new SplitAttributes.Builder()
118                                 .setSplitType(SPLIT_TYPE)
119                                 .build()
120                 )
121                 .setShouldClearTop(clearTop)
122                 .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
123                 .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY)
124                 .setTag(TEST_TAG)
125                 .build();
126     }
127 
128     /** Creates a rule to always split the given activities. */
createSplitRule(@onNull Activity primaryActivity, @NonNull Activity secondaryActivity)129     static SplitRule createSplitRule(@NonNull Activity primaryActivity,
130             @NonNull Activity secondaryActivity) {
131         return createSplitRule(primaryActivity, secondaryActivity,
132                 DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
133                 true /* clearTop */);
134     }
135 
136     /** Creates a rule to always split the given activities. */
createSplitRule(@onNull Activity primaryActivity, @NonNull Activity secondaryActivity, boolean clearTop)137     static SplitRule createSplitRule(@NonNull Activity primaryActivity,
138             @NonNull Activity secondaryActivity, boolean clearTop) {
139         return createSplitRule(primaryActivity, secondaryActivity,
140                 DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
141                 clearTop);
142     }
143 
144     /** Creates a rule to always split the given activities with the given finish behaviors. */
createSplitRule(@onNull Activity primaryActivity, @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary, int finishSecondaryWithPrimary, boolean clearTop)145     static SplitRule createSplitRule(@NonNull Activity primaryActivity,
146             @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
147             int finishSecondaryWithPrimary, boolean clearTop) {
148         final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
149         return createSplitPairRuleBuilder(
150                 targetPair::equals,
151                 activityIntentPair -> false,
152                 w -> true)
153                 .setDefaultSplitAttributes(
154                         new SplitAttributes.Builder()
155                                 .setSplitType(SPLIT_TYPE)
156                                 .build()
157                 )
158                 .setFinishPrimaryWithSecondary(finishPrimaryWithSecondary)
159                 .setFinishSecondaryWithPrimary(finishSecondaryWithPrimary)
160                 .setShouldClearTop(clearTop)
161                 .setTag(TEST_TAG)
162                 .build();
163     }
164 
165     /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
166     @NonNull
createMockTaskFragmentInfo(@onNull TaskFragmentContainer container, @NonNull Activity activity)167     static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
168             @NonNull Activity activity) {
169         return createMockTaskFragmentInfo(container, activity, true /* isVisible */);
170     }
171 
172     /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
173     @NonNull
createMockTaskFragmentInfo(@onNull TaskFragmentContainer container, @NonNull Activity activity, boolean isVisible)174     static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
175             @NonNull Activity activity, boolean isVisible) {
176         return new TaskFragmentInfo(container.getTaskFragmentToken(),
177                 mock(WindowContainerToken.class),
178                 new Configuration(),
179                 1,
180                 isVisible,
181                 Collections.singletonList(activity.getActivityToken()),
182                 new ArrayList<>(),
183                 new Point(),
184                 false /* isTaskClearedForReuse */,
185                 false /* isTaskFragmentClearedForPip */,
186                 false /* isClearedForReorderActivityToFront */,
187                 new Point(),
188                 false /* isTopChild */);
189     }
190 
191     /** Creates a mock TaskFragmentInfo for the given TaskFragment. */
192     @NonNull
createMockTaskFragmentInfo(@onNull TaskFragmentContainer container, @NonNull Activity activity, boolean isVisible, boolean isOnTop)193     static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
194             @NonNull Activity activity, boolean isVisible, boolean isOnTop) {
195         return new TaskFragmentInfo(container.getTaskFragmentToken(),
196                 mock(WindowContainerToken.class),
197                 new Configuration(),
198                 1,
199                 isVisible,
200                 Collections.singletonList(activity.getActivityToken()),
201                 new ArrayList<>(),
202                 new Point(),
203                 false /* isTaskClearedForReuse */,
204                 false /* isTaskFragmentClearedForPip */,
205                 false /* isClearedForReorderActivityToFront */,
206                 new Point(),
207                 isOnTop);
208     }
209 
createActivityInfoWithMinDimensions()210     static ActivityInfo createActivityInfoWithMinDimensions() {
211         ActivityInfo aInfo = new ActivityInfo();
212         final Rect primaryBounds = getSplitBounds(true /* isPrimary */);
213         aInfo.windowLayout = new ActivityInfo.WindowLayout(0, 0, 0, 0, 0,
214                 primaryBounds.width() + 1, primaryBounds.height() + 1);
215         return aInfo;
216     }
217 
createTestTaskContainer()218     static TaskContainer createTestTaskContainer() {
219         Resources resources = mock(Resources.class);
220         doReturn(new Configuration()).when(resources).getConfiguration();
221         Activity activity = mock(Activity.class);
222         doReturn(resources).when(activity).getResources();
223         doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();
224 
225         return new TaskContainer(TASK_ID, activity, mock(SplitController.class));
226     }
227 
createTestTaskContainer(@onNull SplitController controller)228     static TaskContainer createTestTaskContainer(@NonNull SplitController controller) {
229         final TaskContainer taskContainer = createTestTaskContainer();
230         final int taskId = taskContainer.getTaskId();
231         // Should not call to create TaskContainer with the same task id twice.
232         assertFalse(controller.mTaskContainers.contains(taskId));
233         controller.mTaskContainers.put(taskId, taskContainer);
234         return taskContainer;
235     }
236 
createWindowLayoutInfo()237     static WindowLayoutInfo createWindowLayoutInfo() {
238         final FoldingFeature foldingFeature = new FoldingFeature(
239                 new Rect(
240                         TASK_BOUNDS.left,
241                         TASK_BOUNDS.top + TASK_BOUNDS.height() / 2 - 5,
242                         TASK_BOUNDS.right,
243                         TASK_BOUNDS.top + TASK_BOUNDS.height() / 2 + 5
244                         ),
245                 FoldingFeature.TYPE_HINGE,
246                 FoldingFeature.STATE_HALF_OPENED);
247         final List<DisplayFeature> displayFeatures = new ArrayList<>();
248         displayFeatures.add(foldingFeature);
249         return new WindowLayoutInfo(displayFeatures);
250     }
251 
createActivityBuilder( @onNull Predicate<Activity> activityPredicate, @NonNull Predicate<Intent> intentPredicate)252     static ActivityRule.Builder createActivityBuilder(
253             @NonNull Predicate<Activity> activityPredicate,
254             @NonNull Predicate<Intent> intentPredicate) {
255         return new ActivityRule.Builder(activityPredicate, intentPredicate);
256     }
257 
createSplitPairRuleBuilder( @onNull Predicate<Pair<Activity, Activity>> activitiesPairPredicate, @NonNull Predicate<Pair<Activity, Intent>> activityIntentPairPredicate, @NonNull Predicate<WindowMetrics> windowMetricsPredicate)258     static SplitPairRule.Builder createSplitPairRuleBuilder(
259             @NonNull Predicate<Pair<Activity, Activity>> activitiesPairPredicate,
260             @NonNull Predicate<Pair<Activity, Intent>> activityIntentPairPredicate,
261             @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
262         return new SplitPairRule.Builder(activitiesPairPredicate, activityIntentPairPredicate,
263                 windowMetricsPredicate);
264     }
265 
createSplitPlaceholderRuleBuilder( @onNull Intent placeholderIntent, @NonNull Predicate<Activity> activityPredicate, @NonNull Predicate<Intent> intentPredicate, @NonNull Predicate<WindowMetrics> windowMetricsPredicate)266     static SplitPlaceholderRule.Builder createSplitPlaceholderRuleBuilder(
267             @NonNull Intent placeholderIntent, @NonNull Predicate<Activity> activityPredicate,
268             @NonNull Predicate<Intent> intentPredicate,
269             @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
270         return new SplitPlaceholderRule.Builder(placeholderIntent, activityPredicate,
271                 intentPredicate, windowMetricsPredicate);
272     }
273 
274     @NonNull
createTfContainer( @onNull SplitController splitController, @NonNull Activity activity)275     static TaskFragmentContainer createTfContainer(
276             @NonNull SplitController splitController, @NonNull Activity activity) {
277         return createTfContainer(splitController, TASK_ID, activity);
278     }
279 
280     @NonNull
createTfContainer( @onNull SplitController splitController, int taskId, @NonNull Activity activity)281     static TaskFragmentContainer createTfContainer(
282             @NonNull SplitController splitController, int taskId, @NonNull Activity activity) {
283         return new TaskFragmentContainer.Builder(splitController, taskId, activity)
284                 .setPendingAppearedActivity(activity).build();
285     }
286 }
287