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; 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 intentToSysuiVisibility(Intent intent)86 private static int intentToSysuiVisibility(Intent intent) { 87 int vis = 0; 88 vis |= intent.getBooleanExtra(ARG_LAYOUT_HIDE_NAV, false) 89 ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0; 90 vis |= intent.getBooleanExtra(ARG_LAYOUT_FULLSCREEN, false) 91 ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN : 0; 92 vis |= intent.getBooleanExtra(ARG_LAYOUT_STABLE, false) 93 ? View.SYSTEM_UI_FLAG_LAYOUT_STABLE : 0; 94 return vis; 95 } 96 } 97 assertContentViewLocationMatchesInsets()98 public void assertContentViewLocationMatchesInsets() { 99 TestActivity activity = mDecorActivity.getActivity(); 100 101 Insets insetsConsumedByDecor = Insets.subtract( 102 systemWindowInsetsOrZero(activity.mLastDecorInsets), 103 systemWindowInsetsOrZero(activity.mLastContentInsets)); 104 Rect expectedContentRect = rectInWindow(activity.getWindow().getDecorView()); 105 insetRect(expectedContentRect, insetsConsumedByDecor); 106 107 Rect actualContentRect = rectInWindow(activity.findViewById(android.R.id.content)); 108 109 assertEquals("Decor consumed " + insetsConsumedByDecor + ", content rect:", 110 expectedContentRect, actualContentRect); 111 } 112 systemWindowInsetsOrZero(WindowInsets wi)113 public Insets systemWindowInsetsOrZero(WindowInsets wi) { 114 if (wi == null) { 115 return Insets.NONE; 116 } 117 return wi.getSystemWindowInsets(); 118 } 119 rectInWindow(View view)120 private Rect rectInWindow(View view) { 121 int[] loc = new int[2]; 122 view.getLocationInWindow(loc); 123 return new Rect(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight()); 124 } 125 insetRect(Rect rect, Insets insets)126 private static void insetRect(Rect rect, Insets insets) { 127 rect.left += insets.left; 128 rect.top += insets.top; 129 rect.right -= insets.right; 130 rect.bottom -= insets.bottom; 131 } 132 } 133