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 android.server.wm.animations;
18 
19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.graphics.Color;
26 import android.os.Bundle;
27 import android.provider.Settings;
28 import android.server.wm.Condition;
29 import android.server.wm.WindowManagerState;
30 import android.server.wm.WindowManagerTestBase;
31 import android.server.wm.settings.SettingsSession;
32 import android.view.Display;
33 import android.view.Gravity;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.view.Window;
37 import android.view.WindowManager;
38 
39 import org.junit.ClassRule;
40 import org.junit.Test;
41 import org.junit.rules.TestRule;
42 
43 /**
44  * Test the following APIs for controlling FLAG_NO_MOVE_ANIMATION:
45  * <ul>
46  * <li>{@code WindowManager.LayoutParams.setCanPlayMoveAnimation(boolean)}
47  * <li>{@code WindowManager.LayoutParams.canPlayMoveAnimation()}
48  * <li>{@code android:style/windowNoMoveAnimation}
49  * </ul>
50  *
51  * Build/Install/Run:
52  *     atest CtsWindowManagerDeviceAnimations:MoveAnimationTests
53  */
54 public class MoveAnimationTests extends WindowManagerTestBase {
55 
56     /**
57      * All tests in this class run with window animation scaling set to 20.0f
58      */
59     @ClassRule
60     public static final TestRule sWindowAnimationRule = SettingsSession.overrideForTest(
61             Settings.Global.getUriFor(Settings.Global.WINDOW_ANIMATION_SCALE),
62             Settings.Global::getFloat,
63             Settings.Global::putFloat,
64             20.0f);
65 
66     /**
67      * Activity with a theme setting {@code windowIsFloating} as {@code true} to get the default
68      * behavior of floating window move animations.
69      */
70     public static class FloatingActivity extends FocusableActivity {
71 
72         protected View mContentView;
73 
74         /**
75          * Instance of {@link FloatingActivity} with a theme setting {@code windowNoMoveAnimation}
76          * as {@code true}.
77          */
78         public static class NoMove extends FloatingActivity {
79             @Override
onCreate(Bundle savedInstanceState)80             protected void onCreate(Bundle savedInstanceState) {
81                 super.onCreate(savedInstanceState);
82             }
83         }
84 
85         @Override
onCreate(Bundle savedInstanceState)86         protected void onCreate(Bundle savedInstanceState) {
87             super.onCreate(savedInstanceState);
88 
89             mContentView = new View(this);
90             mContentView.setBackgroundColor(Color.BLUE);
91 
92             setContentSquare(40);
93         }
94 
setContentSquare(int size)95         public void setContentSquare(int size) {
96             getWindow()
97                     .setLayout(
98                             ViewGroup.LayoutParams.WRAP_CONTENT,
99                             ViewGroup.LayoutParams.WRAP_CONTENT);
100             getWindow().setContentView(mContentView, new ViewGroup.LayoutParams(size, size));
101 
102             WindowManager.LayoutParams attrs = getWindow().getAttributes();
103             attrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
104             getWindow().setAttributes(attrs);
105         }
106     }
107 
108     @Test
testCanPlayMoveAnimationByDefault()109     public void testCanPlayMoveAnimationByDefault() {
110         final FloatingActivity activity =
111                 startActivityInWindowingModeFullScreen(FloatingActivity.class);
112         final Window window = activity.getWindow();
113 
114         // Default state should be TRUE because the activity has done nothing to override it.
115         assertTrue("Floating windows should play move animations by default",
116                 window.getAttributes().canPlayMoveAnimation());
117         assertPlaysMoveAnimation(activity, true);
118     }
119 
120     @Test
testCanOverrideTheme()121     public void testCanOverrideTheme() {
122         final FloatingActivity.NoMove activity =
123                 startActivityInWindowingModeFullScreen(FloatingActivity.NoMove.class);
124         final Window window = activity.getWindow();
125 
126         // Default state should be FALSE because this Activity uses a theme with no move animation.
127         assertFalse("Themes should be able to prevent move animations via windowNoMoveAnimation",
128                 window.getAttributes().canPlayMoveAnimation());
129 
130         // Window API should be able to override theme defaults from FALSE to TRUE.
131         getInstrumentation().runOnMainSync(() -> {
132             WindowManager.LayoutParams attrs = window.getAttributes();
133             attrs.setCanPlayMoveAnimation(true);
134             window.setAttributes(attrs);
135         });
136 
137         assertTrue("Window should know that it can play a move animation",
138                 window.getAttributes().canPlayMoveAnimation());
139         assertPlaysMoveAnimation(activity, true);
140     }
141 
142     @Test
testThemeCanDisable()143     public void testThemeCanDisable() {
144         final FloatingActivity.NoMove activity =
145                 startActivityInWindowingModeFullScreen(FloatingActivity.NoMove.class);
146         final Window window = activity.getWindow();
147 
148         // Window API should be able to override theme defaults from TRUE to FALSE.
149         getInstrumentation().runOnMainSync(() -> {
150             WindowManager.LayoutParams attrs = window.getAttributes();
151             attrs.setCanPlayMoveAnimation(false);
152             window.setAttributes(attrs);
153         });
154 
155         assertFalse("Window should know that it can NOT play a move animation",
156                 window.getAttributes().canPlayMoveAnimation());
157         assertPlaysMoveAnimation(activity, false);
158     }
159 
assertPlaysMoveAnimation(final FloatingActivity activity, final boolean isPlayed)160     private void assertPlaysMoveAnimation(final FloatingActivity activity, final boolean isPlayed) {
161         mWmState.waitForAppTransitionIdleOnDisplay(Display.DEFAULT_DISPLAY);
162 
163         activity.mContentView.post(() -> activity.setContentSquare(200));
164 
165         Condition isAnimating = new Condition("Window is animating",
166                 () -> {
167                         mWmState.computeState();
168                         WindowManagerState.Activity ar =
169                                 mWmState.getActivity(activity.getComponentName());
170                         return ar != null && ar.isAnimating();
171                 })
172                 .setRetryIntervalMs(16)
173                 .setRetryLimit(50);
174         assertEquals(isPlayed, Condition.waitFor(isAnimating));
175     }
176 }
177