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