1 /*
2  * Copyright (C) 2024 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 android.server.wm.animations;
18 
19 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
20 import static android.server.wm.animations.ActivityTransitionTests.CustomBackgroundTransitionActivity.BACKGROUND_COLOR_KEY;
21 import static android.server.wm.animations.ActivityTransitionTests.CustomBackgroundTransitionActivity.ENTER_ANIM_KEY;
22 import static android.server.wm.animations.ActivityTransitionTests.CustomBackgroundTransitionActivity.EXIT_ANIM_KEY;
23 import static android.server.wm.animations.ActivityTransitionTests.EdgeExtensionActivity.BOTTOM;
24 import static android.server.wm.animations.ActivityTransitionTests.EdgeExtensionActivity.DIRECTION_KEY;
25 import static android.server.wm.animations.ActivityTransitionTests.EdgeExtensionActivity.LEFT;
26 import static android.server.wm.animations.ActivityTransitionTests.EdgeExtensionActivity.RIGHT;
27 import static android.server.wm.animations.ActivityTransitionTests.EdgeExtensionActivity.TOP;
28 import static android.server.wm.app.Components.TEST_ACTIVITY;
29 import static android.view.Display.DEFAULT_DISPLAY;
30 import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
31 import static android.view.RoundedCorner.POSITION_TOP_LEFT;
32 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
33 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
34 
35 import static org.junit.Assert.assertTrue;
36 import static org.junit.Assert.fail;
37 import static org.junit.Assume.assumeFalse;
38 
39 import android.app.Activity;
40 import android.app.ActivityOptions;
41 import android.app.Instrumentation;
42 import android.content.BroadcastReceiver;
43 import android.content.ComponentName;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.content.IntentFilter;
47 import android.graphics.Bitmap;
48 import android.graphics.Color;
49 import android.graphics.ColorSpace;
50 import android.graphics.Insets;
51 import android.graphics.Point;
52 import android.graphics.Rect;
53 import android.os.Bundle;
54 import android.os.Handler;
55 import android.os.Looper;
56 import android.os.SystemClock;
57 import android.platform.test.annotations.Presubmit;
58 import android.provider.Settings;
59 import android.server.wm.ActivityManagerTestBase;
60 import android.server.wm.Condition;
61 import android.server.wm.WindowManagerState;
62 import android.server.wm.cts.R;
63 import android.server.wm.settings.SettingsSession;
64 import android.util.Range;
65 import android.view.RoundedCorner;
66 import android.view.View;
67 import android.view.ViewGroup;
68 import android.view.WindowInsets;
69 
70 import androidx.annotation.Nullable;
71 import androidx.test.platform.app.InstrumentationRegistry;
72 
73 import org.junit.After;
74 import org.junit.Before;
75 import org.junit.ClassRule;
76 import org.junit.Test;
77 import org.junit.rules.TestRule;
78 
79 import java.util.ArrayList;
80 import java.util.concurrent.CountDownLatch;
81 import java.util.concurrent.TimeUnit;
82 import java.util.concurrent.atomic.AtomicLong;
83 import java.util.function.Function;
84 
85 /**
86  * <p>Build/Install/Run:
87  * atest CtsWindowManagerDeviceAnimations:ActivityTransitionTests
88  */
89 @Presubmit
90 public class ActivityTransitionTests extends ActivityManagerTestBase {
91     // Duration of the R.anim.alpha animation.
92     private static final long CUSTOM_ANIMATION_DURATION = 2000L;
93 
94     // Allowable range with error error for the R.anim.alpha animation duration.
95     private static final Range<Long> CUSTOM_ANIMATION_DURATION_RANGE = new Range<>(
96             CUSTOM_ANIMATION_DURATION - 200L, CUSTOM_ANIMATION_DURATION + 1000L);
97 
98     static final String TEST_METHOD_KEY = "test_method_key";
99     static final String TRANSITION_TYPE_KEY = "transition_type_key";
100 
101     static final int TEST_METHOD_OVERRIDE_PENDING_TRANSITION = 1;
102     static final int TEST_METHOD_OVERRIDE_ACTIVITY_TRANSITION = 2;
103     static final int TEST_METHOD_CLEAR_OVERRIDE_ACTIVITY_TRANSITION = 3;
104 
105     static final int TRANSITION_TYPE_OPEN = 0x1;
106     static final int TRANSITION_TYPE_CLOSE = 0x2;
107 
108     static final String ACTION_UPDATE =
109             "android.server.wm.animations.ActivityTransitionTests.ACTION_UPDATE";
110     static final String ACTION_FINISH =
111             "android.server.wm.animations.ActivityTransitionTests.ACTION_FINISH";
112 
113     // We need to allow for some variation stemming from color conversions
114     private static final float COLOR_VALUE_VARIANCE_TOLERANCE = 0.05f;
115 
116     @ClassRule
117     public static DisableImmersiveModeConfirmationRule mDisableImmersiveModeConfirmationRule =
118             new DisableImmersiveModeConfirmationRule();
119 
120     @ClassRule
121     public static final TestRule enableWindowAnimationRule = SettingsSession.overrideForTest(
122             Settings.Global.getUriFor(Settings.Global.WINDOW_ANIMATION_SCALE),
123             Settings.Global::getFloat,
124             Settings.Global::putFloat,
125             1.0f);
126 
127     @ClassRule
128     public static final TestRule enableTransitionAnimationRule = SettingsSession.overrideForTest(
129             Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE),
130             Settings.Global::getFloat,
131             Settings.Global::putFloat,
132             1.0f);
133 
134     @ClassRule
135     public static final TestRule enableAnimatorDurationRule = SettingsSession.overrideForTest(
136             Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
137             Settings.Global::getFloat,
138             Settings.Global::putFloat,
139             1.0f);
140 
141     @Before
setUp()142     public void setUp() throws Exception {
143         super.setUp();
144         mWmState.setSanityCheckWithFocusedWindow(false);
145         mWmState.waitForDisplayUnfrozen();
146     }
147 
148     @After
tearDown()149     public void tearDown() {
150         mWmState.setSanityCheckWithFocusedWindow(true);
151     }
152 
startLauncherActivity()153     private LauncherActivity startLauncherActivity() {
154         final Intent intent = new Intent(mContext, LauncherActivity.class)
155                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
156         final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
157         return (LauncherActivity) instrumentation.startActivitySync(intent);
158     }
159 
160     @Test
testActivityTransitionOverride()161     public void testActivityTransitionOverride() throws Exception {
162         final CountDownLatch latch = new CountDownLatch(1);
163         AtomicLong transitionStartTime = new AtomicLong();
164         AtomicLong transitionEndTime = new AtomicLong();
165 
166         final ActivityOptions.OnAnimationStartedListener startedListener = transitionStartTime::set;
167         final ActivityOptions.OnAnimationFinishedListener finishedListener = (t) -> {
168             transitionEndTime.set(t);
169             latch.countDown();
170         };
171 
172         final LauncherActivity launcherActivity = startLauncherActivity();
173 
174         final ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext,
175                 R.anim.alpha, 0 /* exitResId */, 0 /* backgroundColor */,
176                 new Handler(Looper.getMainLooper()), startedListener, finishedListener);
177         launcherActivity.startActivity(options, TransitionActivity.class);
178         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
179         waitAndAssertTopResumedActivity(new ComponentName(mContext, TransitionActivity.class),
180                 DEFAULT_DISPLAY, "Activity must be launched");
181 
182         latch.await(5, TimeUnit.SECONDS);
183         final long totalTime = transitionEndTime.get() - transitionStartTime.get();
184         assertTrue("Actual transition duration should be in the range "
185                 + "<" + CUSTOM_ANIMATION_DURATION_RANGE.getLower() + ", "
186                 + CUSTOM_ANIMATION_DURATION_RANGE.getUpper() + "> ms, "
187                 + "actual=" + totalTime, CUSTOM_ANIMATION_DURATION_RANGE.contains(totalTime));
188     }
189 
190     @Test
testTaskTransitionOverrideDisabled()191     public void testTaskTransitionOverrideDisabled() throws Exception {
192         final CountDownLatch latch = new CountDownLatch(1);
193         AtomicLong transitionStartTime = new AtomicLong();
194         AtomicLong transitionEndTime = new AtomicLong();
195 
196         final ActivityOptions.OnAnimationStartedListener startedListener = transitionStartTime::set;
197         final ActivityOptions.OnAnimationFinishedListener finishedListener = (t) -> {
198             transitionEndTime.set(t);
199             latch.countDown();
200         };
201 
202         // Overriding task transit animation is disabled, so default wallpaper close animation
203         // is played.
204         final Bundle bundle = ActivityOptions.makeCustomAnimation(mContext,
205                 R.anim.alpha, 0 /* exitResId */, 0 /* backgroundColor */,
206                 new Handler(Looper.getMainLooper()), startedListener, finishedListener).toBundle();
207         final Intent intent = new Intent().setComponent(TEST_ACTIVITY)
208                 .addFlags(FLAG_ACTIVITY_NEW_TASK);
209         mContext.startActivity(intent, bundle);
210         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
211         waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
212                 "Activity must be launched");
213 
214         latch.await(5, TimeUnit.SECONDS);
215         final long totalTime = transitionEndTime.get() - transitionStartTime.get();
216         assertTrue("Actual transition duration should be out of the range "
217                 + "<" + CUSTOM_ANIMATION_DURATION_RANGE.getLower() + ", "
218                 + CUSTOM_ANIMATION_DURATION_RANGE.getUpper() + "> ms, "
219                 + "actual=" + totalTime, !CUSTOM_ANIMATION_DURATION_RANGE.contains(totalTime));
220     }
221 
222     @Test
testTaskWindowAnimationOverrideDisabled()223     public void testTaskWindowAnimationOverrideDisabled() throws Exception {
224         final CountDownLatch latch = new CountDownLatch(1);
225         AtomicLong transitionStartTime = new AtomicLong();
226         AtomicLong transitionEndTime = new AtomicLong();
227 
228         final ActivityOptions.OnAnimationStartedListener startedListener = transitionStartTime::set;
229         final ActivityOptions.OnAnimationFinishedListener finishedListener = (t) -> {
230             transitionEndTime.set(t);
231             latch.countDown();
232         };
233 
234         // Overriding task transit animation is disabled, so default wallpaper close animation
235         // is played.
236         final Bundle bundle = ActivityOptions.makeCustomAnimation(mContext,
237                 R.anim.alpha, 0 /* exitResId */, 0 /* backgroundColor */,
238                 new Handler(Looper.getMainLooper()), startedListener, finishedListener).toBundle();
239 
240         final ComponentName customWindowAnimationActivity = new ComponentName(
241                 mContext, CustomWindowAnimationActivity.class);
242         final Intent intent = new Intent().setComponent(customWindowAnimationActivity)
243                 .addFlags(FLAG_ACTIVITY_NEW_TASK);
244         mContext.startActivity(intent, bundle);
245         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
246         waitAndAssertTopResumedActivity(customWindowAnimationActivity, DEFAULT_DISPLAY,
247                 "Activity must be launched");
248 
249         latch.await(5, TimeUnit.SECONDS);
250         final long totalTime = transitionEndTime.get() - transitionStartTime.get();
251         assertTrue("Actual transition duration should be out of the range "
252                 + "<" + CUSTOM_ANIMATION_DURATION_RANGE.getLower() + ", "
253                 + CUSTOM_ANIMATION_DURATION_RANGE.getUpper() + "> ms, "
254                 + "actual=" + totalTime, !CUSTOM_ANIMATION_DURATION_RANGE.contains(totalTime));
255     }
256 
257     /**
258      * Checks that the activity's theme's background color is used as the default animation's
259      * background color when no override is specified.
260      */
261     @Test
testThemeBackgroundColorShowsDuringActivityTransition()262     public void testThemeBackgroundColorShowsDuringActivityTransition() {
263         final int backgroundColor = Color.WHITE;
264         final TestBounds testBounds = getTestBounds();
265 
266         getTestBuilder().setClass(TransitionActivityWithWhiteBackground.class)
267                 .setTestFunction(createAssertAppRegionOfScreenIsColor(backgroundColor, testBounds))
268                 .run();
269     }
270 
271     /**
272      * Checks that the background color set in the animation definition is used as the animation's
273      * background color instead of the theme's background color.
274      *
275      * @see R.anim.alpha_0_with_red_backdrop for animation defintition.
276      */
277     @Test
testAnimationBackgroundColorIsUsedDuringActivityTransition()278     public void testAnimationBackgroundColorIsUsedDuringActivityTransition() {
279         // TODO (b/319637823): Find proper fix for background overriding in ActivityTransitionTests
280         // This test expects the default task display area to show a red color during the
281         // transition. But since in auto split screen UI, the launcher activity is always
282         // shown, no screenshot of the test would return the red background color.
283         assumeFalse(hasAutomotiveSplitscreenMultitaskingFeature());
284         final int backgroundColor = Color.RED;
285         final ActivityOptions activityOptions = ActivityOptions.makeCustomAnimation(mContext,
286                 R.anim.alpha_0_with_red_backdrop, R.anim.alpha_0_with_red_backdrop);
287         final TestBounds testBounds = getTestBounds();
288 
289         getTestBuilder().setClass(TransitionActivityWithWhiteBackground.class)
290                 .setActivityOptions(activityOptions)
291                 .setTestFunction(createAssertAppRegionOfScreenIsColor(backgroundColor, testBounds))
292                 .run();
293     }
294 
295     /**
296      * Checks that we can override the default background color of the animation using the
297      * CustomAnimation activityOptions.
298      */
299     @Test
testCustomTransitionCanOverrideBackgroundColor()300     public void testCustomTransitionCanOverrideBackgroundColor() {
301         // TODO (b/319637823): Find proper fix for background overriding in ActivityTransitionTests
302         // This test expects the default task display area to show a green color during the
303         // transition. But since in auto split screen UI, the launcher activity is always
304         // shown, no screenshot of the test would return the green background color.
305         assumeFalse(hasAutomotiveSplitscreenMultitaskingFeature());
306         final int backgroundColor = Color.GREEN;
307         final ActivityOptions activityOptions = ActivityOptions.makeCustomAnimation(mContext,
308                 R.anim.alpha_0_with_backdrop, R.anim.alpha_0_with_backdrop, backgroundColor
309         );
310         final TestBounds testBounds = getTestBounds();
311 
312         getTestBuilder().setClass(TransitionActivityWithWhiteBackground.class)
313                 .setActivityOptions(activityOptions)
314                 .setTestFunction(createAssertAppRegionOfScreenIsColor(backgroundColor, testBounds))
315                 .run();
316     }
317 
318     /**
319      * Checks that we can override the default background color of the animation through
320      * overridePendingTransition.
321      */
322     @Test
testPendingTransitionCanOverrideBackgroundColor()323     public void testPendingTransitionCanOverrideBackgroundColor() {
324         // TODO (b/319637823): Find proper fix for background overriding in ActivityTransitionTests
325         // This test expects the default task display area to show a green color during the
326         // transition. But since in auto split screen UI, the launcher activity is always
327         // shown, no screenshot of the test would return the green background color.
328         assumeFalse(hasAutomotiveSplitscreenMultitaskingFeature());
329         final int backgroundColor = Color.GREEN;
330 
331         final Bundle extras = new Bundle();
332         extras.putInt(ENTER_ANIM_KEY, R.anim.alpha_0_with_backdrop);
333         extras.putInt(EXIT_ANIM_KEY, R.anim.alpha_0_with_backdrop);
334         extras.putInt(BACKGROUND_COLOR_KEY, backgroundColor);
335         addTestMethodToExtras(TEST_METHOD_OVERRIDE_PENDING_TRANSITION, 0, extras);
336         final TestBounds testBounds = getTestBounds();
337 
338         getTestBuilder().setClass(CustomBackgroundTransitionActivity.class).setExtras(extras)
339                 .setTestFunction(createAssertAppRegionOfScreenIsColor(backgroundColor, testBounds))
340                 .run();
341     }
342 
343     @Test
testSetTransitionCanOverrideBackgroundColor()344     public void testSetTransitionCanOverrideBackgroundColor() {
345         // TODO (b/319637823): Find proper fix for background overriding in ActivityTransitionTests
346         // This test expects the default task display area to show a green color during the
347         // transition. But since in auto split screen UI, the launcher activity is always
348         // shown, no screenshot of the test would return the green background color.
349         assumeFalse(hasAutomotiveSplitscreenMultitaskingFeature());
350         final int backgroundColor = Color.GREEN;
351 
352         final Bundle extras = new Bundle();
353         extras.putInt(ENTER_ANIM_KEY, R.anim.alpha_0_with_backdrop);
354         extras.putInt(EXIT_ANIM_KEY, R.anim.alpha_0_with_backdrop);
355         extras.putInt(BACKGROUND_COLOR_KEY, backgroundColor);
356         addTestMethodToExtras(TEST_METHOD_OVERRIDE_ACTIVITY_TRANSITION,
357                 TRANSITION_TYPE_OPEN | TRANSITION_TYPE_CLOSE, extras);
358         final TestBounds testBounds = getTestBounds();
359 
360         getTestBuilder().setClass(CustomBackgroundTransitionActivity.class).setExtras(extras)
361                 .setTestFunction(createAssertAppRegionOfScreenIsColor(backgroundColor, testBounds))
362                 .run();
363 
364         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
365         mContext.sendBroadcast(new Intent(ACTION_FINISH));
366         runAndAssertActivityTransition(
367                 createAssertAppRegionOfScreenIsColor(backgroundColor, testBounds));
368     }
369     /**
370      * Checks that when an activity transition with a left edge extension is run that the animating
371      * activity is extended on the left side by clamping the edge pixels of the activity.
372      *
373      * The test runs an activity transition where the animating activities are X scaled to 50%,
374      * positioned of the right side of the screen, and edge extended on the left. Because the
375      * animating activities are half red half blue (split at the middle of the X axis of the
376      * activity). We expect first 75% pixel columns of the screen to be red (50% from the edge
377      * extension and the next 25% from from the activity) and the remaining 25% columns after that
378      * to be blue (from the activity).
379      *
380      * @see R.anim.edge_extension_left for the transition applied.
381      */
382     @Test
testLeftEdgeExtensionWorksDuringActivityTransition()383     public void testLeftEdgeExtensionWorksDuringActivityTransition() {
384         final Bundle extras = new Bundle();
385         extras.putInt(DIRECTION_KEY, LEFT);
386         addTestMethodToExtras(TEST_METHOD_OVERRIDE_PENDING_TRANSITION, 0, extras);
387         final TestBounds testBounds = getTestBounds();
388         final Rect transitionBounds = testBounds.transitionBounds;
389         final int xIndex = transitionBounds.left
390                 + (transitionBounds.right - transitionBounds.left) * 3 / 4;
391         getTestBuilder().setClass(EdgeExtensionActivity.class).setExtras(extras)
392                 .setTestFunction(createAssertColorChangeXIndex(xIndex, testBounds))
393                 .run();
394     }
395 
396     /**
397      * Checks that when an activity transition with a top edge extension is run that the animating
398      * activity is extended on the left side by clamping the edge pixels of the activity.
399      *
400      * The test runs an activity transition where the animating activities are Y scaled to 50%,
401      * positioned of the bottom of the screen, and edge extended on the top. Because the
402      * animating activities are half red half blue (split at the middle of the X axis of the
403      * activity). We expect first 50% pixel columns of the screen to be red (the top half from the
404      * extension and the bottom half from the activity) and the remaining 50% columns after that
405      * to be blue (the top half from the extension and the bottom half from the activity).
406      *
407      * @see R.anim.edge_extension_top for the transition applied.
408      */
409     @Test
testTopEdgeExtensionWorksDuringActivityTransition()410     public void testTopEdgeExtensionWorksDuringActivityTransition() {
411         final Bundle extras = new Bundle();
412         extras.putInt(DIRECTION_KEY, TOP);
413         addTestMethodToExtras(TEST_METHOD_OVERRIDE_PENDING_TRANSITION, 0, extras);
414         final TestBounds testBounds = getTestBounds();
415         final Rect transitionBounds = testBounds.transitionBounds;
416         final int xIndex = (transitionBounds.left + transitionBounds.right) / 2;
417         getTestBuilder().setClass(EdgeExtensionActivity.class).setExtras(extras)
418                 .setTestFunction(createAssertColorChangeXIndex(xIndex, testBounds))
419                 .run();
420     }
421 
422     /**
423      * Checks that when an activity transition with a right edge extension is run that the animating
424      * activity is extended on the right side by clamping the edge pixels of the activity.
425      *
426      * The test runs an activity transition where the animating activities are X scaled to 50% and
427      * edge extended on the right. Because the animating activities are half red half blue. We
428      * expect first 25% pixel columns of the screen to be red (from the activity) and the remaining
429      * 75% columns after that to be blue (25% from the activity and 50% from the edge extension
430      * which should be extending the right edge pixel (so red pixels).
431      *
432      * @see R.anim.edge_extension_right for the transition applied.
433      */
434     @Test
testRightEdgeExtensionWorksDuringActivityTransition()435     public void testRightEdgeExtensionWorksDuringActivityTransition() {
436         final Bundle extras = new Bundle();
437         extras.putInt(DIRECTION_KEY, RIGHT);
438         addTestMethodToExtras(TEST_METHOD_OVERRIDE_PENDING_TRANSITION, 0, extras);
439         final TestBounds testBounds = getTestBounds();
440         final Rect transitionBounds = testBounds.transitionBounds;
441         final int xIndex = transitionBounds.left
442                 + (transitionBounds.right - transitionBounds.left) / 4;
443         getTestBuilder().setClass(EdgeExtensionActivity.class).setExtras(extras)
444                 .setTestFunction(createAssertColorChangeXIndex(xIndex, testBounds))
445                 .run();
446     }
447 
448     /**
449      * Borrow the test from testRightEdgeExtensionWorksDuringActivityTransition, mainly test for
450      * API Activity#overrideActivityTransition.
451      */
452     @Test
testOverrideActivityTransition()453     public void testOverrideActivityTransition() {
454         final Bundle extras = new Bundle();
455         extras.putInt(DIRECTION_KEY, RIGHT);
456         addTestMethodToExtras(TEST_METHOD_OVERRIDE_ACTIVITY_TRANSITION,
457                 TRANSITION_TYPE_OPEN | TRANSITION_TYPE_CLOSE, extras);
458         final TestBounds testBounds = getTestBounds();
459         final Rect transitionBounds = testBounds.transitionBounds;
460         final int xIndex = transitionBounds.left
461                 + (transitionBounds.right - transitionBounds.left) / 4;
462         getTestBuilder().setClass(EdgeExtensionActivity.class).setExtras(extras)
463                 .setTestFunction(createAssertColorChangeXIndex(xIndex, testBounds))
464                 .run();
465 
466         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
467         mContext.sendBroadcast(new Intent(ACTION_FINISH));
468         runAndAssertActivityTransition(createAssertColorChangeXIndex(xIndex, testBounds));
469     }
470 
471     /**
472      * Borrow the test from testRightEdgeExtensionWorksDuringActivityTransition, mainly test for
473      * API Activity#clearOverrideActivityTransition.
474      */
475     @Test
testClearOverrideActivityTransition()476     public void testClearOverrideActivityTransition() {
477         final Bundle extras = new Bundle();
478         extras.putInt(DIRECTION_KEY, RIGHT);
479         addTestMethodToExtras(TEST_METHOD_OVERRIDE_ACTIVITY_TRANSITION,
480                 TRANSITION_TYPE_OPEN | TRANSITION_TYPE_CLOSE, extras);
481         final TestBounds testBounds = getTestBounds();
482         final LauncherActivity launcherActivity = startLauncherActivity();
483         launcherActivity.startActivity(null, EdgeExtensionActivity.class, extras);
484 
485         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
486         final Intent update = new Intent(ACTION_UPDATE);
487         update.putExtra(TEST_METHOD_KEY, TEST_METHOD_CLEAR_OVERRIDE_ACTIVITY_TRANSITION);
488         update.putExtra(TRANSITION_TYPE_KEY, TRANSITION_TYPE_OPEN | TRANSITION_TYPE_CLOSE);
489         mContext.sendBroadcast(update);
490         mContext.sendBroadcast(new Intent(ACTION_FINISH));
491         runAndAssertActivityTransition(
492                 createAssertAppRegionOfScreenIsColor(Color.CYAN, testBounds));
493     }
494 
495     /**
496      * Checks that when an activity transition with a bottom edge extension is run that the
497      * animating activity is extended on the bottom side by clamping the edge pixels of the
498      * activity.
499      *
500      * The test runs an activity transition where the animating activities are Y scaled to 50%,
501      * positioned of the top of the screen, and edge extended on the bottom. Because the
502      * animating activities are half red half blue (split at the middle of the X axis of the
503      * activity). We expect first 50% pixel columns of the screen to be red (the top half from the
504      * activity and the bottom half from gthe extensions) and the remaining 50% columns after that
505      * to be blue (the top half from the activity and the bottom half from the extension).
506      *
507      * @see R.anim.edge_extension_bottom for the transition applied.
508      */
509     @Test
testBottomEdgeExtensionWorksDuringActivityTransition()510     public void testBottomEdgeExtensionWorksDuringActivityTransition() {
511         final Bundle extras = new Bundle();
512         extras.putInt(DIRECTION_KEY, BOTTOM);
513         addTestMethodToExtras(TEST_METHOD_OVERRIDE_PENDING_TRANSITION, 0, extras);
514         final TestBounds testBounds = getTestBounds();
515         final Rect transitionBounds = testBounds.transitionBounds;
516         final int xIndex = (transitionBounds.left + transitionBounds.right) / 2;
517         getTestBuilder().setClass(EdgeExtensionActivity.class).setExtras(extras)
518                 .setTestFunction(createAssertColorChangeXIndex(xIndex, testBounds))
519                 .run();
520     }
521 
getTestBuilder()522     private TestBuilder getTestBuilder() {
523         return new TestBuilder();
524     }
525 
526     private class TestBuilder {
527         private ActivityOptions mActivityOptions = ActivityOptions.makeBasic();
528         private Bundle mExtras = Bundle.EMPTY;
529         private Class<?> mKlass;
530         private Function<Bitmap, AssertionResult> mTestFunction;
531 
setActivityOptions(ActivityOptions activityOptions)532         public TestBuilder setActivityOptions(ActivityOptions activityOptions) {
533             this.mActivityOptions = activityOptions;
534             return this;
535         }
536 
setExtras(Bundle extra)537         public TestBuilder setExtras(Bundle extra) {
538             this.mExtras = extra;
539             return this;
540         }
541 
setClass(Class<?> klass)542         public TestBuilder setClass(Class<?> klass) {
543             this.mKlass = klass;
544             return this;
545         }
546 
setTestFunction(Function<Bitmap, AssertionResult> testFunction)547         public TestBuilder setTestFunction(Function<Bitmap, AssertionResult> testFunction) {
548             this.mTestFunction = testFunction;
549             return this;
550         }
551 
run()552         public void run() {
553             final LauncherActivity launcherActivity = startLauncherActivity();
554             launcherActivity.startActivity(mActivityOptions, mKlass, mExtras);
555             runAndAssertActivityTransition(mTestFunction);
556         }
557     }
558 
559     private static class TestBounds {
560         // The region which transition will apply.
561         public Rect transitionBounds;
562         // The region which should be stable for verify.
563         public Rect testableBounds;
564     }
565 
getTestBounds()566     private TestBounds getTestBounds() {
567         final LauncherActivity activity = startLauncherActivity();
568         final TestBounds bounds = new TestBounds();
569         bounds.transitionBounds = getTransitionAppBounds();
570         bounds.testableBounds = activity.getActivityTestableRegion();
571         launchHomeActivityNoWait();
572         removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
573         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
574         return bounds;
575     }
576 
runAndAssertActivityTransition(Function<Bitmap, AssertionResult> assertFunction)577     private void runAndAssertActivityTransition(Function<Bitmap, AssertionResult> assertFunction) {
578         // Busy wait until we are running the transition to capture the screenshot
579         // Set a limited time to wait for transition start since there can still miss the state.
580         assertTrue(Condition.waitFor(new Condition<>("Wait for transition running", () -> {
581             mWmState.computeState();
582             return WindowManagerState.APP_STATE_RUNNING.equals(
583                     mWmState.getDisplay(DEFAULT_DISPLAY).getAppTransitionState());
584         }).setRetryIntervalMs(15).setRetryLimit(200)));
585 
586         // Because of differences in timing between devices we try the given assert function
587         // by taking multiple screenshots approximately to ensure we capture at least one screenshot
588         // around the beginning of the activity transition.
589         // The Timing issue exists around the beginning, so we use a sleep duration that increases
590         // exponentially. The total amount of sleep duration is between 5 and 10 seconds, which
591         // matches the most common wait time in CTS (2^0 + 2^1 + ... + 2^13 = about 8000).
592         final ArrayList<AssertionResult> failedResults = new ArrayList<>();
593         int sleepDurationMilliseconds = 1;
594         for (int i = 0; i < 13; i++) {
595             final AssertionResult result = assertFunction.apply(
596                     mInstrumentation.getUiAutomation().takeScreenshot());
597             if (!result.isFailure) {
598                 return;
599             }
600             failedResults.add(result);
601             SystemClock.sleep(sleepDurationMilliseconds);
602             sleepDurationMilliseconds *= 2;
603         }
604 
605         fail("No screenshot of the activity transition passed the assertions ::\n"
606                 + String.join(",\n", failedResults.stream().map(Object::toString)
607                 .toArray(String[]::new)));
608 
609     }
610 
createAssertAppRegionOfScreenIsColor(int color, TestBounds testBounds)611     private Function<Bitmap, AssertionResult> createAssertAppRegionOfScreenIsColor(int color,
612             TestBounds testBounds) {
613         return (screen) -> getIsAppRegionOfScreenOfColorResult(screen, color, testBounds);
614     }
615 
616     private static class ColorCheckResult extends AssertionResult {
617         public final Point firstWrongPixel;
618         public final Color expectedColor;
619         public final Color actualColor;
620 
ColorCheckResult(boolean isFailure, Point firstWrongPixel, Color expectedColor, Color actualColor)621         private ColorCheckResult(boolean isFailure, Point firstWrongPixel, Color expectedColor,
622                 Color actualColor) {
623             super(isFailure);
624             this.firstWrongPixel = firstWrongPixel;
625             this.expectedColor = expectedColor;
626             this.actualColor = actualColor;
627         }
628 
ColorCheckResult(Point firstWrongPixel, Color expectedColor, Color actualColor)629         private ColorCheckResult(Point firstWrongPixel, Color expectedColor, Color actualColor) {
630             this(true, firstWrongPixel, expectedColor, actualColor);
631         }
632 
633         @Override
toString()634         public String toString() {
635             return "ColorCheckResult{"
636                     + "isFailure=" + isFailure
637                     + ", firstWrongPixel=" + firstWrongPixel
638                     + ", expectedColor=" + expectedColor
639                     + ", actualColor=" + actualColor
640                     + '}';
641         }
642     }
643 
getIsAppRegionOfScreenOfColorResult(Bitmap screen, int color, TestBounds testBounds)644     private AssertionResult getIsAppRegionOfScreenOfColorResult(Bitmap screen, int color,
645             TestBounds testBounds) {
646         final int scaleWidth = testBounds.testableBounds.width() / 5;
647         final int[] xSample = {
648                 (scaleWidth + testBounds.testableBounds.left),
649                 (scaleWidth * 2 + testBounds.testableBounds.left),
650                 (scaleWidth * 3 + testBounds.testableBounds.left),
651                 (scaleWidth * 4 + testBounds.testableBounds.left)};
652         final int scaleHeight = testBounds.testableBounds.height() / 5;
653         final int[] ySample = {
654                 (scaleHeight + testBounds.testableBounds.top),
655                 (scaleHeight * 2 + testBounds.testableBounds.top),
656                 (scaleHeight * 3 + testBounds.testableBounds.top),
657                 (scaleHeight * 4 + testBounds.testableBounds.top)};
658         final Color verifyColor = Color.valueOf(color);
659         for (int x = xSample.length - 1; x >= 0; --x) {
660             final int sampleX = xSample[x];
661             for (int y = ySample.length - 1; y >= 0; --y) {
662                 final int sampleY = ySample[y];
663                 final Color rawColor = screen.getColor(sampleX, sampleY);
664                 final Color sRgbColor;
665                 if (!rawColor.getColorSpace().equals(ColorSpace.get(ColorSpace.Named.SRGB))) {
666                     // Conversion is required because the color space of the screenshot may be in
667                     // the DCI-P3 color space or some other color space and we want to compare the
668                     // color against once in the SRGB color space, so we must convert the color back
669                     // to the SRGB color space.
670                     sRgbColor = screen.getColor(sampleX, sampleY)
671                             .convert(ColorSpace.get(ColorSpace.Named.SRGB));
672                 } else {
673                     sRgbColor = rawColor;
674                 }
675                 if (arrayEquals(new float[]{
676                                 verifyColor.red(), verifyColor.green(), verifyColor.blue()},
677                         new float[]{sRgbColor.red(), sRgbColor.green(), sRgbColor.blue()})) {
678                     return new ColorCheckResult(new Point(sampleX, sampleY), verifyColor,
679                             sRgbColor);
680                 }
681             }
682         }
683 
684         return AssertionResult.SUCCESS;
685     }
686 
arrayEquals(float[] array1, float[] array2)687     private boolean arrayEquals(float[] array1, float[] array2) {
688         return arrayEquals(array1, array2, COLOR_VALUE_VARIANCE_TOLERANCE);
689     }
690 
arrayEquals(float[] array1, float[] array2, float varianceTolerance)691     private boolean arrayEquals(float[] array1, float[] array2, float varianceTolerance) {
692         if (array1.length != array2.length) {
693             return true;
694         }
695         for (int i = 0; i < array1.length; i++) {
696             if (Math.abs(array1[i] - array2[i]) > varianceTolerance) {
697                 return true;
698             }
699         }
700         return false;
701     }
702 
getTransitionAppBounds()703     private Rect getTransitionAppBounds() {
704         getWmState().computeState();
705         final WindowManagerState.Activity activity = getWmState().getActivity(
706                 ComponentName.unflattenFromString(getWmState().getFocusedActivity()));
707         return activity.getBounds();
708     }
709 
710     private static class AssertionResult {
711         public final boolean isFailure;
712         public final String message;
713 
AssertionResult(boolean isFailure, String message)714         private AssertionResult(boolean isFailure, String message) {
715             this.isFailure = isFailure;
716             this.message = message;
717         }
718 
AssertionResult(boolean isFailure)719         private AssertionResult(boolean isFailure) {
720             this(isFailure, null);
721         }
722 
723         @Override
toString()724         public String toString() {
725             return "AssertionResult{"
726                     + "isFailure=" + isFailure
727                     + ", message='" + message + '\''
728                     + '}';
729         }
730 
731         private static final AssertionResult SUCCESS = new AssertionResult(false);
732         private static final AssertionResult FAILURE = new AssertionResult(true);
733     }
734 
735     // The activity we are extending is a half red, half blue.
736     // We are scaling the activity in the animation so if the extension doesn't work we should
737     // have a blue, then red, then black section, and if it does work we should see on a blue,
738     // followed by an extended red section.
createAssertColorChangeXIndex(int xIndex, TestBounds testBounds)739     private Function<Bitmap, AssertionResult> createAssertColorChangeXIndex(int xIndex,
740                                                                             TestBounds testBounds) {
741         return (screen) -> assertColorChangeXIndex(
742                 screen, xIndex, testBounds, Color.BLUE, Color.RED);
743     }
744 
assertColorChangeXIndex(Bitmap screen, int splitX, TestBounds testBounds, int lessXColor, int largeXColor)745     private AssertionResult assertColorChangeXIndex(Bitmap screen, int splitX,
746             TestBounds testBounds, int lessXColor, int largeXColor) {
747         final int[] xSample = {
748                 (splitX - testBounds.testableBounds.left) / 2 + testBounds.testableBounds.left,
749                 splitX - 3,
750                 splitX + 3,
751                 (testBounds.testableBounds.right - splitX) / 2 + splitX};
752         final int scaleHeight = testBounds.testableBounds.height() / 5;
753         final int[] ySample = {
754                 (scaleHeight + testBounds.testableBounds.top),
755                 (scaleHeight * 2 + testBounds.testableBounds.top),
756                 (scaleHeight * 3 + testBounds.testableBounds.top),
757                 (scaleHeight * 4 + testBounds.testableBounds.top)};
758         final Color lessXColorC = Color.valueOf(lessXColor);
759         final Color largeXColorC = Color.valueOf(largeXColor);
760         for (int xIndex = xSample.length - 1; xIndex >= 0; --xIndex) {
761             final int sampleX = xSample[xIndex];
762             for (int yIndex = ySample.length - 1; yIndex >= 0; --yIndex) {
763                 final int sampleY = ySample[yIndex];
764                 final Color expectedColor;
765                 if (sampleX < splitX) {
766                     expectedColor = lessXColorC;
767                 } else {
768                     expectedColor = largeXColorC;
769                 }
770                 final Color rawColor = screen.getColor(sampleX, sampleY);
771                 final Color sRgbColor;
772                 if (!rawColor.getColorSpace().equals(ColorSpace.get(ColorSpace.Named.SRGB))) {
773                     // Conversion is required because the color space of the screenshot may be in
774                     // the DCI-P3 color space or some other color space and we want to compare the
775                     // color against once in the SRGB color space, so we must convert the color back
776                     // to the SRGB color space.
777                     sRgbColor = screen.getColor(sampleX, sampleY)
778                             .convert(ColorSpace.get(ColorSpace.Named.SRGB));
779                 } else {
780                     sRgbColor = rawColor;
781                 }
782                 if (arrayEquals(new float[]{
783                                 expectedColor.red(), expectedColor.green(), expectedColor.blue()},
784                         new float[]{sRgbColor.red(), sRgbColor.green(), sRgbColor.blue()})) {
785                     return new ColorCheckResult(new Point(sampleX, sampleY), expectedColor,
786                             sRgbColor);
787                 }
788             }
789         }
790 
791         return AssertionResult.SUCCESS;
792     }
793 
addTestMethodToExtras(int apiType, int transitionType, Bundle extras)794     private static void addTestMethodToExtras(int apiType, int transitionType, Bundle extras) {
795         extras.putInt(TEST_METHOD_KEY, apiType);
796         extras.putInt(TRANSITION_TYPE_KEY, transitionType);
797     }
798 
799     public static class LauncherActivity extends Activity {
800 
801         private WindowInsets mInsets;
802 
803         @Override
onCreate(@ullable Bundle savedInstanceState)804         protected void onCreate(@Nullable Bundle savedInstanceState) {
805             super.onCreate(savedInstanceState);
806 
807             getWindow().getAttributes().layoutInDisplayCutoutMode =
808                     LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
809             // Ensure the activity is edge-to-edge
810             // In tests we rely on the activity's content filling the entire window
811             getWindow().setDecorFitsSystemWindows(false);
812 
813             View view = new View(this);
814             view.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
815             view.setOnApplyWindowInsetsListener((v, insets) -> mInsets = insets);
816             view.setBackgroundColor(Color.CYAN);
817             setContentView(view);
818         }
819 
getActivityTestableRegion()820         private Rect getActivityTestableRegion() {
821             final Rect activityBounds = getWindowManager().getCurrentWindowMetrics().getBounds();
822             final Insets insets = mInsets.getInsets(WindowInsets.Type.systemBars()
823                     | WindowInsets.Type.displayCutout());
824             activityBounds.inset(insets);
825             cropRoundedCornersRegions(activityBounds);
826             return new Rect(activityBounds);
827         }
828 
cropRoundedCornersRegions(Rect activityBounds)829         private void cropRoundedCornersRegions(Rect activityBounds) {
830             RoundedCorner topLeftCorner = mInsets.getRoundedCorner(POSITION_TOP_LEFT);
831             RoundedCorner bottomRightCorner = mInsets.getRoundedCorner(POSITION_BOTTOM_RIGHT);
832 
833             final Rect innerRectangle = new Rect(activityBounds);
834             // There will be no testable region if the radius of rounded corner equals to the
835             // center of screen on a circle shape display. So instead of ignore rounded corner
836             // areas, consider the internal rectangle of rounded corner region as testable region.
837             // Where radius * cosine(45) == the projected length to x and y direction.
838             final double projectionConst = Math.cos(45);
839             if (topLeftCorner != null) {
840                 final Point center = topLeftCorner.getCenter();
841                 final int radius = topLeftCorner.getRadius();
842                 final double projectLength = Math.ceil(radius * projectionConst);
843                 innerRectangle.left = center.x - (int) projectLength;
844                 innerRectangle.top = center.y - (int) projectLength;
845             }
846             if (bottomRightCorner != null) {
847                 final Point center = bottomRightCorner.getCenter();
848                 final int radius = bottomRightCorner.getRadius();
849                 final double projectLength = Math.ceil(radius * projectionConst);
850                 innerRectangle.right = center.x + (int) projectLength;
851                 innerRectangle.bottom = center.y + (int) projectLength;
852             }
853             activityBounds.setIntersect(activityBounds, innerRectangle);
854         }
855 
startActivity(ActivityOptions activityOptions, Class<?> klass)856         public void startActivity(ActivityOptions activityOptions, Class<?> klass) {
857             startActivity(activityOptions, klass, new Bundle());
858         }
859 
startActivity(ActivityOptions activityOptions, Class<?> klass, Bundle extras)860         public void startActivity(ActivityOptions activityOptions, Class<?> klass,
861                 Bundle extras) {
862             final Intent i = new Intent(this, klass);
863             i.putExtras(extras);
864             startActivity(i, activityOptions != null ? activityOptions.toBundle() : null);
865         }
866     }
867 
868     public static class TransitionActivity extends Activity { }
869 
870     public static class CustomBackgroundTransitionActivity extends Activity {
871         static final String ENTER_ANIM_KEY = "enterAnim";
872         static final String EXIT_ANIM_KEY = "enterAnim";
873         static final String BACKGROUND_COLOR_KEY = "backgroundColor";
874 
875         private boolean mPendingOverrideTransition;
876         private int mPendingEnterRes;
877         private int mPendingExitRes;
878         private int mBackgroundColor;
879 
880         @Override
onCreate(@ullable Bundle savedInstanceState)881         protected void onCreate(@Nullable Bundle savedInstanceState) {
882             super.onCreate(savedInstanceState);
883             registerReceiver(mReceiver, new IntentFilter(ACTION_FINISH), Context.RECEIVER_EXPORTED);
884             processIntent();
885         }
886         private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
887             @Override
888             public void onReceive(Context context, Intent intent) {
889                 if (ACTION_FINISH.equals(intent.getAction())) {
890                     finish();
891                 }
892             }
893         };
894 
processIntent()895         private void processIntent() {
896             Bundle extras = getIntent().getExtras();
897             int testAPI = extras.getInt(TEST_METHOD_KEY);
898             int enterAnim = extras.getInt(ENTER_ANIM_KEY);
899             int exitAnim = extras.getInt(EXIT_ANIM_KEY);
900             int backgroundColor = extras.getInt(BACKGROUND_COLOR_KEY);
901             int transitionType = extras.getInt(TRANSITION_TYPE_KEY);
902             if (testAPI == TEST_METHOD_OVERRIDE_PENDING_TRANSITION) {
903                 mPendingOverrideTransition = true;
904                 mPendingEnterRes = enterAnim;
905                 mPendingExitRes = exitAnim;
906                 mBackgroundColor = backgroundColor;
907             } else if (testAPI == TEST_METHOD_OVERRIDE_ACTIVITY_TRANSITION) {
908                 if ((transitionType & TRANSITION_TYPE_OPEN) != 0) {
909                     if (backgroundColor != 0) {
910                         overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, enterAnim, exitAnim,
911                                 backgroundColor /* backgroundColor */);
912                     } else {
913                         overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, enterAnim, exitAnim);
914                     }
915                 }
916                 if ((transitionType & TRANSITION_TYPE_CLOSE) != 0) {
917                     if (backgroundColor != 0) {
918                         overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, enterAnim, exitAnim,
919                                 backgroundColor /* backgroundColor */);
920                     } else {
921                         overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, enterAnim, exitAnim);
922                     }
923                 }
924             }
925         }
926 
927         @Override
onResume()928         protected void onResume() {
929             super.onResume();
930 
931             if (mPendingOverrideTransition) {
932                 overridePendingTransition(mPendingEnterRes, mPendingExitRes, mBackgroundColor);
933                 mPendingOverrideTransition = false;
934             }
935         }
936 
937         @Override
onDestroy()938         protected void onDestroy() {
939             super.onDestroy();
940             unregisterReceiver(mReceiver);
941         }
942     }
943 
944     public static class TransitionActivityWithWhiteBackground extends Activity { }
945 
946     public static class EdgeExtensionActivity extends Activity {
947         static final String DIRECTION_KEY = "direction";
948         static final int LEFT = 0;
949         static final int TOP = 1;
950         static final int RIGHT = 2;
951         static final int BOTTOM = 3;
952 
953         private boolean mPendingOverrideTransition;
954         private int mPendingEnterRes;
955         private int mPendingExitRes;
956 
957         @Override
onCreate(@ullable Bundle savedInstanceState)958         protected void onCreate(@Nullable Bundle savedInstanceState) {
959             super.onCreate(savedInstanceState);
960             setContentView(R.layout.vertical_color_split);
961 
962             getWindow().getAttributes().layoutInDisplayCutoutMode =
963                     LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
964             // Ensure the activity is edge-to-edge
965             // In tests we rely on the activity's content filling the entire window
966             getWindow().setDecorFitsSystemWindows(false);
967 
968             // Hide anything that the decor view might add to the window to avoid extending that
969             getWindow().getInsetsController()
970                     .hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
971             final IntentFilter intentFilter = new IntentFilter();
972             intentFilter.addAction(ACTION_FINISH);
973             intentFilter.addAction(ACTION_UPDATE);
974             registerReceiver(mReceiver, intentFilter, Context.RECEIVER_EXPORTED);
975             processIntent();
976         }
977 
978         private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
979             @Override
980             public void onReceive(Context context, Intent intent) {
981                 if (ACTION_UPDATE.equals(intent.getAction())) {
982                     final int clearApi = intent.getIntExtra(TEST_METHOD_KEY, 0);
983                     if (clearApi == TEST_METHOD_CLEAR_OVERRIDE_ACTIVITY_TRANSITION) {
984                         final int clearType = intent.getIntExtra(TRANSITION_TYPE_KEY, 0);
985                         if ((clearType & TRANSITION_TYPE_OPEN) != 0) {
986                             clearOverrideActivityTransition(OVERRIDE_TRANSITION_OPEN);
987                         }
988                         if ((clearType & TRANSITION_TYPE_CLOSE) != 0) {
989                             clearOverrideActivityTransition(OVERRIDE_TRANSITION_CLOSE);
990                         }
991                     }
992                 }
993                 if (ACTION_FINISH.equals(intent.getAction())) {
994                     finish();
995                 }
996             }
997         };
998 
999         @Override
onDestroy()1000         protected void onDestroy() {
1001             super.onDestroy();
1002             unregisterReceiver(mReceiver);
1003         }
1004 
1005         @Override
onResume()1006         protected void onResume() {
1007             super.onResume();
1008 
1009             if (mPendingOverrideTransition) {
1010                 overridePendingTransition(mPendingEnterRes, mPendingExitRes);
1011                 mPendingOverrideTransition = false;
1012             }
1013         }
1014 
processIntent()1015         private void processIntent() {
1016             Bundle extras = getIntent().getExtras();
1017             int direction = extras.getInt(DIRECTION_KEY);
1018             int testAPI = extras.getInt(TEST_METHOD_KEY);
1019             int transitionType = extras.getInt(TRANSITION_TYPE_KEY);
1020             int testAnim = 0;
1021             switch (direction) {
1022                 case LEFT:
1023                     testAnim = R.anim.edge_extension_left;
1024                     break;
1025                 case TOP:
1026                     testAnim = R.anim.edge_extension_top;
1027                     break;
1028                 case RIGHT:
1029                     testAnim = R.anim.edge_extension_right;
1030                     break;
1031                 case BOTTOM:
1032                     testAnim = R.anim.edge_extension_bottom;
1033                     break;
1034             }
1035             if (testAPI == TEST_METHOD_OVERRIDE_PENDING_TRANSITION) {
1036                 mPendingOverrideTransition = true;
1037                 mPendingEnterRes = testAnim;
1038                 mPendingExitRes = R.anim.alpha_0;
1039             } else if (testAPI == TEST_METHOD_OVERRIDE_ACTIVITY_TRANSITION) {
1040                 if ((transitionType & TRANSITION_TYPE_OPEN) != 0) {
1041                     overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, testAnim, R.anim.alpha_0,
1042                             0 /* backgroundColor */);
1043                 }
1044                 if ((transitionType & TRANSITION_TYPE_CLOSE) != 0) {
1045                     overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, R.anim.alpha_0, testAnim,
1046                             0 /* backgroundColor */);
1047                 }
1048             }
1049         }
1050     }
1051 
1052     public static class CustomWindowAnimationActivity extends Activity { }
1053 }
1054