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