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 com.android.server.wm; 18 19 import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME; 20 import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME; 21 import static com.android.server.wm.WindowFramesProto.CONTENT_INSETS; 22 import static com.android.server.wm.WindowFramesProto.CUTOUT; 23 import static com.android.server.wm.WindowFramesProto.DECOR_FRAME; 24 import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME; 25 import static com.android.server.wm.WindowFramesProto.FRAME; 26 import static com.android.server.wm.WindowFramesProto.PARENT_FRAME; 27 import static com.android.server.wm.WindowFramesProto.STABLE_INSETS; 28 import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME; 29 import static com.android.server.wm.WindowFramesProto.VISIBLE_INSETS; 30 31 import android.annotation.NonNull; 32 import android.graphics.Rect; 33 import android.util.proto.ProtoOutputStream; 34 import android.view.DisplayCutout; 35 36 import com.android.server.wm.utils.InsetUtils; 37 import com.android.server.wm.utils.WmDisplayCutout; 38 39 import java.io.PrintWriter; 40 41 /** 42 * Container class for all the window frames that affect how windows are laid out. 43 * 44 * TODO(b/111611553): Investigate which frames are still needed and which are duplicates 45 */ 46 public class WindowFrames { 47 private static final StringBuilder sTmpSB = new StringBuilder(); 48 49 /** 50 * In most cases, this is the area of the entire screen. 51 * 52 * TODO(b/111611553): The name is unclear and most likely should be swapped with 53 * {@link #mDisplayFrame} 54 * TODO(b/111611553): In some cases, it also includes top insets, like for IME. Determine 55 * whether this is still necessary to do. 56 */ 57 public final Rect mParentFrame = new Rect(); 58 59 /** 60 * The entire screen area of the {@link ActivityStack} this window is in. Usually equal to the 61 * screen area of the device. 62 * 63 * TODO(b/111611553): The name is unclear and most likely should be swapped with 64 * {@link #mParentFrame} 65 */ 66 public final Rect mDisplayFrame = new Rect(); 67 68 /** 69 * Legacy stuff. Generally equal to the content frame expect when the IME for older apps 70 * displays hint text. 71 */ 72 public final Rect mVisibleFrame = new Rect(); 73 74 /** 75 * The area not occupied by the status and navigation bars. So, if both status and navigation 76 * bars are visible, the decor frame is equal to the stable frame. 77 */ 78 public final Rect mDecorFrame = new Rect(); 79 80 /** 81 * Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame 82 * minus the area occupied by the IME if the IME is present. 83 */ 84 public final Rect mContentFrame = new Rect(); 85 86 /** 87 * The display frame minus the stable insets. This value is always constant regardless of if 88 * the status bar or navigation bar is visible. 89 */ 90 public final Rect mStableFrame = new Rect(); 91 92 /** 93 * Similar to {@link #mDisplayFrame} 94 * 95 * TODO: Why is this different than mDisplayFrame 96 */ 97 final Rect mContainingFrame = new Rect(); 98 99 /** 100 * "Real" frame that the application sees, in display coordinate space. 101 */ 102 final Rect mFrame = new Rect(); 103 104 /** 105 * The last real frame that was reported to the client. 106 */ 107 final Rect mLastFrame = new Rect(); 108 109 /** 110 * mFrame but relative to the parent container. 111 */ 112 final Rect mRelFrame = new Rect(); 113 114 /** 115 * mLastFrame but relative to the parent container 116 */ 117 final Rect mLastRelFrame = new Rect(); 118 119 private boolean mFrameSizeChanged = false; 120 121 // Frame that is scaled to the application's coordinate space when in 122 // screen size compatibility mode. 123 final Rect mCompatFrame = new Rect(); 124 125 /** 126 * Whether the parent frame would have been different if there was no display cutout. 127 */ 128 private boolean mParentFrameWasClippedByDisplayCutout; 129 130 /** 131 * Part of the display that has been cut away. See {@link DisplayCutout}. 132 */ 133 WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT; 134 135 /** 136 * The last cutout that has been reported to the client. 137 */ 138 private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT; 139 140 private boolean mDisplayCutoutChanged; 141 142 /** 143 * Insets that determine the area covered by the stable system windows. These are in the 144 * application's coordinate space (without compatibility scale applied). 145 */ 146 final Rect mStableInsets = new Rect(); 147 final Rect mLastStableInsets = new Rect(); 148 private boolean mStableInsetsChanged; 149 150 /** 151 * Insets that determine the actually visible area. These are in the application's 152 * coordinate space (without compatibility scale applied). 153 */ 154 final Rect mVisibleInsets = new Rect(); 155 final Rect mLastVisibleInsets = new Rect(); 156 private boolean mVisibleInsetsChanged; 157 158 /** 159 * Insets that are covered by system windows (such as the status bar) and 160 * transient docking windows (such as the IME). These are in the application's 161 * coordinate space (without compatibility scale applied). 162 */ 163 final Rect mContentInsets = new Rect(); 164 final Rect mLastContentInsets = new Rect(); 165 private boolean mContentInsetsChanged; 166 167 private final Rect mTmpRect = new Rect(); 168 169 private boolean mContentChanged; 170 WindowFrames()171 public WindowFrames() { 172 } 173 WindowFrames(Rect parentFrame, Rect displayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame)174 public WindowFrames(Rect parentFrame, Rect displayFrame, Rect contentFrame, 175 Rect visibleFrame, Rect decorFrame, Rect stableFrame) { 176 setFrames(parentFrame, displayFrame, contentFrame, visibleFrame, decorFrame, 177 stableFrame); 178 } 179 setFrames(Rect parentFrame, Rect displayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame)180 public void setFrames(Rect parentFrame, Rect displayFrame, 181 Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame) { 182 mParentFrame.set(parentFrame); 183 mDisplayFrame.set(displayFrame); 184 mContentFrame.set(contentFrame); 185 mVisibleFrame.set(visibleFrame); 186 mDecorFrame.set(decorFrame); 187 mStableFrame.set(stableFrame); 188 } 189 setParentFrameWasClippedByDisplayCutout( boolean parentFrameWasClippedByDisplayCutout)190 public void setParentFrameWasClippedByDisplayCutout( 191 boolean parentFrameWasClippedByDisplayCutout) { 192 mParentFrameWasClippedByDisplayCutout = parentFrameWasClippedByDisplayCutout; 193 } 194 parentFrameWasClippedByDisplayCutout()195 boolean parentFrameWasClippedByDisplayCutout() { 196 return mParentFrameWasClippedByDisplayCutout; 197 } 198 setDisplayCutout(WmDisplayCutout displayCutout)199 public void setDisplayCutout(WmDisplayCutout displayCutout) { 200 mDisplayCutout = displayCutout; 201 } 202 203 /** 204 * @return true if the width or height has changed since last reported to the client. 205 */ didFrameSizeChange()206 private boolean didFrameSizeChange() { 207 return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height()); 208 } 209 210 // TODO(b/118118435): Remove after migration. 211 /** 212 * Calculate the insets for the type 213 * {@link android.view.WindowManager.LayoutParams#TYPE_DOCK_DIVIDER} 214 * 215 * @param cutoutInsets The insets for the cutout. 216 */ calculateDockedDividerInsets(Rect cutoutInsets)217 void calculateDockedDividerInsets(Rect cutoutInsets) { 218 // For the docked divider, we calculate the stable insets like a full-screen window 219 // so it can use it to calculate the snap positions. 220 mTmpRect.set(mDisplayFrame); 221 mTmpRect.inset(cutoutInsets); 222 mTmpRect.intersectUnchecked(mStableFrame); 223 InsetUtils.insetsBetweenFrames(mDisplayFrame, mTmpRect, mStableInsets); 224 225 // The divider doesn't care about insets in any case, so set it to empty so we don't 226 // trigger a relayout when moving it. 227 mContentInsets.setEmpty(); 228 mVisibleInsets.setEmpty(); 229 mDisplayCutout = WmDisplayCutout.NO_CUTOUT; 230 } 231 232 /** 233 * Calculate the insets for a window. 234 * 235 * @param windowsAreFloating Whether the window is in a floating task such as pinned or 236 * freeform 237 * @param inFullscreenContainer Whether the window is in a container that takes up the screen's 238 * entire space 239 * @param windowBounds The bounds for the window 240 */ calculateInsets(boolean windowsAreFloating, boolean inFullscreenContainer, Rect windowBounds)241 void calculateInsets(boolean windowsAreFloating, boolean inFullscreenContainer, 242 Rect windowBounds) { 243 // Override right and/or bottom insets in case if the frame doesn't fit the screen in 244 // non-fullscreen mode. 245 boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer 246 && mFrame.right > windowBounds.right; 247 boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer 248 && mFrame.bottom > windowBounds.bottom; 249 250 mTmpRect.set(mFrame.left, mFrame.top, 251 overrideRightInset ? windowBounds.right : mFrame.right, 252 overrideBottomInset ? windowBounds.bottom : mFrame.bottom); 253 254 InsetUtils.insetsBetweenFrames(mTmpRect, mContentFrame, mContentInsets); 255 InsetUtils.insetsBetweenFrames(mTmpRect, mVisibleFrame, mVisibleInsets); 256 InsetUtils.insetsBetweenFrames(mTmpRect, mStableFrame, mStableInsets); 257 } 258 259 /** 260 * Scales all the insets by a specific amount. 261 * 262 * @param scale The amount to scale the insets by. 263 */ scaleInsets(float scale)264 void scaleInsets(float scale) { 265 mContentInsets.scale(scale); 266 mVisibleInsets.scale(scale); 267 mStableInsets.scale(scale); 268 } 269 offsetFrames(int layoutXDiff, int layoutYDiff)270 void offsetFrames(int layoutXDiff, int layoutYDiff) { 271 mFrame.offset(layoutXDiff, layoutYDiff); 272 mContentFrame.offset(layoutXDiff, layoutYDiff); 273 mVisibleFrame.offset(layoutXDiff, layoutYDiff); 274 mStableFrame.offset(layoutXDiff, layoutYDiff); 275 } 276 277 /** 278 * Updates info about whether the size of the window has changed since last reported. 279 * 280 * @return true if info about size has changed since last reported. 281 */ setReportResizeHints()282 boolean setReportResizeHints() { 283 mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets); 284 mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets); 285 mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets); 286 mFrameSizeChanged |= didFrameSizeChange(); 287 mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout); 288 return mContentInsetsChanged || mVisibleInsetsChanged 289 || mStableInsetsChanged || mFrameSizeChanged 290 || mDisplayCutoutChanged; 291 } 292 293 /** 294 * Resets the insets changed flags so they're all set to false again. This should be called 295 * after the insets are reported to client. 296 */ resetInsetsChanged()297 void resetInsetsChanged() { 298 mContentInsetsChanged = false; 299 mVisibleInsetsChanged = false; 300 mStableInsetsChanged = false; 301 mFrameSizeChanged = false; 302 mDisplayCutoutChanged = false; 303 } 304 305 /** 306 * Copy over inset values as the last insets that were sent to the client. 307 */ updateLastInsetValues()308 void updateLastInsetValues() { 309 mLastContentInsets.set(mContentInsets); 310 mLastVisibleInsets.set(mVisibleInsets); 311 mLastStableInsets.set(mStableInsets); 312 mLastDisplayCutout = mDisplayCutout; 313 } 314 315 /** 316 * Sets the last content insets as (-1, -1, -1, -1) to force the next layout pass to update 317 * the client. 318 */ resetLastContentInsets()319 void resetLastContentInsets() { 320 mLastContentInsets.set(-1, -1, -1, -1); 321 } 322 323 /** 324 * Sets whether the content has changed. This means that either the size or parent frame has 325 * changed. 326 */ setContentChanged(boolean contentChanged)327 public void setContentChanged(boolean contentChanged) { 328 mContentChanged = contentChanged; 329 } 330 331 /** 332 * @see #setContentChanged(boolean) 333 */ hasContentChanged()334 boolean hasContentChanged() { 335 return mContentChanged; 336 } 337 dumpDebug(@onNull ProtoOutputStream proto, long fieldId)338 public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) { 339 final long token = proto.start(fieldId); 340 mParentFrame.dumpDebug(proto, PARENT_FRAME); 341 mContentFrame.dumpDebug(proto, CONTENT_FRAME); 342 mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME); 343 mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME); 344 mDecorFrame.dumpDebug(proto, DECOR_FRAME); 345 mContainingFrame.dumpDebug(proto, CONTAINING_FRAME); 346 mFrame.dumpDebug(proto, FRAME); 347 mDisplayCutout.getDisplayCutout().dumpDebug(proto, CUTOUT); 348 mContentInsets.dumpDebug(proto, CONTENT_INSETS); 349 mVisibleInsets.dumpDebug(proto, VISIBLE_INSETS); 350 mStableInsets.dumpDebug(proto, STABLE_INSETS); 351 352 proto.end(token); 353 } 354 dump(PrintWriter pw, String prefix)355 public void dump(PrintWriter pw, String prefix) { 356 pw.println(prefix + "Frames: containing=" 357 + mContainingFrame.toShortString(sTmpSB) 358 + " parent=" + mParentFrame.toShortString(sTmpSB)); 359 pw.println(prefix + " display=" + mDisplayFrame.toShortString(sTmpSB)); 360 pw.println(prefix + " content=" + mContentFrame.toShortString(sTmpSB) 361 + " visible=" + mVisibleFrame.toShortString(sTmpSB)); 362 pw.println(prefix + " decor=" + mDecorFrame.toShortString(sTmpSB)); 363 pw.println(prefix + "mFrame=" + mFrame.toShortString(sTmpSB) 364 + " last=" + mLastFrame.toShortString(sTmpSB)); 365 pw.println(prefix + " cutout=" + mDisplayCutout.getDisplayCutout() 366 + " last=" + mLastDisplayCutout.getDisplayCutout()); 367 pw.print(prefix + "Cur insets: content=" + mContentInsets.toShortString(sTmpSB) 368 + " visible=" + mVisibleInsets.toShortString(sTmpSB) 369 + " stable=" + mStableInsets.toShortString(sTmpSB)); 370 pw.println(prefix + "Lst insets: content=" + mLastContentInsets.toShortString(sTmpSB) 371 + " visible=" + mLastVisibleInsets.toShortString(sTmpSB) 372 + " stable=" + mLastStableInsets.toShortString(sTmpSB)); 373 } 374 getInsetsInfo()375 String getInsetsInfo() { 376 return "ci=" + mContentInsets.toShortString() 377 + " vi=" + mVisibleInsets.toShortString() 378 + " si=" + mStableInsets.toShortString(); 379 } 380 getInsetsChangedInfo()381 String getInsetsChangedInfo() { 382 return "contentInsetsChanged=" + mContentInsetsChanged 383 + " " + mContentInsets.toShortString() 384 + " visibleInsetsChanged=" + mVisibleInsetsChanged 385 + " " + mVisibleInsets.toShortString() 386 + " stableInsetsChanged=" + mStableInsetsChanged 387 + " " + mStableInsets.toShortString() 388 + " displayCutoutChanged=" + mDisplayCutoutChanged; 389 } 390 } 391