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