1 /* 2 * Copyright (C) 2017 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 com.android.cts.mockime; 18 19 import android.graphics.Point; 20 import android.graphics.Rect; 21 import android.os.Bundle; 22 import androidx.annotation.NonNull; 23 import androidx.annotation.Nullable; 24 import android.view.Display; 25 import android.view.View; 26 import android.view.WindowInsets; 27 28 /** 29 * A collection of layout-related information when 30 * {@link View.OnLayoutChangeListener#onLayoutChange(View, int, int, int, int, int, int, int, int)} 31 * is called back for the input view (the view returned from {@link MockIme#onCreateInputView()}). 32 */ 33 public final class ImeLayoutInfo { 34 35 private static final String NEW_LAYOUT_KEY = "newLayout"; 36 private static final String OLD_LAYOUT_KEY = "oldLayout"; 37 private static final String VIEW_ORIGIN_ON_SCREEN_KEY = "viewOriginOnScreen"; 38 private static final String DISPLAY_SIZE_KEY = "displaySize"; 39 private static final String SYSTEM_WINDOW_INSET_KEY = "systemWindowInset"; 40 private static final String STABLE_INSET_KEY = "stableInset"; 41 42 @NonNull 43 private final Rect mNewLayout; 44 @NonNull 45 private final Rect mOldLayout; 46 @Nullable 47 private Point mViewOriginOnScreen; 48 @Nullable 49 private Point mDisplaySize; 50 @Nullable 51 private Rect mSystemWindowInset; 52 @Nullable 53 private Rect mStableInset; 54 55 /** 56 * Returns the bounding box of the {@link View} passed to 57 * {@link android.inputmethodservice.InputMethodService#onCreateInputView()} in screen 58 * coordinates. 59 * 60 * <p>Currently this method assumes that no {@link View} in the hierarchy uses 61 * transformations such as {@link View#setRotation(float)}.</p> 62 * 63 * @return Region in screen coordinates. 64 */ 65 @Nullable getInputViewBoundsInScreen()66 public Rect getInputViewBoundsInScreen() { 67 return new Rect( 68 mViewOriginOnScreen.x, mViewOriginOnScreen.y, 69 mViewOriginOnScreen.x + mNewLayout.width(), 70 mViewOriginOnScreen.y + mNewLayout.height()); 71 } 72 73 /** 74 * Returns the screen area in screen coordinates that does not overlap with the system 75 * window inset, which represents the area of a full-screen window that is partially or 76 * fully obscured by the status bar, navigation bar, IME or other system windows. 77 * 78 * <p>May return {@code null} when this information is not yet ready.</p> 79 * 80 * @return Region in screen coordinates. {@code null} when it is not available 81 * 82 * @see WindowInsets#hasSystemWindowInsets() 83 * @see WindowInsets#getSystemWindowInsetBottom() 84 * @see WindowInsets#getSystemWindowInsetLeft() 85 * @see WindowInsets#getSystemWindowInsetRight() 86 * @see WindowInsets#getSystemWindowInsetTop() 87 */ 88 @Nullable getScreenRectWithoutSystemWindowInset()89 public Rect getScreenRectWithoutSystemWindowInset() { 90 if (mDisplaySize == null) { 91 return null; 92 } 93 if (mSystemWindowInset == null) { 94 return new Rect(0, 0, mDisplaySize.x, mDisplaySize.y); 95 } 96 return new Rect(mSystemWindowInset.left, mSystemWindowInset.top, 97 mDisplaySize.x - mSystemWindowInset.right, 98 mDisplaySize.y - mSystemWindowInset.bottom); 99 } 100 101 /** 102 * Returns the screen area in screen coordinates that does not overlap with the stable 103 * inset, which represents the area of a full-screen window that <b>may</b> be partially or 104 * fully obscured by the system UI elements. 105 * 106 * <p>May return {@code null} when this information is not yet ready.</p> 107 * 108 * @return Region in screen coordinates. {@code null} when it is not available 109 * 110 * @see WindowInsets#hasStableInsets() 111 * @see WindowInsets#getStableInsetBottom() 112 * @see WindowInsets#getStableInsetLeft() 113 * @see WindowInsets#getStableInsetRight() 114 * @see WindowInsets#getStableInsetTop() 115 */ 116 @Nullable getScreenRectWithoutStableInset()117 public Rect getScreenRectWithoutStableInset() { 118 if (mDisplaySize == null) { 119 return null; 120 } 121 if (mStableInset == null) { 122 return new Rect(0, 0, mDisplaySize.x, mDisplaySize.y); 123 } 124 return new Rect(mStableInset.left, mStableInset.top, 125 mDisplaySize.x - mStableInset.right, 126 mDisplaySize.y - mStableInset.bottom); 127 } 128 ImeLayoutInfo(@onNull Rect newLayout, @NonNull Rect oldLayout, @NonNull Point viewOriginOnScreen, @Nullable Point displaySize, @Nullable Rect systemWindowInset, @Nullable Rect stableInset)129 ImeLayoutInfo(@NonNull Rect newLayout, @NonNull Rect oldLayout, 130 @NonNull Point viewOriginOnScreen, @Nullable Point displaySize, 131 @Nullable Rect systemWindowInset, @Nullable Rect stableInset) { 132 mNewLayout = new Rect(newLayout); 133 mOldLayout = new Rect(oldLayout); 134 mViewOriginOnScreen = new Point(viewOriginOnScreen); 135 mDisplaySize = new Point(displaySize); 136 mSystemWindowInset = systemWindowInset; 137 mStableInset = stableInset; 138 } 139 writeToBundle(@onNull Bundle bundle)140 void writeToBundle(@NonNull Bundle bundle) { 141 bundle.putParcelable(NEW_LAYOUT_KEY, mNewLayout); 142 bundle.putParcelable(OLD_LAYOUT_KEY, mOldLayout); 143 bundle.putParcelable(VIEW_ORIGIN_ON_SCREEN_KEY, mViewOriginOnScreen); 144 bundle.putParcelable(DISPLAY_SIZE_KEY, mDisplaySize); 145 bundle.putParcelable(SYSTEM_WINDOW_INSET_KEY, mSystemWindowInset); 146 bundle.putParcelable(STABLE_INSET_KEY, mStableInset); 147 } 148 readFromBundle(@onNull Bundle bundle)149 static ImeLayoutInfo readFromBundle(@NonNull Bundle bundle) { 150 final Rect newLayout = bundle.getParcelable(NEW_LAYOUT_KEY); 151 final Rect oldLayout = bundle.getParcelable(OLD_LAYOUT_KEY); 152 final Point viewOrigin = bundle.getParcelable(VIEW_ORIGIN_ON_SCREEN_KEY); 153 final Point displaySize = bundle.getParcelable(DISPLAY_SIZE_KEY); 154 final Rect systemWindowInset = bundle.getParcelable(SYSTEM_WINDOW_INSET_KEY); 155 final Rect stableInset = bundle.getParcelable(STABLE_INSET_KEY); 156 157 return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize, systemWindowInset, 158 stableInset); 159 } 160 fromLayoutListenerCallback(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)161 static ImeLayoutInfo fromLayoutListenerCallback(View v, int left, int top, int right, 162 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 163 final Rect newLayout = new Rect(left, top, right, bottom); 164 final Rect oldLayout = new Rect(oldLeft, oldTop, oldRight, oldBottom); 165 final int[] viewOriginArray = new int[2]; 166 v.getLocationOnScreen(viewOriginArray); 167 final Point viewOrigin = new Point(viewOriginArray[0], viewOriginArray[1]); 168 final Display display = v.getDisplay(); 169 final Point displaySize; 170 if (display != null) { 171 displaySize = new Point(); 172 display.getRealSize(displaySize); 173 } else { 174 displaySize = null; 175 } 176 final WindowInsets windowInsets = v.getRootWindowInsets(); 177 final Rect systemWindowInset; 178 if (windowInsets != null && windowInsets.hasSystemWindowInsets()) { 179 systemWindowInset = new Rect( 180 windowInsets.getSystemWindowInsetLeft(), windowInsets.getSystemWindowInsetTop(), 181 windowInsets.getSystemWindowInsetRight(), 182 windowInsets.getSystemWindowInsetBottom()); 183 } else { 184 systemWindowInset = null; 185 } 186 final Rect stableInset; 187 if (windowInsets != null && windowInsets.hasStableInsets()) { 188 stableInset = new Rect( 189 windowInsets.getStableInsetLeft(), windowInsets.getStableInsetTop(), 190 windowInsets.getStableInsetRight(), windowInsets.getStableInsetBottom()); 191 } else { 192 stableInset = null; 193 } 194 return new ImeLayoutInfo(newLayout, oldLayout, viewOrigin, displaySize, systemWindowInset, 195 stableInset); 196 } 197 } 198