Add new wallpaper features for insets and offsets.

Issue #17394151: WallpaperService / Engines need to get notified
of WindowInsets

Issue #17394203 Wallpapers need a system API to be shifted in order
to support burn in protection

Adds a new API on WallpaperManager to set additional offsets to
make wallpapers extend beyond the display size.

Insets are now reported to wallpapers, to use as they may.  This
includes information about the above offsets, so they can place
their content within the visible area.  And to help with this, also
expose the stable offsets APIs in WindowInsets which is also very
useful information for the wallpaper.

Another new API on WallpaperManager to set a raw offset to apply
to the wallpaper window, forcing it to move on the screen regardless
of what the wallpaper is drawing.

Fix wallpapers when used with overscan enabled, so they still extend
out across the entire screen.  Conveniently, the above new window
insets information is very useful for this case as well!

And a new wallpaper test app for all this stuff.

Change-Id: I287ee36581283dd34607609fcd3170d99d120d8e
This commit is contained in:
Dianne Hackborn
2014-09-07 23:14:30 -07:00
parent 8232d822aa
commit 067e5f68b9
27 changed files with 1254 additions and 50 deletions

View File

@@ -27275,6 +27275,7 @@ package android.service.wallpaper {
method public android.view.SurfaceHolder getSurfaceHolder();
method public boolean isPreview();
method public boolean isVisible();
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
method public void onCreate(android.view.SurfaceHolder);
method public void onDesiredSizeChanged(int, int);
@@ -34989,12 +34990,18 @@ package android.view {
public final class WindowInsets {
ctor public WindowInsets(android.view.WindowInsets);
method public android.view.WindowInsets consumeStableInsets();
method public android.view.WindowInsets consumeSystemWindowInsets();
method public int getStableInsetBottom();
method public int getStableInsetLeft();
method public int getStableInsetRight();
method public int getStableInsetTop();
method public int getSystemWindowInsetBottom();
method public int getSystemWindowInsetLeft();
method public int getSystemWindowInsetRight();
method public int getSystemWindowInsetTop();
method public boolean hasInsets();
method public boolean hasStableInsets();
method public boolean hasSystemWindowInsets();
method public boolean isConsumed();
method public boolean isRound();

View File

@@ -16,6 +16,7 @@
package android.app;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.app.IWallpaperManagerCallback;
@@ -72,6 +73,11 @@ interface IWallpaperManager {
*/
int getHeightHint();
/**
* Sets extra padding that we would like the wallpaper to have outside of the display.
*/
void setDisplayPadding(in Rect padding);
/**
* Returns the name of the wallpaper. Private API.
*/

View File

@@ -16,6 +16,7 @@
package android.app;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -950,6 +951,48 @@ public class WallpaperManager {
}
}
/**
* Specify extra padding that the wallpaper should have outside of the display.
* That is, the given padding supplies additional pixels the wallpaper should extend
* outside of the display itself.
* @param padding The number of pixels the wallpaper should extend beyond the display,
* on its left, top, right, and bottom sides.
* @hide
*/
@SystemApi
public void setDisplayPadding(Rect padding) {
try {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
} else {
sGlobals.mService.setDisplayPadding(padding);
}
} catch (RemoteException e) {
// Ignore
}
}
/**
* Apply a raw offset to the wallpaper window. Should only be used in
* combination with {@link #setDisplayPadding(android.graphics.Rect)} when you
* have ensured that the wallpaper will extend outside of the display area so that
* it can be moved without leaving part of the display uncovered.
* @param x The offset, in pixels, to apply to the left edge.
* @param y The offset, in pixels, to apply to the top edge.
* @hide
*/
@SystemApi
public void setDisplayOffset(IBinder windowToken, int x, int y) {
try {
//Log.v(TAG, "Sending new wallpaper display offsets from app...");
WindowManagerGlobal.getWindowSession().setWallpaperDisplayOffset(
windowToken, x, y);
//Log.v(TAG, "...app returning after sending display offset!");
} catch (RemoteException e) {
// Ignore.
}
}
/**
* Set the position of the current wallpaper within any larger space, when
* that wallpaper is visible behind the given window. The X and Y offsets

View File

@@ -16,6 +16,7 @@
package android.service.wallpaper;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.os.Bundle;
@@ -24,6 +25,7 @@ import android.os.Bundle;
*/
oneway interface IWallpaperEngine {
void setDesiredSize(int width, int height);
void setDisplayPadding(in Rect padding);
void setVisibility(boolean visible);
void dispatchPointer(in MotionEvent event);
void dispatchWallpaperCommand(String action, int x, int y,

View File

@@ -16,6 +16,7 @@
package android.service.wallpaper;
import android.graphics.Rect;
import android.service.wallpaper.IWallpaperConnection;
/**
@@ -24,5 +25,5 @@ import android.service.wallpaper.IWallpaperConnection;
oneway interface IWallpaperService {
void attach(IWallpaperConnection connection,
IBinder windowToken, int windowType, boolean isPreview,
int reqWidth, int reqHeight);
int reqWidth, int reqHeight, in Rect padding);
}

View File

@@ -16,6 +16,14 @@
package android.service.wallpaper;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.SystemProperties;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.ViewRootImpl;
import android.view.WindowInsets;
import com.android.internal.R;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;
@@ -56,6 +64,8 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
/**
* A wallpaper service is responsible for showing a live wallpaper behind
* applications that would like to sit on top of it. This service object
@@ -90,7 +100,8 @@ public abstract class WallpaperService extends Service {
private static final int DO_ATTACH = 10;
private static final int DO_DETACH = 20;
private static final int DO_SET_DESIRED_SIZE = 30;
private static final int DO_SET_DISPLAY_PADDING = 40;
private static final int MSG_UPDATE_SURFACE = 10000;
private static final int MSG_VISIBILITY_CHANGED = 10010;
private static final int MSG_WALLPAPER_OFFSETS = 10020;
@@ -150,13 +161,23 @@ public abstract class WallpaperService extends Service {
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
int mCurWindowFlags = mWindowFlags;
int mCurWindowPrivateFlags = mWindowPrivateFlags;
TypedValue mOutsetBottom;
final Rect mVisibleInsets = new Rect();
final Rect mWinFrame = new Rect();
final Rect mOverscanInsets = new Rect();
final Rect mContentInsets = new Rect();
final Rect mStableInsets = new Rect();
final Rect mDispatchedOverscanInsets = new Rect();
final Rect mDispatchedContentInsets = new Rect();
final Rect mDispatchedStableInsets = new Rect();
final Rect mFinalSystemInsets = new Rect();
final Rect mFinalStableInsets = new Rect();
final Configuration mConfiguration = new Configuration();
private boolean mIsEmulator;
private boolean mIsCircularEmulator;
private boolean mWindowIsRound;
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
IWindowSession mSession;
@@ -406,7 +427,7 @@ public abstract class WallpaperService extends Service {
*/
public void onCreate(SurfaceHolder surfaceHolder) {
}
/**
* Called right before the engine is going away. After this the
* surface will be destroyed and this Engine object is no longer
@@ -414,7 +435,7 @@ public abstract class WallpaperService extends Service {
*/
public void onDestroy() {
}
/**
* Called to inform you of the wallpaper becoming visible or
* hidden. <em>It is very important that a wallpaper only use
@@ -422,7 +443,17 @@ public abstract class WallpaperService extends Service {
*/
public void onVisibilityChanged(boolean visible) {
}
/**
* Called with the current insets that are in effect for the wallpaper.
* This gives you the part of the overall wallpaper surface that will
* generally be visible to the user (ignoring position offsets applied to it).
*
* @param insets Insets to apply.
*/
public void onApplyWindowInsets(WindowInsets insets) {
}
/**
* Called as the user performs touch-screen interaction with the
* window that is currently showing this wallpaper. Note that the
@@ -432,7 +463,7 @@ public abstract class WallpaperService extends Service {
*/
public void onTouchEvent(MotionEvent event) {
}
/**
* Called to inform you of the wallpaper's offsets changing
* within its contain, corresponding to the container's
@@ -443,7 +474,7 @@ public abstract class WallpaperService extends Service {
float xOffsetStep, float yOffsetStep,
int xPixelOffset, int yPixelOffset) {
}
/**
* Process a command that was sent to the wallpaper with
* {@link WallpaperManager#sendWallpaperCommand}.
@@ -465,14 +496,14 @@ public abstract class WallpaperService extends Service {
Bundle extras, boolean resultRequested) {
return null;
}
/**
* Called when an application has changed the desired virtual size of
* the wallpaper.
*/
public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
}
/**
* Convenience for {@link SurfaceHolder.Callback#surfaceChanged
* SurfaceHolder.Callback.surfaceChanged()}.
@@ -561,16 +592,20 @@ public abstract class WallpaperService extends Service {
if (mDestroyed) {
Log.w(TAG, "Ignoring updateSurface: destroyed");
}
boolean fixedSize = false;
int myWidth = mSurfaceHolder.getRequestedWidth();
if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
else fixedSize = true;
int myHeight = mSurfaceHolder.getRequestedHeight();
if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
else fixedSize = true;
final boolean creating = !mCreated;
final boolean surfaceCreating = !mSurfaceCreated;
final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
boolean insetsChanged = !mCreated;
final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
final boolean flagsChanged = mCurWindowFlags != mWindowFlags ||
mCurWindowPrivateFlags != mWindowPrivateFlags;
@@ -607,6 +642,32 @@ public abstract class WallpaperService extends Service {
mLayout.token = mWindowToken;
if (!mCreated) {
// Retrieve watch round and outset info
final WindowManager windowService = (WindowManager)getSystemService(
Context.WINDOW_SERVICE);
TypedArray windowStyle = obtainStyledAttributes(
com.android.internal.R.styleable.Window);
final Display display = windowService.getDefaultDisplay();
final boolean shouldUseBottomOutset =
display.getDisplayId() == Display.DEFAULT_DISPLAY;
if (shouldUseBottomOutset && windowStyle.hasValue(
R.styleable.Window_windowOutsetBottom)) {
if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
windowStyle.getValue(R.styleable.Window_windowOutsetBottom,
mOutsetBottom);
} else {
mOutsetBottom = null;
}
mWindowIsRound = getResources().getBoolean(
com.android.internal.R.bool.config_windowIsRound);
windowStyle.recycle();
// detect emulator
mIsEmulator = Build.HARDWARE.contains("goldfish");
mIsCircularEmulator = SystemProperties.getBoolean(
ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
// Add window
mLayout.type = mIWallpaperEngine.mWindowType;
mLayout.gravity = Gravity.START|Gravity.TOP;
mLayout.setTitle(WallpaperService.this.getClass().getName());
@@ -627,6 +688,11 @@ public abstract class WallpaperService extends Service {
mSurfaceHolder.mSurfaceLock.lock();
mDrawingAllowed = true;
if (!fixedSize) {
mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding);
} else {
mLayout.surfaceInsets.set(0, 0, 0, 0);
}
final int relayoutResult = mSession.relayout(
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
@@ -636,16 +702,39 @@ public abstract class WallpaperService extends Service {
+ ", frame=" + mWinFrame);
int w = mWinFrame.width();
int h = mWinFrame.height();
if (!fixedSize) {
final Rect padding = mIWallpaperEngine.mDisplayPadding;
w += padding.left + padding.right;
h += padding.top + padding.bottom;
mOverscanInsets.left += padding.left;
mOverscanInsets.top += padding.top;
mOverscanInsets.right += padding.right;
mOverscanInsets.bottom += padding.bottom;
mContentInsets.left += padding.left;
mContentInsets.top += padding.top;
mContentInsets.right += padding.right;
mContentInsets.bottom += padding.bottom;
mStableInsets.left += padding.left;
mStableInsets.top += padding.top;
mStableInsets.right += padding.right;
mStableInsets.bottom += padding.bottom;
}
if (mCurWidth != w) {
sizeChanged = true;
mCurWidth = w;
}
int h = mWinFrame.height();
if (mCurHeight != h) {
sizeChanged = true;
mCurHeight = h;
}
insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets);
insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);
mSurfaceHolder.setSurfaceFrameSize(w, h);
mSurfaceHolder.mSurfaceLock.unlock();
@@ -702,6 +791,25 @@ public abstract class WallpaperService extends Service {
}
}
if (insetsChanged) {
mDispatchedOverscanInsets.set(mOverscanInsets);
mDispatchedContentInsets.set(mContentInsets);
mDispatchedStableInsets.set(mStableInsets);
final boolean isRound = (mIsEmulator && mIsCircularEmulator)
|| mWindowIsRound;
mFinalSystemInsets.set(mDispatchedOverscanInsets);
mFinalStableInsets.set(mDispatchedStableInsets);
if (mOutsetBottom != null) {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
mFinalSystemInsets.bottom =
( (int) mOutsetBottom.getDimension(metrics) )
+ mIWallpaperEngine.mDisplayPadding.bottom;
}
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
null, mFinalStableInsets, isRound);
onApplyWindowInsets(insets);
}
if (redrawNeeded) {
onSurfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
@@ -781,7 +889,7 @@ public abstract class WallpaperService extends Service {
mReportedVisible = false;
updateSurface(false, false, false);
}
void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
if (!mDestroyed) {
if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
@@ -792,14 +900,24 @@ public abstract class WallpaperService extends Service {
doOffsetsChanged(true);
}
}
void doDisplayPaddingChanged(Rect padding) {
if (!mDestroyed) {
if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this);
if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) {
mIWallpaperEngine.mDisplayPadding.set(padding);
updateSurface(true, false, false);
}
}
}
void doVisibilityChanged(boolean visible) {
if (!mDestroyed) {
mVisible = visible;
reportVisibility();
}
}
void reportVisibility() {
if (!mDestroyed) {
boolean visible = mVisible && mScreenOn;
@@ -956,12 +1074,13 @@ public abstract class WallpaperService extends Service {
boolean mShownReported;
int mReqWidth;
int mReqHeight;
final Rect mDisplayPadding = new Rect();
Engine mEngine;
IWallpaperEngineWrapper(WallpaperService context,
IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight) {
int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
mConnection = conn;
mWindowToken = windowToken;
@@ -969,16 +1088,22 @@ public abstract class WallpaperService extends Service {
mIsPreview = isPreview;
mReqWidth = reqWidth;
mReqHeight = reqHeight;
mDisplayPadding.set(padding);
Message msg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
}
public void setDesiredSize(int width, int height) {
Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
mCaller.sendMessage(msg);
}
public void setDisplayPadding(Rect padding) {
Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding);
mCaller.sendMessage(msg);
}
public void setVisibility(boolean visible) {
Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
visible ? 1 : 0);
@@ -1041,6 +1166,9 @@ public abstract class WallpaperService extends Service {
mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
return;
}
case DO_SET_DISPLAY_PADDING: {
mEngine.doDisplayPaddingChanged((Rect) message.obj);
}
case MSG_UPDATE_SURFACE:
mEngine.updateSurface(true, false, false);
break;
@@ -1102,9 +1230,9 @@ public abstract class WallpaperService extends Service {
@Override
public void attach(IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight) {
int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
new IWallpaperEngineWrapper(mTarget, conn, windowToken,
windowType, isPreview, reqWidth, reqHeight);
windowType, isPreview, reqWidth, reqHeight, padding);
}
}

View File

@@ -177,6 +177,11 @@ interface IWindowSession {
void wallpaperOffsetsComplete(IBinder window);
/**
* Apply a raw offset to the wallpaper service when shown behind this window.
*/
void setWallpaperDisplayOffset(IBinder windowToken, int x, int y);
Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, in Bundle extras, boolean sync);

View File

@@ -121,7 +121,7 @@ public final class ViewRootImpl implements ViewParent,
private static final String PROPERTY_MEDIA_DISABLED = "config.disable_media";
// property used by emulator to determine display shape
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
public static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
/**
* Maximum time we allow the user to roll the trackball enough to generate

View File

@@ -378,35 +378,75 @@ public final class WindowInsets {
}
/**
* @hide
* Returns the top stable inset in pixels.
*
* <p>The stable inset represents the area of a full-screen window that <b>may</b> be
* partially or fully obscured by the system UI elements. This value does not change
* based on the visibility state of those elements; for example, if the status bar is
* normally shown, but temporarily hidden, the stable inset will still provide the inset
* associated with the status bar being shown.</p>
*
* @return The top stable inset
*/
public int getStableInsetTop() {
return mStableInsets.top;
}
/**
* @hide
* Returns the left stable inset in pixels.
*
* <p>The stable inset represents the area of a full-screen window that <b>may</b> be
* partially or fully obscured by the system UI elements. This value does not change
* based on the visibility state of those elements; for example, if the status bar is
* normally shown, but temporarily hidden, the stable inset will still provide the inset
* associated with the status bar being shown.</p>
*
* @return The left stable inset
*/
public int getStableInsetLeft() {
return mStableInsets.left;
}
/**
* @hide
* Returns the right stable inset in pixels.
*
* <p>The stable inset represents the area of a full-screen window that <b>may</b> be
* partially or fully obscured by the system UI elements. This value does not change
* based on the visibility state of those elements; for example, if the status bar is
* normally shown, but temporarily hidden, the stable inset will still provide the inset
* associated with the status bar being shown.</p>
*
* @return The right stable inset
*/
public int getStableInsetRight() {
return mStableInsets.right;
}
/**
* @hide
* Returns the bottom stable inset in pixels.
*
* <p>The stable inset represents the area of a full-screen window that <b>may</b> be
* partially or fully obscured by the system UI elements. This value does not change
* based on the visibility state of those elements; for example, if the status bar is
* normally shown, but temporarily hidden, the stable inset will still provide the inset
* associated with the status bar being shown.</p>
*
* @return The bottom stable inset
*/
public int getStableInsetBottom() {
return mStableInsets.bottom;
}
/**
* @hide
* Returns true if this WindowInsets has nonzero stable insets.
*
* <p>The stable inset represents the area of a full-screen window that <b>may</b> be
* partially or fully obscured by the system UI elements. This value does not change
* based on the visibility state of those elements; for example, if the status bar is
* normally shown, but temporarily hidden, the stable inset will still provide the inset
* associated with the status bar being shown.</p>
*
* @return true if any of the stable inset values are nonzero
*/
public boolean hasStableInsets() {
return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0
@@ -414,7 +454,9 @@ public final class WindowInsets {
}
/**
* @hide
* Returns a copy of this WindowInsets with the stable insets fully consumed.
*
* @return A modified copy of this WindowInsets
*/
public WindowInsets consumeStableInsets() {
final WindowInsets result = new WindowInsets(this);

View File

@@ -3533,13 +3533,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
+ mOverscanScreenHeight;
} else if (attrs.type == TYPE_WALLPAPER) {
// The wallpaper also has Real Ultimate Power.
pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft;
pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
pf.right = df.right = of.right = cf.right
= mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom
= mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
// The wallpaper also has Real Ultimate Power, but we want to tell
// it about the overscan area.
pf.left = df.left = mOverscanScreenLeft;
pf.top = df.top = mOverscanScreenTop;
pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
of.left = cf.left = mUnrestrictedScreenLeft;
of.top = cf.top = mUnrestrictedScreenTop;
of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
&& attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
@@ -3653,9 +3656,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR) {
df.left = df.top = of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
df.right = df.bottom = of.right = of.bottom = cf.right = cf.bottom
= vf.right = vf.bottom = 10000;
df.left = df.top = -10000;
df.right = df.bottom = 10000;
if (attrs.type != TYPE_WALLPAPER) {
of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
}
}
if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()

View File

@@ -42,6 +42,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -206,6 +207,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
int width = -1;
int height = -1;
final Rect padding = new Rect(0, 0, 0, 0);
WallpaperData(int userId) {
this.userId = userId;
wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
@@ -222,6 +225,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
IRemoteCallback mReply;
boolean mDimensionsChanged = false;
boolean mPaddingChanged = false;
public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
mInfo = info;
@@ -283,6 +287,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
mDimensionsChanged = false;
}
if (mPaddingChanged) {
try {
mEngine.setDisplayPadding(mWallpaper.padding);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set wallpaper padding", e);
}
mPaddingChanged = false;
}
}
}
@@ -719,6 +731,40 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
public void setDisplayPadding(Rect padding) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
synchronized (mLock) {
int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = mWallpaperMap.get(userId);
if (wallpaper == null) {
throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
}
if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
throw new IllegalArgumentException("padding must be positive: " + padding);
}
if (!padding.equals(wallpaper.padding)) {
wallpaper.padding.set(padding);
saveSettingsLocked(wallpaper);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
if (wallpaper.connection.mEngine != null) {
try {
wallpaper.connection.mEngine.setDisplayPadding(padding);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
} else if (wallpaper.connection.mService != null) {
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
wallpaper.connection.mPaddingChanged = true;
}
}
}
}
}
public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
Bundle outParams) {
synchronized (mLock) {
@@ -1006,7 +1052,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
try {
conn.mService.attach(conn, conn.mToken,
WindowManager.LayoutParams.TYPE_WALLPAPER, false,
wallpaper.width, wallpaper.height);
wallpaper.width, wallpaper.height, wallpaper.padding);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
if (!wallpaper.wallpaperUpdating) {
@@ -1055,6 +1101,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
out.startTag(null, "wp");
out.attribute(null, "width", Integer.toString(wallpaper.width));
out.attribute(null, "height", Integer.toString(wallpaper.height));
if (wallpaper.padding.left != 0) {
out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left));
}
if (wallpaper.padding.top != 0) {
out.attribute(null, "paddingTop", Integer.toString(wallpaper.padding.top));
}
if (wallpaper.padding.right != 0) {
out.attribute(null, "paddingRight", Integer.toString(wallpaper.padding.right));
}
if (wallpaper.padding.bottom != 0) {
out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom));
}
out.attribute(null, "name", wallpaper.name);
if (wallpaper.wallpaperComponent != null
&& !wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
@@ -1091,6 +1149,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
String value = parser.getAttributeValue(null, name);
if (value == null) {
return defValue;
}
return Integer.parseInt(value);
}
private void loadSettingsLocked(int userId) {
if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
@@ -1121,6 +1187,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
wallpaper.height = Integer.parseInt(parser
.getAttributeValue(null, "height"));
wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
wallpaper.name = parser.getAttributeValue(null, "name");
String comp = parser.getAttributeValue(null, "component");
wallpaper.nextWallpaperComponent = comp != null
@@ -1167,6 +1237,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
if (!success) {
wallpaper.width = -1;
wallpaper.height = -1;
wallpaper.padding.set(0, 0, 0, 0);
wallpaper.name = "";
}
@@ -1330,13 +1401,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
WallpaperData wallpaper = mWallpaperMap.valueAt(i);
pw.println(" User " + wallpaper.userId + ":");
pw.print(" mWidth=");
pw.print(wallpaper.width);
pw.print(" mHeight=");
pw.println(wallpaper.height);
pw.print(" mName=");
pw.println(wallpaper.name);
pw.print(" mWallpaperComponent=");
pw.println(wallpaper.wallpaperComponent);
pw.print(wallpaper.width);
pw.print(" mHeight=");
pw.println(wallpaper.height);
pw.print(" mPadding="); pw.println(wallpaper.padding);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
if (wallpaper.connection != null) {
WallpaperConnection conn = wallpaper.connection;
pw.print(" Wallpaper connection ");

View File

@@ -415,6 +415,18 @@ final class Session extends IWindowSession.Stub
mService.wallpaperOffsetsComplete(window);
}
public void setWallpaperDisplayOffset(IBinder window, int x, int y) {
synchronized(mService.mWindowMap) {
long ident = Binder.clearCallingIdentity();
try {
mService.setWindowWallpaperDisplayOffsetLocked(
mService.windowForClientLocked(this, window, true), x, y);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, Bundle extras, boolean sync) {
synchronized(mService.mWindowMap) {

View File

@@ -572,6 +572,8 @@ public class WindowManagerService extends IWindowManager.Stub
float mLastWallpaperY = -1;
float mLastWallpaperXStep = -1;
float mLastWallpaperYStep = -1;
int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
// This is set when we are waiting for a wallpaper to tell us it is done
// changing its scroll position.
WindowState mWaitingOnWallpaper;
@@ -1892,6 +1894,12 @@ public class WindowManagerService extends IWindowManager.Stub
mLastWallpaperY = mWallpaperTarget.mWallpaperY;
mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
}
if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
}
if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
}
}
// Start stepping backwards from here, ensuring that our wallpaper windows
@@ -2030,6 +2038,9 @@ public class WindowManagerService extends IWindowManager.Stub
float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw;
int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0;
if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetX;
}
changed = wallpaperWin.mXOffset != offset;
if (changed) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper "
@@ -2046,6 +2057,9 @@ public class WindowManagerService extends IWindowManager.Stub
float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh;
offset = availh > 0 ? -(int)(availh*wpy+.5f) : 0;
if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetY;
}
if (wallpaperWin.mYOffset != offset) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Update wallpaper "
+ wallpaperWin + " y: " + offset);
@@ -2130,6 +2144,16 @@ public class WindowManagerService extends IWindowManager.Stub
} else if (changingTarget.mWallpaperY >= 0) {
mLastWallpaperY = changingTarget.mWallpaperY;
}
if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
} else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX;
}
if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY;
} else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
}
}
int curTokenIndex = mWallpaperTokens.size();
@@ -2826,6 +2850,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
public void setWindowWallpaperDisplayOffsetLocked(WindowState window, int x, int y) {
if (window.mWallpaperDisplayOffsetX != x || window.mWallpaperDisplayOffsetY != y) {
window.mWallpaperDisplayOffsetX = x;
window.mWallpaperDisplayOffsetY = y;
updateWallpaperOffsetLocked(window, true);
}
}
public Bundle sendWindowWallpaperCommandLocked(WindowState window,
String action, int x, int y, int z, Bundle extras, boolean sync) {
if (window == mWallpaperTarget || window == mLowerWallpaperTarget
@@ -10880,6 +10912,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" mLastWallpaperX="); pw.print(mLastWallpaperX);
pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE
|| mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
pw.print(" mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
pw.print(" mLastWallpaperDisplayOffsetY=");
pw.println(mLastWallpaperDisplayOffsetY);
}
if (mInputMethodAnimLayerAdjustment != 0 ||
mWallpaperAnimLayerAdjustment != 0) {
pw.print(" mInputMethodAnimLayerAdjustment=");

View File

@@ -247,6 +247,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
float mWallpaperXStep = -1;
float mWallpaperYStep = -1;
// If a window showing a wallpaper: a raw pixel offset to forcibly apply
// to its window; if a wallpaper window: not used.
int mWallpaperDisplayOffsetX = Integer.MIN_VALUE;
int mWallpaperDisplayOffsetY = Integer.MIN_VALUE;
// Wallpaper windows: pixels offset based on above variables.
int mXOffset;
int mYOffset;
@@ -1584,6 +1589,13 @@ final class WindowState implements WindowManagerPolicy.WindowState {
pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep);
pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
}
if (mWallpaperDisplayOffsetX != Integer.MIN_VALUE
|| mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
pw.print(prefix); pw.print("mWallpaperDisplayOffsetX=");
pw.print(mWallpaperDisplayOffsetX);
pw.print(" mWallpaperDisplayOffsetY=");
pw.println(mWallpaperDisplayOffsetY);
}
}
String makeInputChannelName() {

View File

@@ -1526,8 +1526,9 @@ class WindowStateAnimator {
}
void setWallpaperOffset(RectF shownFrame) {
final int left = (int) shownFrame.left;
final int top = (int) shownFrame.top;
final LayoutParams attrs = mWin.getAttrs();
final int left = ((int) shownFrame.left) - attrs.surfaceInsets.left;
final int top = ((int) shownFrame.top) - attrs.surfaceInsets.top;
if (mSurfaceX != left || mSurfaceY != top) {
mSurfaceX = left;
mSurfaceY = top;

View File

@@ -0,0 +1,15 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := WallpaperTest
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wallpapertest" >
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
<application
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:label="@string/test_wallpaper"
android:name=".TestWallpaper"
android:permission="android.permission.BIND_WALLPAPER"
android:enabled="true">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data android:name="android.service.wallpaper"
android:resource="@xml/test_wallpaper" />
</service>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/window_background">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dimens"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/width"/>
<EditText
android:id="@+id/dimen_width"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="numberDecimal"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/height"/>
<EditText
android:id="@+id/dimen_height"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="numberDecimal"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/wallpaper_offset"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/x"/>
<EditText
android:id="@+id/walloff_x"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="numberDecimal"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/y"/>
<EditText
android:id="@+id/walloff_y"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="numberDecimal"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/padding"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/left"/>
<EditText
android:id="@+id/padding_left"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="number"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/right"/>
<EditText
android:id="@+id/padding_right"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="number"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/top"/>
<EditText
android:id="@+id/padding_top"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="number"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/bottom"/>
<EditText
android:id="@+id/padding_bottom"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="number"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/display_offset"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/x"/>
<EditText
android:id="@+id/dispoff_x"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="numberSigned"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17sp"
android:text="@string/y"/>
<EditText
android:id="@+id/dispoff_y"
android:layout_width="60sp"
android:layout_height="wrap_content"
android:inputType="numberSigned"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!--
Base application theme for API 11+. This theme completely replaces
AppBaseTheme from res/values/styles.xml on API 11+ devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Holo.Wallpaper">
<!-- API 11 theme customizations can go here. -->
</style>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!--
Base application theme for API 21+. This theme completely replaces
AppBaseTheme from BOTH res/values/styles.xml and
res/values-v11/styles.xml on API 14+ devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Material.Wallpaper">
<!-- API 14 theme customizations can go here. -->
</style>
</resources>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2014 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
<resources>
<color name="window_background">#80000000</color>
</resources>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="app_name">Wallpaper Test</string>
<string name="test_wallpaper">Test Wallpaper</string>
<string name="test_wallpaper_author">Google</string>
<string name="test_wallpaper_desc">
Test wallpaper for use with the wallpaper test app.
</string>
<string name="dimens">Dimens: </string>
<string name="width">Width: </string>
<string name="height">Height: </string>
<string name="wallpaper_offset">Wall off: </string>
<string name="x">X: </string>
<string name="y">Y: </string>
<string name="padding">Padding: </string>
<string name="left">Left: </string>
<string name="right">Right: </string>
<string name="top">Top: </string>
<string name="bottom">Bottom: </string>
<string name="display_offset">Disp off: </string>
</resources>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Wallpaper">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
</resources>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/**
* Copyright (c) 2008, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<!-- The attributes in this XML file provide configuration information -->
<!-- about the polar clock. -->
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
android:author="@string/test_wallpaper_author"
android:description="@string/test_wallpaper_desc"
android:thumbnail="@drawable/test_wallpaper_thumb" />

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.wallpapertest;
import android.app.Activity;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.WindowManager;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
WallpaperManager mWallpaperManager;
WindowManager mWindowManager;
TextView mDimenWidthView;
TextView mDimenHeightView;
TextView mWallOffXView;
TextView mWallOffYView;
TextView mPaddingLeftView;
TextView mPaddingRightView;
TextView mPaddingTopView;
TextView mPaddingBottomView;
TextView mDispOffXView;
TextView mDispOffYView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWallpaperManager = (WallpaperManager)getSystemService(Context.WALLPAPER_SERVICE);
mWindowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
mDimenWidthView = (TextView) findViewById(R.id.dimen_width);
mDimenWidthView.addTextChangedListener(mTextWatcher);
mDimenHeightView = (TextView) findViewById(R.id.dimen_height);
mDimenHeightView.addTextChangedListener(mTextWatcher);
mWallOffXView = (TextView) findViewById(R.id.walloff_x);
mWallOffXView.addTextChangedListener(mTextWatcher);
mWallOffYView = (TextView) findViewById(R.id.walloff_y);
mWallOffYView.addTextChangedListener(mTextWatcher);
mPaddingLeftView = (TextView) findViewById(R.id.padding_left);
mPaddingLeftView.addTextChangedListener(mTextWatcher);
mPaddingRightView = (TextView) findViewById(R.id.padding_right);
mPaddingRightView.addTextChangedListener(mTextWatcher);
mPaddingTopView = (TextView) findViewById(R.id.padding_top);
mPaddingTopView.addTextChangedListener(mTextWatcher);
mPaddingBottomView = (TextView) findViewById(R.id.padding_bottom);
mPaddingBottomView.addTextChangedListener(mTextWatcher);
mDispOffXView = (TextView) findViewById(R.id.dispoff_x);
mDispOffXView.addTextChangedListener(mTextWatcher);
mDispOffYView = (TextView) findViewById(R.id.dispoff_y);
mDispOffYView.addTextChangedListener(mTextWatcher);
updateDimens();
updateWallOff();
updatePadding();
updateDispOff();
}
private int loadPropIntText(TextView view, int baseVal) {
String str = view.getText().toString();
if (str != null && !TextUtils.isEmpty(str)) {
try {
float fval = Float.parseFloat(str);
return (int)(fval*baseVal);
} catch (NumberFormatException e) {
Log.i(TAG, "Bad number: " + str, e);
}
}
return baseVal;
}
private float loadFloatText(TextView view) {
String str = view.getText().toString();
if (str != null && !TextUtils.isEmpty(str)) {
try {
return Float.parseFloat(str);
} catch (NumberFormatException e) {
Log.i(TAG, "Bad number: " + str, e);
}
}
return 0;
}
private int loadIntText(TextView view) {
String str = view.getText().toString();
if (str != null && !TextUtils.isEmpty(str)) {
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
Log.i(TAG, "Bad number: " + str, e);
}
}
return 0;
}
public void updateDimens() {
Point minDims = new Point();
Point maxDims = new Point();
mWindowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
mWallpaperManager.suggestDesiredDimensions(
loadPropIntText(mDimenWidthView, maxDims.x),
loadPropIntText(mDimenHeightView, maxDims.y));
}
public void updateWallOff() {
IBinder token = getWindow().getDecorView().getWindowToken();
if (token != null) {
mWallpaperManager.setWallpaperOffsets(token, loadFloatText(mWallOffXView),
loadFloatText(mWallOffYView));
}
}
public void updatePadding() {
Rect padding = new Rect();
padding.left = loadIntText(mPaddingLeftView);
padding.top = loadIntText(mPaddingTopView);
padding.right = loadIntText(mPaddingRightView);
padding.bottom = loadIntText(mPaddingBottomView);
mWallpaperManager.setDisplayPadding(padding);
}
public void updateDispOff() {
IBinder token = getWindow().getDecorView().getWindowToken();
if (token != null) {
mWallpaperManager.setDisplayOffset(token, loadIntText(mDispOffXView),
loadIntText(mDispOffYView));
}
}
final TextWatcher mTextWatcher = new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
updateDimens();
updateWallOff();
updatePadding();
updateDispOff();
}
@Override public void afterTextChanged(Editable s) {
}
};
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.wallpapertest;
import android.service.wallpaper.WallpaperService;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Paint;
import android.graphics.Color;
import android.graphics.RectF;
import android.text.TextPaint;
import android.view.SurfaceHolder;
import android.content.res.XmlResourceParser;
import android.os.Handler;
import android.util.Log;
import android.view.WindowInsets;
public class TestWallpaper extends WallpaperService {
private static final String LOG_TAG = "PolarClock";
private final Handler mHandler = new Handler();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
public Engine onCreateEngine() {
return new ClockEngine();
}
class ClockEngine extends Engine {
private static final int OUTER_COLOR = 0xffff0000;
private static final int INNER_COLOR = 0xff000080;
private static final int STABLE_COLOR = 0xa000ff00;
private static final int TEXT_COLOR = 0xa0ffffff;
private final Paint.FontMetrics mTextMetrics = new Paint.FontMetrics();
private int mPadding;
private final Rect mMainInsets = new Rect();
private final Rect mStableInsets = new Rect();
private boolean mRound = false;
private int mDesiredWidth;
private int mDesiredHeight;
private float mOffsetX;
private float mOffsetY;
private float mOffsetXStep;
private float mOffsetYStep;
private int mOffsetXPixels;
private int mOffsetYPixels;
private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private final Runnable mDrawClock = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
ClockEngine() {
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
mDesiredWidth = getDesiredMinimumWidth();
mDesiredHeight = getDesiredMinimumHeight();
Paint paint = mFillPaint;
paint.setStyle(Paint.Style.FILL);
paint = mStrokePaint;
paint.setStrokeWidth(3);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
TextPaint tpaint = mTextPaint;
tpaint.density = getResources().getDisplayMetrics().density;
tpaint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale);
tpaint.setColor(TEXT_COLOR);
tpaint.setTextSize(18 * getResources().getDisplayMetrics().scaledDensity);
tpaint.setShadowLayer(4 * getResources().getDisplayMetrics().density, 0, 0, 0xff000000);
mTextPaint.getFontMetrics(mTextMetrics);
mPadding = (int)(16 * getResources().getDisplayMetrics().density);
if (isPreview()) {
mOffsetX = 0.5f;
mOffsetY = 0.5f;
}
}
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawClock);
}
@Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (!visible) {
mHandler.removeCallbacks(mDrawClock);
}
drawFrame();
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
drawFrame();
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawClock);
}
@Override
public void onApplyWindowInsets(WindowInsets insets) {
super.onApplyWindowInsets(insets);
mMainInsets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(),
insets.getStableInsetRight(), insets.getStableInsetBottom());
mRound = insets.isRound();
drawFrame();
}
@Override
public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
super.onDesiredSizeChanged(desiredWidth, desiredHeight);
mDesiredWidth = desiredWidth;
mDesiredHeight = desiredHeight;
drawFrame();
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xStep, float yStep, int xPixels, int yPixels) {
super.onOffsetsChanged(xOffset, yOffset, xStep, yStep, xPixels, yPixels);
if (isPreview()) return;
mOffsetX = xOffset;
mOffsetY = yOffset;
mOffsetXStep = xStep;
mOffsetYStep = yStep;
mOffsetXPixels = xPixels;
mOffsetYPixels = yPixels;
drawFrame();
}
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
final Rect frame = holder.getSurfaceFrame();
final int width = frame.width();
final int height = frame.height();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
final Paint paint = mFillPaint;
paint.setColor(OUTER_COLOR);
c.drawRect(0, 0, width, height, paint);
paint.setColor(INNER_COLOR);
c.drawRect(0+mMainInsets.left, 0+mMainInsets.top,
width-mMainInsets.right, height-mMainInsets.bottom, paint);
mStrokePaint.setColor(STABLE_COLOR);
c.drawRect(0 + mStableInsets.left, 0 + mStableInsets.top,
width - mStableInsets.right, height - mStableInsets.bottom,
mStrokePaint);
final int ascdesc = (int)(-mTextMetrics.ascent + mTextMetrics.descent);
final int linegap = (int)(-mTextMetrics.ascent + mTextMetrics.descent
+ mTextMetrics.leading);
int x = mStableInsets.left + mPadding;
int y = height - mStableInsets.bottom - mPadding - ascdesc;
c.drawText("Surface Size: " + width + " x " + height,
x, y, mTextPaint);
y -= linegap;
c.drawText("Desired Size: " + mDesiredWidth + " x " + mDesiredHeight,
x, y, mTextPaint);
y -= linegap;
c.drawText("Cur Offset Raw: " + mOffsetX + ", " + mOffsetY,
x, y, mTextPaint);
y -= linegap;
c.drawText("Cur Offset Step: " + mOffsetXStep + ", " + mOffsetYStep,
x, y, mTextPaint);
y -= linegap;
c.drawText("Cur Offset Pixels: " + mOffsetXPixels + ", " + mOffsetYPixels,
x, y, mTextPaint);
y -= linegap;
c.drawText("Stable Insets: (" + mStableInsets.left + ", " + mStableInsets.top
+ ") - (" + mStableInsets.right + ", " + mStableInsets.bottom + ")",
x, y, mTextPaint);
y -= linegap;
c.drawText("System Insets: (" + mMainInsets.left + ", " + mMainInsets.top
+ ") - (" + mMainInsets.right + ", " + mMainInsets.bottom + ")",
x, y, mTextPaint);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
}
}
}