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