Live wallpapers become a little more real.

This adds a new theme for having a wallpaper, and fixes up the window manager
to do the right thing when transitioning between a windows with and without
wallpapers (between two windows with wallpapers is not yet addressed).

The wallpaper API now has callbacks to tell you when to start/stop animating.

Also fiddle the image wallpaper to be a little more interesting.
This commit is contained in:
Dianne Hackborn
2009-08-09 17:20:27 -07:00
parent 29b8fca9fd
commit 759a39e8d2
7 changed files with 300 additions and 25 deletions

View File

@@ -9370,6 +9370,17 @@
visibility="public"
>
</field>
<field name="windowShowWallpaper"
type="int"
transient="false"
volatile="false"
value="16843426"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="windowSoftInputMode"
type="int"
transient="false"
@@ -15002,6 +15013,39 @@
visibility="public"
>
</field>
<field name="Theme_Wallpaper"
type="int"
transient="false"
volatile="false"
value="16973937"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="Theme_Wallpaper_NoTitleBar"
type="int"
transient="false"
volatile="false"
value="16973938"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="Theme_Wallpaper_NoTitleBar_Fullscreen"
type="int"
transient="false"
volatile="false"
value="16973939"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="Widget"
type="int"
transient="false"

View File

@@ -21,6 +21,7 @@ import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseSurfaceHolder;
import android.app.Service;
import android.app.WallpaperManager;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Handler;
@@ -55,12 +56,14 @@ public abstract class WallpaperService extends Service {
private static final int DO_DETACH = 20;
private static final int MSG_UPDATE_SURFACE = 10000;
private static final int MSG_VISIBILITY_CHANGED = 10010;
/**
* The actual implementation of a wallpaper. A wallpaper service may
* have multiple instances running (for example as a real wallpaper
* and as a preview), each of which is represented by its own Engine
* instance.
* instance. You must implement {@link WallpaperService#onCreateEngine()}
* to return your concrete Engine implementation.
*/
public class Engine {
IWallpaperEngineWrapper mIWallpaperEngine;
@@ -119,21 +122,79 @@ public abstract class WallpaperService extends Service {
};
final BaseIWindow mWindow = new BaseIWindow() {
public void dispatchAppVisibility(boolean visible) {
Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
visible ? 1 : 0);
mCaller.sendMessage(msg);
}
};
public void onAttach(SurfaceHolder surfaceHolder) {
/**
* Provides access to the surface in which this wallpaper is drawn.
*/
public SurfaceHolder getSurfaceHolder() {
return mSurfaceHolder;
}
public void onDetach() {
/**
* Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
* WallpaperManager.getDesiredMinimumWidth()}, returning the width
* that the system would like this wallpaper to run in.
*/
public int getDesiredMinimumWidth() {
return mIWallpaperEngine.mReqWidth;
}
/**
* Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
* WallpaperManager.getDesiredMinimumHeight()}, returning the height
* that the system would like this wallpaper to run in.
*/
public int getDesiredMinimumHeight() {
return mIWallpaperEngine.mReqHeight;
}
/**
* Called once to initialize the engine. After returning, the
* engine's surface will be created by the framework.
*/
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
* valid.
*/
public void onDestroy() {
}
/**
* Called to inform you of the wallpaper becoming visible or
* hidden. <em>It is very important that a wallpaper only use
* CPU while it is visible.</em>.
*/
public void onVisibilityChanged(boolean visible) {
}
/**
* Convenience for {@link SurfaceHolder.Callback#surfaceChanged
* SurfaceHolder.Callback.surfaceChanged()}.
*/
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* Convenience for {@link SurfaceHolder.Callback#surfaceCreated
* SurfaceHolder.Callback.surfaceCreated()}.
*/
public void onSurfaceCreated(SurfaceHolder holder) {
}
/**
* Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
* SurfaceHolder.Callback.surfaceDestroyed()}.
*/
public void onSurfaceDestroyed(SurfaceHolder holder) {
}
@@ -249,14 +310,14 @@ public abstract class WallpaperService extends Service {
mSession = ViewRoot.getWindowSession(getMainLooper());
mWindow.setSession(mSession);
onAttach(mSurfaceHolder);
onCreate(mSurfaceHolder);
mInitializing = false;
updateSurface(false);
}
void detach() {
onDetach();
onDestroy();
if (mDestroyReportNeeded) {
mDestroyReportNeeded = false;
SurfaceHolder.Callback callbacks[];
@@ -330,6 +391,11 @@ public abstract class WallpaperService extends Service {
case MSG_UPDATE_SURFACE:
mEngine.updateSurface(false);
break;
case MSG_VISIBILITY_CHANGED:
if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
+ ": " + message.arg1);
mEngine.onVisibilityChanged(message.arg1 != 0);
break;
default :
Log.w(TAG, "Unknown message type " + message.what);
}

View File

@@ -19,7 +19,11 @@ package com.android.internal.service.wallpaper;
import android.app.WallpaperManager;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.service.wallpaper.WallpaperService;
import android.view.SurfaceHolder;
@@ -29,30 +33,58 @@ import android.view.SurfaceHolder;
public class ImageWallpaper extends WallpaperService {
public WallpaperManager mWallpaperManager;
static final int MSG_DRAW = 1;
class MyEngine extends Engine {
final Paint mTextPaint = new Paint();
float mDensity;
Drawable mBackground;
long mAnimStartTime;
boolean mAnimLarger;
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DRAW:
drawFrame(true);
mHandler.sendEmptyMessage(MSG_DRAW);
break;
default:
super.handleMessage(msg);
}
}
};
@Override
public void onAttach(SurfaceHolder surfaceHolder) {
super.onAttach(surfaceHolder);
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
mBackground = mWallpaperManager.getDrawable();
mTextPaint.setAntiAlias(true);
mDensity = getResources().getDisplayMetrics().density;
mTextPaint.setTextSize(30 * mDensity);
mTextPaint.setShadowLayer(5*mDensity, 3*mDensity, 3*mDensity, 0xff000000);
mTextPaint.setARGB(255, 255, 255, 255);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
public void onVisibilityChanged(boolean visible) {
mHandler.removeMessages(MSG_DRAW);
if (visible) {
mHandler.sendEmptyMessage(MSG_DRAW);
mAnimStartTime = SystemClock.uptimeMillis();
mAnimLarger = true;
} else {
drawFrame(false);
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
Canvas c = holder.lockCanvas();
mBackground.setBounds(0, 0, width, height);
mBackground.draw(c);
Paint paint = new Paint();
paint.setAntiAlias(true);
final float density = getResources().getDisplayMetrics().density;
paint.setTextSize(30 * density);
paint.setShadowLayer(5*density, 3*density, 3*density, 0xff000000);
paint.setARGB(255, 255, 255, 255);
c.drawText("Am I live?", 10, 60*density, paint);
holder.unlockCanvasAndPost(c);
drawFrame(false);
}
@Override
@@ -65,6 +97,35 @@ public class ImageWallpaper extends WallpaperService {
super.onSurfaceDestroyed(holder);
}
void drawFrame(boolean drawText) {
SurfaceHolder sh = getSurfaceHolder();
Canvas c = sh.lockCanvas();
if (c != null) {
final Rect frame = sh.getSurfaceFrame();
mBackground.setBounds(frame);
mBackground.draw(c);
if (drawText) {
// Figure out animation.
long now = SystemClock.uptimeMillis();
while (mAnimStartTime < (now-1000)) {
mAnimStartTime += 1000;
mAnimLarger = !mAnimLarger;
}
float size = (now-mAnimStartTime) / (float)1000;
if (!mAnimLarger) size = 1-size;
int alpha = (int)(255*(size*size));
mTextPaint.setARGB(alpha, 255, 255, 255);
mTextPaint.setShadowLayer(5*mDensity, 3*mDensity, 3*mDensity,
alpha<<24);
mTextPaint.setTextSize(100 * mDensity * size);
c.drawText("Am I live?",
frame.left + (frame.right-frame.left)/2,
frame.top + (frame.bottom-frame.top)/2, mTextPaint);
}
}
sh.unlockCanvasAndPost(c);
}
}
@Override

View File

@@ -215,6 +215,9 @@
<attr name="windowIsFloating" format="boolean" />
<!-- Flag indicating whether this is a translucent window. -->
<attr name="windowIsTranslucent" format="boolean" />
<!-- Flag indicating that this window's background should be the
user's current wallpaper. -->
<attr name="windowShowWallpaper" format="boolean" />
<!-- This Drawable is overlaid over the foreground of the Window's content area, usually
to place a shadow below the title. -->
<attr name="windowContentOverlay" format="reference" />
@@ -900,6 +903,7 @@
<attr name="windowFullscreen" />
<attr name="windowIsFloating" />
<attr name="windowIsTranslucent" />
<attr name="windowShowWallpaper" />
<attr name="windowAnimationStyle" />
<attr name="windowSoftInputMode" />
<attr name="windowDisablePreview" />

View File

@@ -1167,9 +1167,13 @@
<public type="attr" name="accountType" />
<public type="attr" name="contentAuthority" />
<public type="attr" name="windowShowWallpaper" />
<public type="drawable" name="stat_sys_vp_phone_call" />
<public type="drawable" name="stat_sys_vp_phone_call_on_hold" />
<public type="style" name="Theme.Wallpaper" />
<public type="style" name="Theme.Wallpaper.NoTitleBar" />
<public type="style" name="Theme.Wallpaper.NoTitleBar.Fullscreen" />
</resources>

View File

@@ -280,6 +280,25 @@
<item name="android:windowContentOverlay">@null</item>
</style>
<!-- Default theme for windows that want to have the user's selected
wallpaper appear behind them. -->
<style name="Theme.Wallpaper">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowShowWallpaper">true</item>
</style>
<!-- Variant of the translucent theme with no title bar -->
<style name="Theme.Wallpaper.NoTitleBar">
<item name="android:windowNoTitle">true</item>
</style>
<!-- Variant of the translucent theme that has no title bar and
fills the entire screen -->
<style name="Theme.Wallpaper.NoTitleBar.Fullscreen">
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<!-- Default theme for translucent activities, that is windows that allow you
to see through them to the windows behind. This sets up the translucent
flag and appropriate animations for your windows. -->

View File

@@ -403,6 +403,11 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>();
// If non-null, this is the currently visible window that is associated
// with the wallpaper.
WindowState mWallpaperTarget = null;
int mWallpaperAnimLayerAdjustment;
AppWindowToken mFocusedApp = null;
PowerManagerService mPowerManager;
@@ -1180,15 +1185,23 @@ public class WindowManagerService extends IWindowManager.Stub
int N = localmWindows.size();
WindowState w = null;
int i = N;
boolean visible = false;
while (i > 0) {
i--;
w = (WindowState)localmWindows.get(i);
if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isVisibleOrAdding()) {
if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay()) {
visible = true;
break;
}
}
if (w != null) {
if (!visible) w = null;
mWallpaperTarget = w;
if (visible) {
mWallpaperAnimLayerAdjustment = w.mAppToken != null
? w.mAppToken.animLayerAdjustment : 0;
// Now w is the window we are supposed to be behind... but we
// need to be sure to also be behind any of its attached windows,
// AND any starting window associated with it.
@@ -1220,6 +1233,24 @@ public class WindowManagerService extends IWindowManager.Stub
while (curWallpaperIndex > 0) {
curWallpaperIndex--;
WindowState wallpaper = token.windows.get(curWallpaperIndex);
// First, make sure the client has the current visibility
// state.
if (wallpaper.mWallpaperVisible != visible) {
wallpaper.mWallpaperVisible = visible;
try {
if (DEBUG_VISIBILITY) Log.v(TAG,
"Setting visibility of wallpaper " + wallpaper
+ ": " + visible);
wallpaper.mClient.dispatchAppVisibility(visible);
} catch (RemoteException e) {
}
}
wallpaper.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
if (DEBUG_LAYERS) Log.v(TAG, "Wallpaper win " + wallpaper
+ " anim layer: " + wallpaper.mAnimLayer);
// First, if this window is at the current index, then all
// is well.
if (wallpaper == w) {
@@ -1248,6 +1279,24 @@ public class WindowManagerService extends IWindowManager.Stub
return changed;
}
void setWallpaperAnimLayerAdjustment(int adj) {
if (DEBUG_LAYERS) Log.v(TAG, "Setting wallpaper layer adj to " + adj);
mWallpaperAnimLayerAdjustment = adj;
int curTokenIndex = mWallpaperTokens.size();
while (curTokenIndex > 0) {
curTokenIndex--;
WindowToken token = mWallpaperTokens.get(curTokenIndex);
int curWallpaperIndex = token.windows.size();
while (curWallpaperIndex > 0) {
curWallpaperIndex--;
WindowState wallpaper = token.windows.get(curWallpaperIndex);
wallpaper.mAnimLayer = wallpaper.mLayer + adj;
if (DEBUG_LAYERS) Log.v(TAG, "Wallpaper win " + wallpaper
+ " anim layer: " + wallpaper.mAnimLayer);
}
}
}
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
@@ -1867,6 +1916,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client);
if (win != null && win.finishDrawingLocked()) {
if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
adjustWallpaperWindowsLocked();
}
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
@@ -5842,6 +5894,7 @@ public class WindowManagerService extends IWindowManager.Stub
Surface mSurface;
boolean mAttachedHidden; // is our parent window hidden?
boolean mLastHidden; // was this window last hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
int mRequestedWidth;
int mRequestedHeight;
int mLastRequestedWidth;
@@ -6518,6 +6571,8 @@ public class WindowManagerService extends IWindowManager.Stub
mAnimLayer = mLayer;
if (mIsImWindow) {
mAnimLayer += mInputMethodAnimLayerAdjustment;
} else if (mIsWallpaper) {
mAnimLayer += mWallpaperAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this
+ " anim layer: " + mAnimLayer);
@@ -6604,6 +6659,19 @@ public class WindowManagerService extends IWindowManager.Stub
Transformation appTransformation =
(mAppToken != null && mAppToken.hasTransformation)
? mAppToken.transformation : null;
// Wallpapers are animated based on the "real" window they
// are currently targeting.
if (mAttrs.type == TYPE_WALLPAPER && mWallpaperTarget != null) {
if (mWallpaperTarget.mHasLocalTransformation) {
attachedTransformation = mWallpaperTarget.mTransformation;
}
if (mWallpaperTarget.mAppToken != null &&
mWallpaperTarget.mAppToken.hasTransformation) {
appTransformation = mWallpaperTarget.mAppToken.transformation;
}
}
if (selfTransformation || attachedTransformation != null
|| appTransformation != null) {
// cache often used attributes locally
@@ -6926,7 +6994,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow);
pw.print(" mIsWallpaper="); pw.print(mIsWallpaper);
pw.print(" mIsFloatingLayer="); pw.println(mIsFloatingLayer);
pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer);
pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible);
}
pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
pw.print(" mSubLayer="); pw.print(mSubLayer);
@@ -7212,6 +7281,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (w == mInputMethodTarget) {
setInputMethodAnimLayerAdjustment(adj);
}
if (w == mWallpaperTarget) {
setWallpaperAnimLayerAdjustment(adj);
}
}
}
@@ -7978,6 +8050,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (w.mIsImWindow) {
w.mAnimLayer += mInputMethodAnimLayerAdjustment;
} else if (w.mIsWallpaper) {
w.mAnimLayer += mWallpaperAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": "
+ w.mAnimLayer);
@@ -8659,7 +8733,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
boolean opaqueDrawn = w.isOpaqueDrawn();
if (opaqueDrawn && w.isFullscreen(dw, dh)) {
if ((opaqueDrawn && w.isFullscreen(dw, dh))
|| attrs.type == TYPE_WALLPAPER) {
// This window completely covers everything behind it,
// so we want to leave all of them as unblurred (for
// performance reasons).
@@ -9316,7 +9391,9 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print( " no DimAnimator ");
}
pw.print(" mInputMethodAnimLayerAdjustment=");
pw.println(mInputMethodAnimLayerAdjustment);
pw.print(mInputMethodAnimLayerAdjustment);
pw.print(" mWallpaperAnimLayerAdjustment=");
pw.println(mWallpaperAnimLayerAdjustment);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
pw.print(" mAppsFreezingScreen="); pw.println(mAppsFreezingScreen);