Merge "Freeze live wallpaper for letterbox background." into sc-v2-dev am: 809e26fc6a
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15360874 Change-Id: I142561c76775ee52f2ffe898b97bbc495db8790b
This commit is contained in:
@@ -220,6 +220,20 @@ public class WallpaperManager {
|
||||
*/
|
||||
public static final String COMMAND_REAPPLY = "android.wallpaper.reapply";
|
||||
|
||||
/**
|
||||
* Command for {@link #sendWallpaperCommand}: reported when the live wallpaper needs to be
|
||||
* frozen.
|
||||
* @hide
|
||||
*/
|
||||
public static final String COMMAND_FREEZE = "android.wallpaper.freeze";
|
||||
|
||||
/**
|
||||
* Command for {@link #sendWallpaperCommand}: reported when the live wallapper doesn't need
|
||||
* to be frozen anymore.
|
||||
* @hide
|
||||
*/
|
||||
public static final String COMMAND_UNFREEZE = "android.wallpaper.unfreeze";
|
||||
|
||||
/**
|
||||
* Extra passed back from setWallpaper() giving the new wallpaper's assigned ID.
|
||||
* @hide
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.service.wallpaper;
|
||||
|
||||
import static android.app.WallpaperManager.COMMAND_FREEZE;
|
||||
import static android.app.WallpaperManager.COMMAND_UNFREEZE;
|
||||
import static android.graphics.Matrix.MSCALE_X;
|
||||
import static android.graphics.Matrix.MSCALE_Y;
|
||||
import static android.graphics.Matrix.MSKEW_X;
|
||||
@@ -42,12 +44,14 @@ import android.content.res.TypedArray;
|
||||
import android.graphics.BLASTBufferQueue;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManager.DisplayListener;
|
||||
import android.os.Build;
|
||||
@@ -56,6 +60,7 @@ import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
@@ -200,6 +205,12 @@ public abstract class WallpaperService extends Service {
|
||||
boolean mVisible;
|
||||
boolean mReportedVisible;
|
||||
boolean mDestroyed;
|
||||
// Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false
|
||||
// after receiving WallpaperManager#COMMAND_UNFREEZE. COMMAND_FREEZE is fully applied once
|
||||
// mScreenshotSurfaceControl isn't null. When this happens, then Engine is notified through
|
||||
// doVisibilityChanged that main wallpaper surface is no longer visible and the wallpaper
|
||||
// host receives onVisibilityChanged(false) callback.
|
||||
private boolean mFrozenRequested = false;
|
||||
|
||||
// Current window state.
|
||||
boolean mCreated;
|
||||
@@ -264,6 +275,8 @@ public abstract class WallpaperService extends Service {
|
||||
SurfaceControl mSurfaceControl = new SurfaceControl();
|
||||
SurfaceControl mBbqSurfaceControl;
|
||||
BLASTBufferQueue mBlastBufferQueue;
|
||||
private SurfaceControl mScreenshotSurfaceControl;
|
||||
private Point mScreenshotSize = new Point();
|
||||
|
||||
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
|
||||
{
|
||||
@@ -1349,11 +1362,15 @@ public abstract class WallpaperService extends Service {
|
||||
if (!mDestroyed) {
|
||||
mVisible = visible;
|
||||
reportVisibility();
|
||||
if (visible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
|
||||
if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
|
||||
}
|
||||
}
|
||||
|
||||
void reportVisibility() {
|
||||
if (mScreenshotSurfaceControl != null && mVisible) {
|
||||
if (DEBUG) Log.v(TAG, "Frozen so don't report visibility change");
|
||||
return;
|
||||
}
|
||||
if (!mDestroyed) {
|
||||
mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState();
|
||||
boolean visible = mVisible && mDisplayState != Display.STATE_OFF;
|
||||
@@ -1370,6 +1387,10 @@ public abstract class WallpaperService extends Service {
|
||||
updateSurface(true, false, false);
|
||||
}
|
||||
onVisibilityChanged(visible);
|
||||
if (mReportedVisible && mFrozenRequested) {
|
||||
if (DEBUG) Log.v(TAG, "Freezing wallpaper after visibility update");
|
||||
freeze();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1830,6 +1851,9 @@ public abstract class WallpaperService extends Service {
|
||||
void doCommand(WallpaperCommand cmd) {
|
||||
Bundle result;
|
||||
if (!mDestroyed) {
|
||||
if (COMMAND_FREEZE.equals(cmd.action) || COMMAND_UNFREEZE.equals(cmd.action)) {
|
||||
updateFrozenState(/* frozenRequested= */ !COMMAND_UNFREEZE.equals(cmd.action));
|
||||
}
|
||||
result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
|
||||
cmd.extras, cmd.sync);
|
||||
} else {
|
||||
@@ -1844,6 +1868,159 @@ public abstract class WallpaperService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFrozenState(boolean frozenRequested) {
|
||||
if (mIWallpaperEngine.mWallpaperManager.getWallpaperInfo() == null
|
||||
// Procees the unfreeze command in case the wallaper became static while
|
||||
// being paused.
|
||||
&& frozenRequested) {
|
||||
if (DEBUG) Log.v(TAG, "Ignoring the freeze command for static wallpapers");
|
||||
return;
|
||||
}
|
||||
mFrozenRequested = frozenRequested;
|
||||
boolean isFrozen = mScreenshotSurfaceControl != null;
|
||||
if (mFrozenRequested == isFrozen) {
|
||||
return;
|
||||
}
|
||||
if (mFrozenRequested) {
|
||||
freeze();
|
||||
} else {
|
||||
unfreeze();
|
||||
}
|
||||
}
|
||||
|
||||
private void freeze() {
|
||||
if (!mReportedVisible || mDestroyed) {
|
||||
// Screenshot can't be taken until visibility is reported to the wallpaper host.
|
||||
return;
|
||||
}
|
||||
if (!showScreenshotOfWallpaper()) {
|
||||
return;
|
||||
}
|
||||
// Prevent a wallpaper host from rendering wallpaper behind a screeshot.
|
||||
doVisibilityChanged(false);
|
||||
// Remember that visibility is requested since it's not guaranteed that
|
||||
// mWindow#dispatchAppVisibility will be called when letterboxed application with
|
||||
// wallpaper background transitions to the Home screen.
|
||||
mVisible = true;
|
||||
}
|
||||
|
||||
private void unfreeze() {
|
||||
cleanUpScreenshotSurfaceControl();
|
||||
if (mVisible) {
|
||||
doVisibilityChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUpScreenshotSurfaceControl() {
|
||||
// TODO(b/194399558): Add crossfade transition.
|
||||
if (mScreenshotSurfaceControl != null) {
|
||||
new SurfaceControl.Transaction()
|
||||
.remove(mScreenshotSurfaceControl)
|
||||
.show(mBbqSurfaceControl)
|
||||
.apply();
|
||||
mScreenshotSurfaceControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
void scaleAndCropScreenshot() {
|
||||
if (mScreenshotSurfaceControl == null) {
|
||||
return;
|
||||
}
|
||||
if (mScreenshotSize.x <= 0 || mScreenshotSize.y <= 0) {
|
||||
Log.w(TAG, "Unexpected screenshot size: " + mScreenshotSize);
|
||||
return;
|
||||
}
|
||||
// Don't scale down and using the same scaling factor for both dimensions to
|
||||
// avoid stretching wallpaper image.
|
||||
float scaleFactor = Math.max(1, Math.max(
|
||||
((float) mSurfaceSize.x) / mScreenshotSize.x,
|
||||
((float) mSurfaceSize.y) / mScreenshotSize.y));
|
||||
int diffX = ((int) (mScreenshotSize.x * scaleFactor)) - mSurfaceSize.x;
|
||||
int diffY = ((int) (mScreenshotSize.y * scaleFactor)) - mSurfaceSize.y;
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "Adjusting screenshot: scaleFactor=" + scaleFactor
|
||||
+ " diffX=" + diffX + " diffY=" + diffY + " mSurfaceSize=" + mSurfaceSize
|
||||
+ " mScreenshotSize=" + mScreenshotSize);
|
||||
}
|
||||
new SurfaceControl.Transaction()
|
||||
.setMatrix(
|
||||
mScreenshotSurfaceControl,
|
||||
/* dsdx= */ scaleFactor, /* dtdx= */ 0,
|
||||
/* dtdy= */ 0, /* dsdy= */ scaleFactor)
|
||||
.setWindowCrop(
|
||||
mScreenshotSurfaceControl,
|
||||
new Rect(
|
||||
/* left= */ diffX / 2,
|
||||
/* top= */ diffY / 2,
|
||||
/* right= */ diffX / 2 + mScreenshotSize.x,
|
||||
/* bottom= */ diffY / 2 + mScreenshotSize.y))
|
||||
.setPosition(mScreenshotSurfaceControl, -diffX / 2, -diffY / 2)
|
||||
.apply();
|
||||
}
|
||||
|
||||
private boolean showScreenshotOfWallpaper() {
|
||||
if (mDestroyed || mSurfaceControl == null || !mSurfaceControl.isValid()) {
|
||||
if (DEBUG) Log.v(TAG, "Failed to screenshot wallpaper: surface isn't valid");
|
||||
return false;
|
||||
}
|
||||
|
||||
final Rect bounds = new Rect(0, 0, mSurfaceSize.x, mSurfaceSize.y);
|
||||
if (bounds.isEmpty()) {
|
||||
Log.w(TAG, "Failed to screenshot wallpaper: surface bounds are empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mScreenshotSurfaceControl != null) {
|
||||
Log.e(TAG, "Screenshot is unexpectedly not null");
|
||||
// Destroying previous screenshot since it can have different size.
|
||||
cleanUpScreenshotSurfaceControl();
|
||||
}
|
||||
|
||||
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
|
||||
SurfaceControl.captureLayers(
|
||||
new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
|
||||
// Needed because SurfaceFlinger#validateScreenshotPermissions
|
||||
// uses this parameter to check whether a caller only attempts
|
||||
// to screenshot itself when call doesn't come from the system.
|
||||
.setUid(Process.myUid())
|
||||
.setChildrenOnly(false)
|
||||
.setSourceCrop(bounds)
|
||||
.build());
|
||||
|
||||
if (screenshotBuffer == null) {
|
||||
Log.w(TAG, "Failed to screenshot wallpaper: screenshotBuffer is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
|
||||
|
||||
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
|
||||
// TODO(b/194399558): Add crossfade transition.
|
||||
mScreenshotSurfaceControl = new SurfaceControl.Builder()
|
||||
.setName("Wallpaper snapshot for engine " + this)
|
||||
.setFormat(hardwareBuffer.getFormat())
|
||||
.setParent(mSurfaceControl)
|
||||
.setSecure(screenshotBuffer.containsSecureLayers())
|
||||
.setCallsite("WallpaperService.Engine.showScreenshotOfWallpaper")
|
||||
.setBLASTLayer()
|
||||
.build();
|
||||
|
||||
mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y);
|
||||
|
||||
GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(hardwareBuffer);
|
||||
|
||||
t.setBuffer(mScreenshotSurfaceControl, graphicBuffer);
|
||||
t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace());
|
||||
// Place on top everything else.
|
||||
t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE);
|
||||
t.show(mScreenshotSurfaceControl);
|
||||
t.hide(mBbqSurfaceControl);
|
||||
t.apply();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reportSurfaceDestroyed() {
|
||||
if (mSurfaceCreated) {
|
||||
mSurfaceCreated = false;
|
||||
@@ -2166,6 +2343,7 @@ public abstract class WallpaperService extends Service {
|
||||
final boolean reportDraw = message.arg1 != 0;
|
||||
mEngine.updateSurface(true, false, reportDraw);
|
||||
mEngine.doOffsetsChanged(true);
|
||||
mEngine.scaleAndCropScreenshot();
|
||||
} break;
|
||||
case MSG_WINDOW_MOVED: {
|
||||
// Do nothing. What does it mean for a Wallpaper to move?
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.app.WallpaperManager.COMMAND_FREEZE;
|
||||
import static android.app.WallpaperManager.COMMAND_UNFREEZE;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
|
||||
@@ -79,6 +81,8 @@ class WallpaperController {
|
||||
private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
|
||||
private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
|
||||
private final float mMaxWallpaperScale;
|
||||
// Whether COMMAND_FREEZE was dispatched.
|
||||
private boolean mLastFrozen = false;
|
||||
|
||||
// This is set when we are waiting for a wallpaper to tell us it is done
|
||||
// changing its scroll position.
|
||||
@@ -194,6 +198,7 @@ class WallpaperController {
|
||||
if (DEBUG_WALLPAPER) Slog.v(TAG,
|
||||
"Win " + w + ": token animating, looking behind.");
|
||||
}
|
||||
mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
|
||||
// Found a target! End search.
|
||||
return true;
|
||||
}
|
||||
@@ -424,20 +429,25 @@ class WallpaperController {
|
||||
Bundle sendWindowWallpaperCommand(
|
||||
WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
|
||||
if (window == mWallpaperTarget || window == mPrevWallpaperTarget) {
|
||||
boolean doWait = sync;
|
||||
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
|
||||
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
|
||||
token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
|
||||
}
|
||||
|
||||
if (doWait) {
|
||||
// TODO: Need to wait for result.
|
||||
}
|
||||
sendWindowWallpaperCommand(action, x, y, z, extras, sync);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void sendWindowWallpaperCommand(
|
||||
String action, int x, int y, int z, Bundle extras, boolean sync) {
|
||||
boolean doWait = sync;
|
||||
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
|
||||
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
|
||||
token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
|
||||
}
|
||||
|
||||
if (doWait) {
|
||||
// TODO: Need to wait for result.
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
|
||||
WindowState target = mWallpaperTarget;
|
||||
if (target != null) {
|
||||
@@ -641,6 +651,13 @@ class WallpaperController {
|
||||
|
||||
updateWallpaperTokens(visible);
|
||||
|
||||
if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) {
|
||||
mLastFrozen = mFindResults.isWallpaperTargetForLetterbox;
|
||||
sendWindowWallpaperCommand(
|
||||
mFindResults.isWallpaperTargetForLetterbox ? COMMAND_FREEZE : COMMAND_UNFREEZE,
|
||||
/* x= */ 0, /* y= */ 0, /* z= */ 0, /* extras= */ null, /* sync= */ false);
|
||||
}
|
||||
|
||||
if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
|
||||
+ " prev=" + mPrevWallpaperTarget);
|
||||
}
|
||||
@@ -838,6 +855,7 @@ class WallpaperController {
|
||||
boolean useTopWallpaperAsTarget = false;
|
||||
WindowState wallpaperTarget = null;
|
||||
boolean resetTopWallpaper = false;
|
||||
boolean isWallpaperTargetForLetterbox = false;
|
||||
|
||||
void setTopWallpaper(WindowState win) {
|
||||
topWallpaper = win;
|
||||
@@ -851,11 +869,16 @@ class WallpaperController {
|
||||
useTopWallpaperAsTarget = topWallpaperAsTarget;
|
||||
}
|
||||
|
||||
void setIsWallpaperTargetForLetterbox(boolean isWallpaperTargetForLetterbox) {
|
||||
this.isWallpaperTargetForLetterbox = isWallpaperTargetForLetterbox;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
topWallpaper = null;
|
||||
wallpaperTarget = null;
|
||||
useTopWallpaperAsTarget = false;
|
||||
resetTopWallpaper = false;
|
||||
isWallpaperTargetForLetterbox = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6043,8 +6043,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
}
|
||||
|
||||
boolean hasWallpaper() {
|
||||
return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
|
||||
|| (mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox());
|
||||
return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 || hasWallpaperForLetterboxBackground();
|
||||
}
|
||||
|
||||
boolean hasWallpaperForLetterboxBackground() {
|
||||
return mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user