1 /*
2  * Copyright (C) 2018 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.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry;
20 import static android.server.wm.animations.LocationOnScreenTests.TestActivity.EXTRA_LAYOUT_PARAMS;
21 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
22 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
23 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
24 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
25 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
26 
27 import static androidx.test.InstrumentationRegistry.getInstrumentation;
28 
29 import static junit.framework.Assert.assertTrue;
30 
31 import static org.hamcrest.Matchers.is;
32 
33 import android.app.Activity;
34 import android.content.Intent;
35 import android.graphics.PixelFormat;
36 import android.graphics.Point;
37 import android.os.Bundle;
38 import android.platform.test.annotations.Presubmit;
39 import android.view.Gravity;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.view.Window;
43 import android.view.WindowManager.LayoutParams;
44 import android.widget.FrameLayout;
45 
46 import androidx.test.filters.SmallTest;
47 import androidx.test.rule.ActivityTestRule;
48 
49 import com.android.compatibility.common.util.PollingCheck;
50 import com.android.compatibility.common.util.WindowUtil;
51 
52 import org.hamcrest.Matcher;
53 import org.junit.Before;
54 import org.junit.Rule;
55 import org.junit.Test;
56 import org.junit.rules.ErrorCollector;
57 
58 import java.util.concurrent.TimeUnit;
59 import java.util.function.Supplier;
60 
61 @SmallTest
62 @Presubmit
63 public class LocationInWindowTests {
64 
65     @Rule
66     public final ErrorCollector mErrorCollector = new ErrorCollector();
67 
68     @Rule
69     public final ActivityTestRule<TestActivity> mActivity =
70             new ActivityTestRule<>(TestActivity.class, false /* initialTouchMode */,
71                     false /* launchActivity */);
72 
73     private LayoutParams mLayoutParams;
74 
75     @Before
setUp()76     public void setUp() throws Exception {
77         mLayoutParams = new LayoutParams(MATCH_PARENT, MATCH_PARENT, LayoutParams.TYPE_APPLICATION,
78                 LayoutParams.FLAG_LAYOUT_IN_SCREEN | LayoutParams.FLAG_LAYOUT_INSET_DECOR,
79                 PixelFormat.TRANSLUCENT);
80         assertTrue("Failed to reach stable window geometry",
81                 waitForStableWindowGeometry(5, TimeUnit.SECONDS));
82     }
83 
84     @Test
testLocationInWindow_appWindow()85     public void testLocationInWindow_appWindow() {
86         runTest(mLayoutParams);
87     }
88 
89     @Test
testLocationInWindow_appWindow_fullscreen()90     public void testLocationInWindow_appWindow_fullscreen() {
91         mLayoutParams.flags |= LayoutParams.FLAG_FULLSCREEN;
92         runTest(mLayoutParams);
93     }
94 
95     @Test
testLocationInWindow_floatingWindow()96     public void testLocationInWindow_floatingWindow() {
97         mLayoutParams.height = 100;
98         mLayoutParams.width = 100;
99         mLayoutParams.gravity = Gravity.CENTER;
100         mLayoutParams.flags &= ~(FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR);
101         runTest(mLayoutParams);
102     }
103 
104     @Test
testLocationInWindow_appWindow_displayCutoutNever()105     public void testLocationInWindow_appWindow_displayCutoutNever() {
106         mLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
107         runTest(mLayoutParams);
108     }
109 
110     @Test
testLocationInWindow_appWindow_displayCutoutShortEdges()111     public void testLocationInWindow_appWindow_displayCutoutShortEdges() {
112         mLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
113         runTest(mLayoutParams);
114     }
115 
runTest(LayoutParams lp)116     private void runTest(LayoutParams lp) {
117         final TestActivity activity = launchAndWait(mActivity, lp);
118         PollingCheck.waitFor(() -> getOnMainSync(activity::isEnterAnimationComplete));
119 
120         runOnMainSync(() -> {
121             assertThatViewGetLocationInWindowIsCorrect(activity.mView);
122         });
123     }
124 
assertThatViewGetLocationInWindowIsCorrect(View v)125     private void assertThatViewGetLocationInWindowIsCorrect(View v) {
126         final float[] expected = new float[] {0, 0};
127 
128         v.getMatrix().mapPoints(expected);
129         expected[0] += v.getLeft();
130         expected[1] += v.getTop();
131 
132         final View parent = (View)v.getParent();
133         expected[0] -= parent.getScrollX();
134         expected[1] -= parent.getScrollY();
135 
136         final Point parentLocation = locationInWindowAsPoint(parent);
137         expected[0] += parentLocation.x;
138         expected[1] += parentLocation.y;
139 
140         Point actual = locationInWindowAsPoint(v);
141 
142         assertThat("getLocationInWindow returned wrong location",
143                 actual, is(new Point((int) expected[0], (int) expected[1])));
144     }
145 
locationInWindowAsPoint(View v)146     private Point locationInWindowAsPoint(View v) {
147         final int[] out = new int[2];
148         v.getLocationInWindow(out);
149         return new Point(out[0], out[1]);
150     }
151 
assertThat(String reason, T actual, Matcher<? super T> matcher)152     private <T> void assertThat(String reason, T actual, Matcher<? super T> matcher) {
153         mErrorCollector.checkThat(reason, actual, matcher);
154     }
155 
getOnMainSync(Supplier<R> f)156     private <R> R getOnMainSync(Supplier<R> f) {
157         final Object[] result = new Object[1];
158         runOnMainSync(() -> result[0] = f.get());
159         //noinspection unchecked
160         return (R) result[0];
161     }
162 
runOnMainSync(Runnable runnable)163     private void runOnMainSync(Runnable runnable) {
164         getInstrumentation().runOnMainSync(runnable);
165     }
166 
launchAndWait(ActivityTestRule<T> rule, LayoutParams lp)167     private <T extends Activity> T launchAndWait(ActivityTestRule<T> rule,
168             LayoutParams lp) {
169         final T activity = rule.launchActivity(
170                 new Intent().putExtra(EXTRA_LAYOUT_PARAMS, lp));
171         WindowUtil.waitForFocus(activity);
172         return activity;
173     }
174 
175     public static class TestActivity extends Activity {
176 
177         static final String EXTRA_LAYOUT_PARAMS = "extra.layout_params";
178         private View mView;
179         private boolean mEnterAnimationComplete;
180 
181         @Override
onCreate(Bundle savedInstanceState)182         protected void onCreate(Bundle savedInstanceState) {
183             super.onCreate(savedInstanceState);
184             getWindow().requestFeature(Window.FEATURE_NO_TITLE);
185 
186             FrameLayout frame = new FrameLayout(this);
187             frame.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
188             frame.setPadding(1, 2, 3, 4);
189             setContentView(frame);
190 
191             mView = new View(this);
192             final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(1, 1);
193             lp.leftMargin = 6;
194             lp.topMargin = 7;
195             frame.addView(mView, lp);
196             mView.setTranslationX(8);
197             mView.setTranslationY(9);
198             mView.setScrollX(10);
199             mView.setScrollY(11);
200             frame.setScrollX(12);
201             frame.setScrollY(13);
202 
203             if (getIntent() != null
204                     && getIntent().getParcelableExtra(EXTRA_LAYOUT_PARAMS) != null) {
205                 getWindow().setAttributes(getIntent().getParcelableExtra(EXTRA_LAYOUT_PARAMS));
206             }
207         }
208 
isEnterAnimationComplete()209         public boolean isEnterAnimationComplete() {
210             return mEnterAnimationComplete;
211         }
212 
213         @Override
onEnterAnimationComplete()214         public void onEnterAnimationComplete() {
215             super.onEnterAnimationComplete();
216             mEnterAnimationComplete = true;
217         }
218     }
219 }
220