1 /* 2 * Copyright (C) 2020 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.insets; 18 19 import static org.junit.Assert.assertEquals; 20 21 import android.app.Activity; 22 import android.content.Intent; 23 import android.graphics.Insets; 24 import android.graphics.Rect; 25 import android.os.Bundle; 26 import android.view.View; 27 import android.view.ViewTreeObserver; 28 import android.view.Window; 29 import android.view.WindowInsets; 30 31 import androidx.annotation.Nullable; 32 import androidx.test.rule.ActivityTestRule; 33 34 import org.junit.Rule; 35 36 import java.util.concurrent.CountDownLatch; 37 38 public class DecorInsetTestsBase { 39 40 public static final String ARG_DECOR_FITS_SYSTEM_WINDOWS = "decorFitsSystemWindows"; 41 public static final String ARG_LAYOUT_STABLE = "flagLayoutStable"; 42 public static final String ARG_LAYOUT_FULLSCREEN = "flagLayoutFullscreen"; 43 public static final String ARG_LAYOUT_HIDE_NAV = "flagLayoutHideNav"; 44 45 @Rule 46 public ActivityTestRule<TestActivity> mDecorActivity = new ActivityTestRule<>( 47 TestActivity.class, false /* initialTouchMode */, 48 false /* launchActivity */); 49 50 public static class TestActivity extends Activity { 51 WindowInsets mLastContentInsets; 52 WindowInsets mLastDecorInsets; 53 final CountDownLatch mLaidOut = new CountDownLatch(1); 54 55 @Override onCreate(@ullable Bundle savedInstanceState)56 protected void onCreate(@Nullable Bundle savedInstanceState) { 57 super.onCreate(savedInstanceState); 58 getWindow().requestFeature(Window.FEATURE_NO_TITLE); 59 60 getWindow().setDecorFitsSystemWindows( 61 getIntent().getBooleanExtra(ARG_DECOR_FITS_SYSTEM_WINDOWS, false)); 62 63 View view = new View(this); 64 view.setSystemUiVisibility(intentToSysuiVisibility(getIntent())); 65 66 view.setOnApplyWindowInsetsListener((v, wi) -> { 67 mLastContentInsets = wi; 68 return WindowInsets.CONSUMED; 69 }); 70 setContentView(view); 71 getWindow().getDecorView().setOnApplyWindowInsetsListener((v, wi) -> { 72 mLastDecorInsets = wi; 73 return v.onApplyWindowInsets(wi); 74 }); 75 76 view.getViewTreeObserver().addOnGlobalLayoutListener( 77 new ViewTreeObserver.OnGlobalLayoutListener() { 78 @Override 79 public void onGlobalLayout() { 80 view.getViewTreeObserver().removeOnGlobalLayoutListener(this); 81 mLaidOut.countDown(); 82 } 83 }); 84 } 85 getLastContentInsets()86 public WindowInsets getLastContentInsets() { 87 return mLastContentInsets; 88 } 89 getLastDecorInsets()90 public WindowInsets getLastDecorInsets() { 91 return mLastDecorInsets; 92 } 93 getLaidOut()94 public CountDownLatch getLaidOut() { 95 return mLaidOut; 96 } 97 intentToSysuiVisibility(Intent intent)98 private static int intentToSysuiVisibility(Intent intent) { 99 int vis = 0; 100 vis |= intent.getBooleanExtra(ARG_LAYOUT_HIDE_NAV, false) 101 ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0; 102 vis |= intent.getBooleanExtra(ARG_LAYOUT_FULLSCREEN, false) 103 ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN : 0; 104 vis |= intent.getBooleanExtra(ARG_LAYOUT_STABLE, false) 105 ? View.SYSTEM_UI_FLAG_LAYOUT_STABLE : 0; 106 return vis; 107 } 108 } 109 assertContentViewLocationMatchesInsets()110 public void assertContentViewLocationMatchesInsets() { 111 TestActivity activity = mDecorActivity.getActivity(); 112 113 Insets insetsConsumedByDecor = Insets.subtract( 114 systemWindowInsetsOrZero(activity.mLastDecorInsets), 115 systemWindowInsetsOrZero(activity.mLastContentInsets)); 116 Rect expectedContentRect = rectInWindow(activity.getWindow().getDecorView()); 117 insetRect(expectedContentRect, insetsConsumedByDecor); 118 119 Rect actualContentRect = rectInWindow(activity.findViewById(android.R.id.content)); 120 121 assertEquals("Decor consumed " + insetsConsumedByDecor + ", content rect:", 122 expectedContentRect, actualContentRect); 123 } 124 systemWindowInsetsOrZero(WindowInsets wi)125 public Insets systemWindowInsetsOrZero(WindowInsets wi) { 126 if (wi == null) { 127 return Insets.NONE; 128 } 129 return wi.getSystemWindowInsets(); 130 } 131 rectInWindow(View view)132 private Rect rectInWindow(View view) { 133 int[] loc = new int[2]; 134 view.getLocationInWindow(loc); 135 return new Rect(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight()); 136 } 137 insetRect(Rect rect, Insets insets)138 private static void insetRect(Rect rect, Insets insets) { 139 rect.left += insets.left; 140 rect.top += insets.top; 141 rect.right -= insets.right; 142 rect.bottom -= insets.bottom; 143 } 144 } 145