1 /* 2 * Copyright (C) 2015 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 android.app.WallpaperManager.COMMAND_FREEZE; 20 import static android.app.WallpaperManager.COMMAND_UNFREEZE; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 22 import static android.view.Display.DEFAULT_DISPLAY; 23 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 24 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 25 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 26 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 27 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; 28 29 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER; 30 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; 31 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; 32 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; 33 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; 34 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; 35 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 36 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 37 import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT; 38 39 import android.annotation.Nullable; 40 import android.graphics.Bitmap; 41 import android.graphics.Point; 42 import android.graphics.Rect; 43 import android.os.Bundle; 44 import android.os.Debug; 45 import android.os.IBinder; 46 import android.os.RemoteException; 47 import android.os.SystemClock; 48 import android.util.ArraySet; 49 import android.util.MathUtils; 50 import android.util.Slog; 51 import android.view.Display; 52 import android.view.DisplayInfo; 53 import android.view.SurfaceControl; 54 import android.view.WindowManager; 55 import android.view.animation.Animation; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.internal.protolog.ProtoLogImpl; 59 import com.android.internal.protolog.common.ProtoLog; 60 import com.android.internal.util.ToBooleanFunction; 61 62 import java.io.PrintWriter; 63 import java.util.ArrayList; 64 import java.util.List; 65 import java.util.function.Consumer; 66 67 /** 68 * Controls wallpaper windows visibility, ordering, and so on. 69 * NOTE: All methods in this class must be called with the window manager service lock held. 70 */ 71 class WallpaperController { 72 private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM; 73 private WindowManagerService mService; 74 private final DisplayContent mDisplayContent; 75 76 private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>(); 77 78 // If non-null, this is the currently visible window that is associated 79 // with the wallpaper. 80 private WindowState mWallpaperTarget = null; 81 // If non-null, we are in the middle of animating from one wallpaper target 82 // to another, and this is the previous wallpaper target. 83 private WindowState mPrevWallpaperTarget = null; 84 85 private float mLastWallpaperX = -1; 86 private float mLastWallpaperY = -1; 87 private float mLastWallpaperXStep = -1; 88 private float mLastWallpaperYStep = -1; 89 private float mLastWallpaperZoomOut = 0; 90 private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE; 91 private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE; 92 private final float mMaxWallpaperScale; 93 // Whether COMMAND_FREEZE was dispatched. 94 private boolean mLastFrozen = false; 95 96 // This is set when we are waiting for a wallpaper to tell us it is done 97 // changing its scroll position. 98 private WindowState mWaitingOnWallpaper; 99 100 // The last time we had a timeout when waiting for a wallpaper. 101 private long mLastWallpaperTimeoutTime; 102 // We give a wallpaper up to 150ms to finish scrolling. 103 private static final long WALLPAPER_TIMEOUT = 150; 104 // Time we wait after a timeout before trying to wait again. 105 private static final long WALLPAPER_TIMEOUT_RECOVERY = 10000; 106 107 // We give a wallpaper up to 500ms to finish drawing before playing app transitions. 108 private static final long WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION = 500; 109 private static final int WALLPAPER_DRAW_NORMAL = 0; 110 private static final int WALLPAPER_DRAW_PENDING = 1; 111 private static final int WALLPAPER_DRAW_TIMEOUT = 2; 112 private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; 113 114 private boolean mShouldUpdateZoom; 115 116 @Nullable private Point mLargestDisplaySize = null; 117 118 private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult(); 119 120 private boolean mShouldOffsetWallpaperCenter; 121 122 private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> { 123 if ((w.mAttrs.type == TYPE_WALLPAPER)) { 124 if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) { 125 mFindResults.setTopWallpaper(w); 126 mFindResults.resetTopWallpaper = false; 127 } 128 return false; 129 } 130 131 mFindResults.resetTopWallpaper = true; 132 if (!w.mTransitionController.isShellTransitionsEnabled()) { 133 if (w.mActivityRecord != null && !w.mActivityRecord.isVisible() 134 && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) { 135 // If this window's app token is hidden and not animating, it is of no interest. 136 if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w); 137 return false; 138 } 139 } else { 140 final ActivityRecord ar = w.mActivityRecord; 141 final TransitionController tc = w.mTransitionController; 142 // The animating window can still be visible on screen if it is in transition, so we 143 // should check whether this window can be wallpaper target even when visibleRequested 144 // is false. 145 if (ar != null && !ar.isVisibleRequested() && !tc.inTransition(ar)) { 146 // An activity that is not going to remain visible shouldn't be the target. 147 return false; 148 } 149 } 150 if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen() 151 + " mDrawState=" + w.mWinAnimator.mDrawState); 152 153 if (w.mWillReplaceWindow && mWallpaperTarget == null 154 && !mFindResults.useTopWallpaperAsTarget) { 155 // When we are replacing a window and there was wallpaper before replacement, we want to 156 // keep the window until the new windows fully appear and can determine the visibility, 157 // to avoid flickering. 158 mFindResults.setUseTopWallpaperAsTarget(true); 159 } 160 161 final WindowContainer animatingContainer = w.mActivityRecord != null 162 ? w.mActivityRecord.getAnimatingContainer() : null; 163 final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null 164 && animatingContainer.isAnimating(TRANSITION | PARENTS) 165 && AppTransition.isKeyguardGoingAwayTransitOld(animatingContainer.mTransit) 166 && (animatingContainer.mTransitFlags 167 & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); 168 169 boolean needsShowWhenLockedWallpaper = false; 170 if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && mService.mPolicy.isKeyguardLocked()) { 171 final TransitionController tc = w.mTransitionController; 172 final boolean isInTransition = tc.isShellTransitionsEnabled() 173 && tc.inTransition(w); 174 if (mService.mPolicy.isKeyguardOccluded() || mService.mPolicy.isKeyguardUnoccluding() 175 || isInTransition) { 176 // The lowest show when locked window decides whether we need to put the wallpaper 177 // behind. 178 needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs) 179 || (w.mActivityRecord != null && !w.mActivityRecord.fillsParent()); 180 } 181 } 182 183 if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) { 184 // Keep the wallpaper during Keyguard exit but also when it's needed for a 185 // non-fullscreen show when locked activity. 186 mFindResults.setUseTopWallpaperAsTarget(true); 187 } 188 189 final boolean animationWallpaper = animatingContainer != null 190 && animatingContainer.getAnimation() != null 191 && animatingContainer.getAnimation().getShowWallpaper(); 192 final boolean hasWallpaper = w.hasWallpaper() || animationWallpaper; 193 if (isRecentsTransitionTarget(w) || isBackAnimationTarget(w)) { 194 if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w); 195 mFindResults.setWallpaperTarget(w); 196 return true; 197 } else if (hasWallpaper && w.isOnScreen() 198 && (mWallpaperTarget == w || w.isDrawFinishedLw())) { 199 if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w); 200 mFindResults.setWallpaperTarget(w); 201 if (w == mWallpaperTarget && w.isAnimating(TRANSITION | PARENTS)) { 202 // The current wallpaper target is animating, so we'll look behind it for 203 // another possible target and figure out what is going on later. 204 if (DEBUG_WALLPAPER) Slog.v(TAG, 205 "Win " + w + ": token animating, looking behind."); 206 } 207 mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground()); 208 // While the keyguard is going away, both notification shade and a normal activity such 209 // as a launcher can satisfy criteria for a wallpaper target. In this case, we should 210 // chose the normal activity, otherwise wallpaper becomes invisible when a new animation 211 // starts before the keyguard going away animation finishes. 212 return w.mActivityRecord != null; 213 } 214 return false; 215 }; 216 isRecentsTransitionTarget(WindowState w)217 private boolean isRecentsTransitionTarget(WindowState w) { 218 if (w.mTransitionController.isShellTransitionsEnabled()) { 219 // Because the recents activity is invisible in background while keyguard is occluded 220 // (the activity window is on screen while keyguard is locked) with recents animation, 221 // the task animating by recents needs to be wallpaper target to make wallpaper visible. 222 // While for unlocked case, because recents activity will be moved to top, it can be 223 // the wallpaper target naturally. 224 return w.mActivityRecord != null && w.mAttrs.type == TYPE_BASE_APPLICATION 225 && mDisplayContent.isKeyguardLocked() 226 && w.mTransitionController.isTransientHide(w.getTask()); 227 } 228 // The window is either the recents activity or is in the task animating by the recents. 229 final RecentsAnimationController controller = mService.getRecentsAnimationController(); 230 return controller != null && controller.isWallpaperVisible(w); 231 } 232 isBackAnimationTarget(WindowState w)233 private boolean isBackAnimationTarget(WindowState w) { 234 // The window is either the back activity or is in the task animating by the back gesture. 235 final BackNaviAnimationController bthController = mService.getBackNaviAnimationController(); 236 return bthController != null && bthController.isWallpaperVisible(w); 237 } 238 239 240 /** 241 * @see #computeLastWallpaperZoomOut() 242 */ 243 private Consumer<WindowState> mComputeMaxZoomOutFunction = windowState -> { 244 if (!windowState.mIsWallpaper 245 && Float.compare(windowState.mWallpaperZoomOut, mLastWallpaperZoomOut) > 0) { 246 mLastWallpaperZoomOut = windowState.mWallpaperZoomOut; 247 } 248 }; 249 WallpaperController(WindowManagerService service, DisplayContent displayContent)250 WallpaperController(WindowManagerService service, DisplayContent displayContent) { 251 mService = service; 252 mDisplayContent = displayContent; 253 mMaxWallpaperScale = service.mContext.getResources() 254 .getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale); 255 mShouldOffsetWallpaperCenter = service.mContext.getResources() 256 .getBoolean( 257 com.android.internal.R.bool.config_offsetWallpaperToCenterOfLargestDisplay); 258 } 259 resetLargestDisplay(Display display)260 void resetLargestDisplay(Display display) { 261 if (display != null && display.getType() == Display.TYPE_INTERNAL) { 262 mLargestDisplaySize = null; 263 } 264 } 265 setShouldOffsetWallpaperCenter(boolean shouldOffset)266 @VisibleForTesting void setShouldOffsetWallpaperCenter(boolean shouldOffset) { 267 mShouldOffsetWallpaperCenter = shouldOffset; 268 } 269 findLargestDisplaySize()270 @Nullable private Point findLargestDisplaySize() { 271 if (!mShouldOffsetWallpaperCenter) { 272 return null; 273 } 274 Point largestDisplaySize = new Point(); 275 List<DisplayInfo> possibleDisplayInfo = 276 mService.getPossibleDisplayInfoLocked(DEFAULT_DISPLAY); 277 for (int i = 0; i < possibleDisplayInfo.size(); i++) { 278 DisplayInfo displayInfo = possibleDisplayInfo.get(i); 279 if (displayInfo.type == Display.TYPE_INTERNAL 280 && Math.max(displayInfo.logicalWidth, displayInfo.logicalHeight) 281 > Math.max(largestDisplaySize.x, largestDisplaySize.y)) { 282 largestDisplaySize.set(displayInfo.logicalWidth, 283 displayInfo.logicalHeight); 284 } 285 } 286 return largestDisplaySize; 287 } 288 getWallpaperTarget()289 WindowState getWallpaperTarget() { 290 return mWallpaperTarget; 291 } 292 isWallpaperTarget(WindowState win)293 boolean isWallpaperTarget(WindowState win) { 294 return win == mWallpaperTarget; 295 } 296 isBelowWallpaperTarget(WindowState win)297 boolean isBelowWallpaperTarget(WindowState win) { 298 return mWallpaperTarget != null && mWallpaperTarget.mLayer >= win.mBaseLayer; 299 } 300 isWallpaperVisible()301 boolean isWallpaperVisible() { 302 for (int i = mWallpaperTokens.size() - 1; i >= 0; --i) { 303 if (mWallpaperTokens.get(i).isVisible()) return true; 304 } 305 return false; 306 } 307 308 /** 309 * Starts {@param a} on all wallpaper windows. 310 */ startWallpaperAnimation(Animation a)311 void startWallpaperAnimation(Animation a) { 312 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 313 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 314 token.startAnimation(a); 315 } 316 } 317 shouldWallpaperBeVisible(WindowState wallpaperTarget)318 private boolean shouldWallpaperBeVisible(WindowState wallpaperTarget) { 319 if (DEBUG_WALLPAPER) { 320 Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + " prev=" 321 + mPrevWallpaperTarget); 322 } 323 return wallpaperTarget != null || mPrevWallpaperTarget != null; 324 } 325 isWallpaperTargetAnimating()326 boolean isWallpaperTargetAnimating() { 327 return mWallpaperTarget != null && mWallpaperTarget.isAnimating(TRANSITION | PARENTS) 328 && (mWallpaperTarget.mActivityRecord == null 329 || !mWallpaperTarget.mActivityRecord.isWaitingForTransitionStart()); 330 } 331 updateWallpaperVisibility()332 void updateWallpaperVisibility() { 333 final boolean visible = shouldWallpaperBeVisible(mWallpaperTarget); 334 335 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 336 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 337 token.setVisibility(visible); 338 } 339 } 340 hideDeferredWallpapersIfNeededLegacy()341 void hideDeferredWallpapersIfNeededLegacy() { 342 for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { 343 final WallpaperWindowToken token = mWallpaperTokens.get(i); 344 if (!token.isVisibleRequested()) { 345 token.commitVisibility(false); 346 } 347 } 348 } 349 hideWallpapers(final WindowState winGoingAway)350 void hideWallpapers(final WindowState winGoingAway) { 351 if (mWallpaperTarget != null 352 && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) { 353 return; 354 } 355 if (mFindResults.useTopWallpaperAsTarget) { 356 // wallpaper target is going away but there has request to use top wallpaper 357 // Keep wallpaper visible. 358 return; 359 } 360 for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) { 361 final WallpaperWindowToken token = mWallpaperTokens.get(i); 362 token.setVisibility(false); 363 if (ProtoLogImpl.isEnabled(WM_DEBUG_WALLPAPER) && token.isVisible()) { 364 ProtoLog.d(WM_DEBUG_WALLPAPER, 365 "Hiding wallpaper %s from %s target=%s prev=%s callers=%s", 366 token, winGoingAway, mWallpaperTarget, mPrevWallpaperTarget, 367 Debug.getCallers(5)); 368 } 369 } 370 } 371 updateWallpaperOffset(WindowState wallpaperWin, boolean sync)372 boolean updateWallpaperOffset(WindowState wallpaperWin, boolean sync) { 373 // Size of the display the wallpaper is rendered on. 374 final Rect lastWallpaperBounds = wallpaperWin.getParentFrame(); 375 // Full size of the wallpaper (usually larger than bounds above to parallax scroll when 376 // swiping through Launcher pages). 377 final Rect wallpaperFrame = wallpaperWin.getFrame(); 378 379 int newXOffset = 0; 380 int newYOffset = 0; 381 boolean rawChanged = false; 382 // Set the default wallpaper x-offset to either edge of the screen (depending on RTL), to 383 // match the behavior of most Launchers 384 float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f; 385 // "Wallpaper X" is the previous x-offset of the wallpaper (in a 0 to 1 scale). 386 // The 0 to 1 scale is because the "length" varies depending on how many home screens you 387 // have, so 0 is the left of the first home screen, and 1 is the right of the last one (for 388 // LTR, and the opposite for RTL). 389 float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX; 390 // "Wallpaper X step size" is how much of that 0-1 is one "page" of the home screen 391 // when scrolling. 392 float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; 393 // Difference between width of wallpaper image, and the last size of the wallpaper. 394 // This is the horizontal surplus from the prior configuration. 395 int availw = wallpaperFrame.width() - lastWallpaperBounds.width(); 396 397 int displayOffset = getDisplayWidthOffset(availw, lastWallpaperBounds, 398 wallpaperWin.isRtl()); 399 availw -= displayOffset; 400 int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0; 401 if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 402 // if device is LTR, then offset wallpaper to the left (the wallpaper is drawn 403 // always starting from the left of the screen). 404 offset += mLastWallpaperDisplayOffsetX; 405 } else if (!wallpaperWin.isRtl()) { 406 // In RTL the offset is calculated so that the wallpaper ends up right aligned (see 407 // offset above). 408 offset -= displayOffset; 409 } 410 newXOffset = offset; 411 412 if (wallpaperWin.mWallpaperX != wpx || wallpaperWin.mWallpaperXStep != wpxs) { 413 wallpaperWin.mWallpaperX = wpx; 414 wallpaperWin.mWallpaperXStep = wpxs; 415 rawChanged = true; 416 } 417 418 float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f; 419 float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f; 420 int availh = wallpaperWin.getFrame().bottom - wallpaperWin.getFrame().top 421 - lastWallpaperBounds.height(); 422 offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0; 423 if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 424 offset += mLastWallpaperDisplayOffsetY; 425 } 426 newYOffset = offset; 427 428 if (wallpaperWin.mWallpaperY != wpy || wallpaperWin.mWallpaperYStep != wpys) { 429 wallpaperWin.mWallpaperY = wpy; 430 wallpaperWin.mWallpaperYStep = wpys; 431 rawChanged = true; 432 } 433 434 if (Float.compare(wallpaperWin.mWallpaperZoomOut, mLastWallpaperZoomOut) != 0) { 435 wallpaperWin.mWallpaperZoomOut = mLastWallpaperZoomOut; 436 rawChanged = true; 437 } 438 439 boolean changed = wallpaperWin.setWallpaperOffset(newXOffset, newYOffset, 440 wallpaperWin.mShouldScaleWallpaper 441 ? zoomOutToScale(wallpaperWin.mWallpaperZoomOut) : 1); 442 443 if (rawChanged && (wallpaperWin.mAttrs.privateFlags & 444 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { 445 try { 446 if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " 447 + wallpaperWin + " x=" + wallpaperWin.mWallpaperX 448 + " y=" + wallpaperWin.mWallpaperY 449 + " zoom=" + wallpaperWin.mWallpaperZoomOut); 450 if (sync) { 451 mWaitingOnWallpaper = wallpaperWin; 452 } 453 wallpaperWin.mClient.dispatchWallpaperOffsets( 454 wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY, 455 wallpaperWin.mWallpaperXStep, wallpaperWin.mWallpaperYStep, 456 wallpaperWin.mWallpaperZoomOut, sync); 457 458 if (sync) { 459 if (mWaitingOnWallpaper != null) { 460 long start = SystemClock.uptimeMillis(); 461 if ((mLastWallpaperTimeoutTime + WALLPAPER_TIMEOUT_RECOVERY) 462 < start) { 463 try { 464 if (DEBUG_WALLPAPER) Slog.v(TAG, 465 "Waiting for offset complete..."); 466 mService.mGlobalLock.wait(WALLPAPER_TIMEOUT); 467 } catch (InterruptedException e) { 468 } 469 if (DEBUG_WALLPAPER) Slog.v(TAG, "Offset complete!"); 470 if ((start + WALLPAPER_TIMEOUT) < SystemClock.uptimeMillis()) { 471 Slog.i(TAG, "Timeout waiting for wallpaper to offset: " 472 + wallpaperWin); 473 mLastWallpaperTimeoutTime = start; 474 } 475 } 476 mWaitingOnWallpaper = null; 477 } 478 } 479 } catch (RemoteException e) { 480 } 481 } 482 483 return changed; 484 } 485 486 /** 487 * Get an extra offset if needed ({@link #mShouldOffsetWallpaperCenter} = true, typically on 488 * multiple display devices) so that the wallpaper in a smaller display ends up centered at the 489 * same position as in the largest display of the device. 490 * 491 * Note that the wallpaper has already been cropped when set by the user, so these calculations 492 * apply to the image size for the display the wallpaper was set for. 493 * 494 * @param availWidth width available for the wallpaper offset in the current display 495 * @param displayFrame size of the "display" (parent frame) 496 * @param isRtl whether we're in an RTL configuration 497 * @return an offset to apply to the width, or 0 if the current configuration doesn't require 498 * any adjustment (either @link #mShouldOffsetWallpaperCenter} is false or we're on the largest 499 * display). 500 */ getDisplayWidthOffset(int availWidth, Rect displayFrame, boolean isRtl)501 private int getDisplayWidthOffset(int availWidth, Rect displayFrame, boolean isRtl) { 502 if (!mShouldOffsetWallpaperCenter) { 503 return 0; 504 } 505 if (mLargestDisplaySize == null) { 506 mLargestDisplaySize = findLargestDisplaySize(); 507 } 508 if (mLargestDisplaySize == null) { 509 return 0; 510 } 511 // Page width is the width of a Launcher "page", for pagination when swiping right. 512 int pageWidth = displayFrame.width(); 513 // Only need offset if the current size is different from the largest display, and we're 514 // in a portrait configuration 515 if (mLargestDisplaySize.x != pageWidth && displayFrame.width() < displayFrame.height()) { 516 // The wallpaper will be scaled to fit the height of the wallpaper, so if the height 517 // of the displays are different, we need to account for that scaling when calculating 518 // the offset to the center 519 float sizeRatio = (float) displayFrame.height() / mLargestDisplaySize.y; 520 // Scale the width of the largest display to match the scale of the wallpaper size in 521 // the current display 522 int adjustedLargestWidth = Math.round(mLargestDisplaySize.x * sizeRatio); 523 // Finally, find the difference between the centers, taking into account that the 524 // size of the wallpaper frame could be smaller than the screen 525 return isRtl 526 ? adjustedLargestWidth - (adjustedLargestWidth + pageWidth) / 2 527 : Math.min(adjustedLargestWidth - pageWidth, availWidth) / 2; 528 } 529 return 0; 530 } 531 setWindowWallpaperPosition( WindowState window, float x, float y, float xStep, float yStep)532 void setWindowWallpaperPosition( 533 WindowState window, float x, float y, float xStep, float yStep) { 534 if (window.mWallpaperX != x || window.mWallpaperY != y) { 535 window.mWallpaperX = x; 536 window.mWallpaperY = y; 537 window.mWallpaperXStep = xStep; 538 window.mWallpaperYStep = yStep; 539 updateWallpaperOffsetLocked(window, true); 540 } 541 } 542 setWallpaperZoomOut(WindowState window, float zoom)543 void setWallpaperZoomOut(WindowState window, float zoom) { 544 if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) { 545 window.mWallpaperZoomOut = zoom; 546 mShouldUpdateZoom = true; 547 updateWallpaperOffsetLocked(window, false); 548 } 549 } 550 setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom)551 void setShouldZoomOutWallpaper(WindowState window, boolean shouldZoom) { 552 if (shouldZoom != window.mShouldScaleWallpaper) { 553 window.mShouldScaleWallpaper = shouldZoom; 554 updateWallpaperOffsetLocked(window, false); 555 } 556 } 557 setWindowWallpaperDisplayOffset(WindowState window, int x, int y)558 void setWindowWallpaperDisplayOffset(WindowState window, int x, int y) { 559 if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) { 560 window.mWallpaperDisplayOffsetX = x; 561 window.mWallpaperDisplayOffsetY = y; 562 updateWallpaperOffsetLocked(window, true); 563 } 564 } 565 sendWindowWallpaperCommand( WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync)566 Bundle sendWindowWallpaperCommand( 567 WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) { 568 if (window == mWallpaperTarget || window == mPrevWallpaperTarget) { 569 sendWindowWallpaperCommand(action, x, y, z, extras, sync); 570 } 571 572 return null; 573 } 574 sendWindowWallpaperCommand( String action, int x, int y, int z, Bundle extras, boolean sync)575 private void sendWindowWallpaperCommand( 576 String action, int x, int y, int z, Bundle extras, boolean sync) { 577 boolean doWait = sync; 578 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 579 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 580 token.sendWindowWallpaperCommand(action, x, y, z, extras, sync); 581 } 582 583 if (doWait) { 584 // TODO: Need to wait for result. 585 } 586 } 587 updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync)588 private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) { 589 WindowState target = mWallpaperTarget; 590 if (target == null && changingTarget.mToken.isVisible() 591 && changingTarget.mTransitionController.inTransition()) { 592 // If the wallpaper target was cleared during transition, still allows the visible 593 // window which may have been requested to be invisible to update the offset, e.g. 594 // zoom effect from home. 595 target = changingTarget; 596 } 597 if (target != null) { 598 if (target.mWallpaperX >= 0) { 599 mLastWallpaperX = target.mWallpaperX; 600 } else if (changingTarget.mWallpaperX >= 0) { 601 mLastWallpaperX = changingTarget.mWallpaperX; 602 } 603 if (target.mWallpaperY >= 0) { 604 mLastWallpaperY = target.mWallpaperY; 605 } else if (changingTarget.mWallpaperY >= 0) { 606 mLastWallpaperY = changingTarget.mWallpaperY; 607 } 608 computeLastWallpaperZoomOut(); 609 if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 610 mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX; 611 } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 612 mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX; 613 } 614 if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 615 mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY; 616 } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 617 mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY; 618 } 619 if (target.mWallpaperXStep >= 0) { 620 mLastWallpaperXStep = target.mWallpaperXStep; 621 } else if (changingTarget.mWallpaperXStep >= 0) { 622 mLastWallpaperXStep = changingTarget.mWallpaperXStep; 623 } 624 if (target.mWallpaperYStep >= 0) { 625 mLastWallpaperYStep = target.mWallpaperYStep; 626 } else if (changingTarget.mWallpaperYStep >= 0) { 627 mLastWallpaperYStep = changingTarget.mWallpaperYStep; 628 } 629 } 630 631 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 632 mWallpaperTokens.get(curTokenNdx).updateWallpaperOffset(sync); 633 } 634 } 635 clearLastWallpaperTimeoutTime()636 void clearLastWallpaperTimeoutTime() { 637 mLastWallpaperTimeoutTime = 0; 638 } 639 wallpaperCommandComplete(IBinder window)640 void wallpaperCommandComplete(IBinder window) { 641 if (mWaitingOnWallpaper != null && 642 mWaitingOnWallpaper.mClient.asBinder() == window) { 643 mWaitingOnWallpaper = null; 644 mService.mGlobalLock.notifyAll(); 645 } 646 } 647 wallpaperOffsetsComplete(IBinder window)648 void wallpaperOffsetsComplete(IBinder window) { 649 if (mWaitingOnWallpaper != null && 650 mWaitingOnWallpaper.mClient.asBinder() == window) { 651 mWaitingOnWallpaper = null; 652 mService.mGlobalLock.notifyAll(); 653 } 654 } 655 findWallpaperTarget()656 private void findWallpaperTarget() { 657 mFindResults.reset(); 658 if (mDisplayContent.getDefaultTaskDisplayArea() 659 .isRootTaskVisible(WINDOWING_MODE_FREEFORM)) { 660 // In freeform mode we set the wallpaper as its own target, so we don't need an 661 // additional window to make it visible. 662 mFindResults.setUseTopWallpaperAsTarget(true); 663 } 664 665 mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */); 666 667 if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) { 668 mFindResults.setWallpaperTarget(mFindResults.topWallpaper); 669 } 670 } 671 isFullscreen(WindowManager.LayoutParams attrs)672 private boolean isFullscreen(WindowManager.LayoutParams attrs) { 673 return attrs.x == 0 && attrs.y == 0 674 && attrs.width == MATCH_PARENT && attrs.height == MATCH_PARENT; 675 } 676 677 /** Updates the target wallpaper if needed and returns true if an update happened. */ updateWallpaperWindowsTarget(FindWallpaperTargetResult result)678 private void updateWallpaperWindowsTarget(FindWallpaperTargetResult result) { 679 680 WindowState wallpaperTarget = result.wallpaperTarget; 681 682 if (mWallpaperTarget == wallpaperTarget 683 || (mPrevWallpaperTarget != null && mPrevWallpaperTarget == wallpaperTarget)) { 684 685 if (mPrevWallpaperTarget == null) { 686 return; 687 } 688 689 // Is it time to stop animating? 690 if (!mPrevWallpaperTarget.isAnimatingLw()) { 691 ProtoLog.v(WM_DEBUG_WALLPAPER, "No longer animating wallpaper targets!"); 692 mPrevWallpaperTarget = null; 693 mWallpaperTarget = wallpaperTarget; 694 } 695 return; 696 } 697 698 ProtoLog.v(WM_DEBUG_WALLPAPER, "New wallpaper target: %s prevTarget: %s caller=%s", 699 wallpaperTarget, mWallpaperTarget, Debug.getCallers(5)); 700 701 mPrevWallpaperTarget = null; 702 703 final WindowState prevWallpaperTarget = mWallpaperTarget; 704 mWallpaperTarget = wallpaperTarget; 705 706 if (prevWallpaperTarget == null && wallpaperTarget != null) { 707 updateWallpaperOffsetLocked(mWallpaperTarget, false); 708 } 709 if (wallpaperTarget == null || prevWallpaperTarget == null) { 710 return; 711 } 712 713 // Now what is happening... if the current and new targets are animating, 714 // then we are in our super special mode! 715 boolean oldAnim = prevWallpaperTarget.isAnimatingLw(); 716 boolean foundAnim = wallpaperTarget.isAnimatingLw(); 717 ProtoLog.v(WM_DEBUG_WALLPAPER, "New animation: %s old animation: %s", 718 foundAnim, oldAnim); 719 720 if (!foundAnim || !oldAnim) { 721 return; 722 } 723 724 if (mDisplayContent.getWindow(w -> w == prevWallpaperTarget) == null) { 725 return; 726 } 727 728 final boolean newTargetHidden = wallpaperTarget.mActivityRecord != null 729 && !wallpaperTarget.mActivityRecord.isVisibleRequested(); 730 final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null 731 && !prevWallpaperTarget.mActivityRecord.isVisibleRequested(); 732 733 ProtoLog.v(WM_DEBUG_WALLPAPER, "Animating wallpapers: " 734 + "old: %s hidden=%b new: %s hidden=%b", 735 prevWallpaperTarget, oldTargetHidden, wallpaperTarget, newTargetHidden); 736 737 mPrevWallpaperTarget = prevWallpaperTarget; 738 739 if (newTargetHidden && !oldTargetHidden) { 740 ProtoLog.v(WM_DEBUG_WALLPAPER, "Old wallpaper still the target."); 741 // Use the old target if new target is hidden but old target 742 // is not. If they're both hidden, still use the new target. 743 mWallpaperTarget = prevWallpaperTarget; 744 } else if (newTargetHidden == oldTargetHidden 745 && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mActivityRecord) 746 && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mActivityRecord) 747 || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mActivityRecord))) { 748 // If they're both hidden (or both not hidden), prefer the one that's currently in 749 // opening or closing app list, this allows transition selection logic to better 750 // determine the wallpaper status of opening/closing apps. 751 mWallpaperTarget = prevWallpaperTarget; 752 } 753 754 result.setWallpaperTarget(wallpaperTarget); 755 } 756 updateWallpaperTokens(boolean visible)757 private void updateWallpaperTokens(boolean visible) { 758 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 759 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 760 if (token.updateWallpaperWindows(visible)) { 761 token.mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */); 762 } 763 } 764 } 765 adjustWallpaperWindows()766 void adjustWallpaperWindows() { 767 mDisplayContent.mWallpaperMayChange = false; 768 769 // First find top-most window that has asked to be on top of the wallpaper; 770 // all wallpapers go behind it. 771 findWallpaperTarget(); 772 updateWallpaperWindowsTarget(mFindResults); 773 774 // The window is visible to the compositor...but is it visible to the user? 775 // That is what the wallpaper cares about. 776 final boolean visible = mWallpaperTarget != null; 777 if (DEBUG_WALLPAPER) { 778 Slog.v(TAG, "Wallpaper visibility: " + visible + " at display " 779 + mDisplayContent.getDisplayId()); 780 } 781 782 if (visible) { 783 if (mWallpaperTarget.mWallpaperX >= 0) { 784 mLastWallpaperX = mWallpaperTarget.mWallpaperX; 785 mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; 786 } 787 computeLastWallpaperZoomOut(); 788 if (mWallpaperTarget.mWallpaperY >= 0) { 789 mLastWallpaperY = mWallpaperTarget.mWallpaperY; 790 mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; 791 } 792 if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { 793 mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX; 794 } 795 if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 796 mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY; 797 } 798 } 799 800 updateWallpaperTokens(visible); 801 802 if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) { 803 mLastFrozen = mFindResults.isWallpaperTargetForLetterbox; 804 sendWindowWallpaperCommand( 805 mFindResults.isWallpaperTargetForLetterbox ? COMMAND_FREEZE : COMMAND_UNFREEZE, 806 /* x= */ 0, /* y= */ 0, /* z= */ 0, /* extras= */ null, /* sync= */ false); 807 } 808 809 ProtoLog.d(WM_DEBUG_WALLPAPER, "New wallpaper: target=%s prev=%s", 810 mWallpaperTarget, mPrevWallpaperTarget); 811 } 812 processWallpaperDrawPendingTimeout()813 boolean processWallpaperDrawPendingTimeout() { 814 if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) { 815 mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT; 816 if (DEBUG_WALLPAPER) { 817 Slog.v(TAG, "*** WALLPAPER DRAW TIMEOUT"); 818 } 819 820 // If there was a pending recents animation, start the animation anyways (it's better 821 // to not see the wallpaper than for the animation to not start) 822 if (mService.getRecentsAnimationController() != null) { 823 mService.getRecentsAnimationController().startAnimation(); 824 } 825 return true; 826 } 827 return false; 828 } 829 wallpaperTransitionReady()830 boolean wallpaperTransitionReady() { 831 boolean transitionReady = true; 832 boolean wallpaperReady = true; 833 for (int curTokenIndex = mWallpaperTokens.size() - 1; 834 curTokenIndex >= 0 && wallpaperReady; curTokenIndex--) { 835 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenIndex); 836 if (token.hasVisibleNotDrawnWallpaper()) { 837 // We've told this wallpaper to be visible, but it is not drawn yet 838 wallpaperReady = false; 839 if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) { 840 // wait for this wallpaper until it is drawn or timeout 841 transitionReady = false; 842 } 843 if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) { 844 mWallpaperDrawState = WALLPAPER_DRAW_PENDING; 845 mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this); 846 mService.mH.sendMessageDelayed( 847 mService.mH.obtainMessage(WALLPAPER_DRAW_PENDING_TIMEOUT, this), 848 WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION); 849 850 } 851 if (DEBUG_WALLPAPER) { 852 Slog.v(TAG, 853 "Wallpaper should be visible but has not been drawn yet. " 854 + "mWallpaperDrawState=" + mWallpaperDrawState); 855 } 856 break; 857 } 858 } 859 if (wallpaperReady) { 860 mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; 861 mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this); 862 } 863 864 return transitionReady; 865 } 866 867 /** 868 * Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of 869 * the opening apps should be a wallpaper target. 870 */ adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps)871 void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps) { 872 boolean adjust = false; 873 if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) { 874 adjust = true; 875 } else { 876 for (int i = openingApps.size() - 1; i >= 0; --i) { 877 final ActivityRecord activity = openingApps.valueAt(i); 878 if (activity.windowsCanBeWallpaperTarget()) { 879 adjust = true; 880 break; 881 } 882 } 883 } 884 885 if (adjust) { 886 adjustWallpaperWindows(); 887 } 888 } 889 addWallpaperToken(WallpaperWindowToken token)890 void addWallpaperToken(WallpaperWindowToken token) { 891 mWallpaperTokens.add(token); 892 } 893 removeWallpaperToken(WallpaperWindowToken token)894 void removeWallpaperToken(WallpaperWindowToken token) { 895 mWallpaperTokens.remove(token); 896 } 897 898 899 @VisibleForTesting canScreenshotWallpaper()900 boolean canScreenshotWallpaper() { 901 return canScreenshotWallpaper(getTopVisibleWallpaper()); 902 } 903 canScreenshotWallpaper(WindowState wallpaperWindowState)904 private boolean canScreenshotWallpaper(WindowState wallpaperWindowState) { 905 if (!mService.mPolicy.isScreenOn()) { 906 if (DEBUG_SCREENSHOT) { 907 Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); 908 } 909 return false; 910 } 911 912 if (wallpaperWindowState == null) { 913 if (DEBUG_SCREENSHOT) { 914 Slog.i(TAG_WM, "No visible wallpaper to screenshot"); 915 } 916 return false; 917 } 918 return true; 919 } 920 921 /** 922 * Take a screenshot of the wallpaper if it's visible. 923 * 924 * @return Bitmap of the wallpaper 925 */ screenshotWallpaperLocked()926 Bitmap screenshotWallpaperLocked() { 927 final WindowState wallpaperWindowState = getTopVisibleWallpaper(); 928 if (!canScreenshotWallpaper(wallpaperWindowState)) { 929 return null; 930 } 931 932 final Rect bounds = wallpaperWindowState.getBounds(); 933 bounds.offsetTo(0, 0); 934 935 SurfaceControl.ScreenshotHardwareBuffer wallpaperBuffer = SurfaceControl.captureLayers( 936 wallpaperWindowState.getSurfaceControl(), bounds, 1 /* frameScale */); 937 938 if (wallpaperBuffer == null) { 939 Slog.w(TAG_WM, "Failed to screenshot wallpaper"); 940 return null; 941 } 942 return Bitmap.wrapHardwareBuffer( 943 wallpaperBuffer.getHardwareBuffer(), wallpaperBuffer.getColorSpace()); 944 } 945 946 /** 947 * Mirrors the visible wallpaper if it's available. 948 * 949 * @return A SurfaceControl for the parent of the mirrored wallpaper. 950 */ mirrorWallpaperSurface()951 SurfaceControl mirrorWallpaperSurface() { 952 final WindowState wallpaperWindowState = getTopVisibleWallpaper(); 953 return wallpaperWindowState != null 954 ? SurfaceControl.mirrorSurface(wallpaperWindowState.getSurfaceControl()) 955 : null; 956 } 957 getTopVisibleWallpaper()958 WindowState getTopVisibleWallpaper() { 959 for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) { 960 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx); 961 for (int i = token.getChildCount() - 1; i >= 0; i--) { 962 final WindowState w = token.getChildAt(i); 963 if (w.mWinAnimator.getShown() && w.mWinAnimator.mLastAlpha > 0f) { 964 return w; 965 } 966 } 967 } 968 return null; 969 } 970 971 /** 972 * Each window can request a zoom, example: 973 * - User is in overview, zoomed out. 974 * - User also pulls down the shade. 975 * 976 * This means that we always have to choose the largest zoom out that we have, otherwise 977 * we'll have conflicts and break the "depth system" mental model. 978 */ computeLastWallpaperZoomOut()979 private void computeLastWallpaperZoomOut() { 980 if (mShouldUpdateZoom) { 981 mLastWallpaperZoomOut = 0; 982 mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true); 983 mShouldUpdateZoom = false; 984 } 985 } 986 zoomOutToScale(float zoom)987 private float zoomOutToScale(float zoom) { 988 return MathUtils.lerp(1, mMaxWallpaperScale, 1 - zoom); 989 } 990 dump(PrintWriter pw, String prefix)991 void dump(PrintWriter pw, String prefix) { 992 pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId()); 993 pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget); 994 if (mPrevWallpaperTarget != null) { 995 pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget); 996 } 997 pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX); 998 pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY); 999 if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE 1000 || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) { 1001 pw.print(prefix); 1002 pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX); 1003 pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY); 1004 } 1005 } 1006 1007 /** Helper class for storing the results of a wallpaper target find operation. */ 1008 final private static class FindWallpaperTargetResult { 1009 WindowState topWallpaper = null; 1010 boolean useTopWallpaperAsTarget = false; 1011 WindowState wallpaperTarget = null; 1012 boolean resetTopWallpaper = false; 1013 boolean isWallpaperTargetForLetterbox = false; 1014 setTopWallpaper(WindowState win)1015 void setTopWallpaper(WindowState win) { 1016 topWallpaper = win; 1017 } 1018 setWallpaperTarget(WindowState win)1019 void setWallpaperTarget(WindowState win) { 1020 wallpaperTarget = win; 1021 } 1022 setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget)1023 void setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget) { 1024 useTopWallpaperAsTarget = topWallpaperAsTarget; 1025 } 1026 setIsWallpaperTargetForLetterbox(boolean isWallpaperTargetForLetterbox)1027 void setIsWallpaperTargetForLetterbox(boolean isWallpaperTargetForLetterbox) { 1028 this.isWallpaperTargetForLetterbox = isWallpaperTargetForLetterbox; 1029 } 1030 reset()1031 void reset() { 1032 topWallpaper = null; 1033 wallpaperTarget = null; 1034 useTopWallpaperAsTarget = false; 1035 resetTopWallpaper = false; 1036 isWallpaperTargetForLetterbox = false; 1037 } 1038 } 1039 } 1040