blob: 45f3bc15df5f2a479d24c1f0e874dc67f8f7b65e [file] [log] [blame]
/*
* Copyright (C) 2009 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.android.internal.service.wallpaper;
import java.io.IOException;
import com.android.internal.view.WindowManagerPolicyThread;
import android.app.WallpaperManager;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.content.Context;
import android.content.IntentFilter;
import android.content.Intent;
import android.content.BroadcastReceiver;
/**
* Default built-in wallpaper that simply shows a static image.
*/
public class ImageWallpaper extends WallpaperService {
private static final String TAG = "ImageWallpaper";
private static final boolean DEBUG = false;
WallpaperManager mWallpaperManager;
private HandlerThread mThread;
private Handler mHandler;
@Override
public void onCreate() {
super.onCreate();
mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
Looper looper = WindowManagerPolicyThread.getLooper();
if (looper == null) {
mThread = new HandlerThread("Wallpaper", Process.THREAD_PRIORITY_FOREGROUND);
mThread.start();
looper = mThread.getLooper();
}
setCallbackLooper(looper);
mHandler = new Handler(looper);
}
public Engine onCreateEngine() {
return new DrawableEngine();
}
@Override
public void onDestroy() {
super.onDestroy();
if (mThread != null) {
mThread.quit();
}
}
class DrawableEngine extends Engine {
private final Object mLock = new Object();
private WallpaperObserver mReceiver;
Drawable mBackground;
float mXOffset;
float mYOffset;
boolean mVisible = true;
boolean mRedrawNeeded;
boolean mOffsetsChanged;
int mLastXTranslation;
int mLastYTranslation;
class WallpaperObserver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Log.d(TAG, "onReceive");
}
synchronized (mLock) {
updateWallpaperLocked();
drawFrameLocked();
}
}
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
if (DEBUG) {
Log.d(TAG, "onCreate");
}
super.onCreate(surfaceHolder);
IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
mReceiver = new WallpaperObserver();
registerReceiver(mReceiver, filter, null, mHandler);
updateSurfaceSize(surfaceHolder);
synchronized (mLock) {
updateWallpaperLocked();
}
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
@Override
public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
super.onDesiredSizeChanged(desiredWidth, desiredHeight);
SurfaceHolder surfaceHolder = getSurfaceHolder();
if (surfaceHolder != null) {
updateSurfaceSize(surfaceHolder);
}
}
void updateSurfaceSize(SurfaceHolder surfaceHolder) {
surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
// Used a fixed size surface, because we are special. We can do
// this because we know the current design of window animations doesn't
// cause this to break.
//surfaceHolder.setSizeFromLayout();
}
@Override
public void onVisibilityChanged(boolean visible) {
if (DEBUG) {
Log.d(TAG, "onVisibilityChanged: visible=" + visible);
}
synchronized (mLock) {
if (mVisible != visible) {
if (DEBUG) {
Log.d(TAG, "Visibility changed to visible=" + visible);
}
mVisible = visible;
drawFrameLocked();
}
}
}
@Override
public void onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep,
int xPixels, int yPixels) {
if (DEBUG) {
Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
+ ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
+ ", xPixels=" + xPixels + ", yPixels=" + yPixels);
}
synchronized (mLock) {
if (mXOffset != xOffset || mYOffset != yOffset) {
if (DEBUG) {
Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
}
mXOffset = xOffset;
mYOffset = yOffset;
mOffsetsChanged = true;
}
drawFrameLocked();
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (DEBUG) {
Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
}
super.onSurfaceChanged(holder, format, width, height);
synchronized (mLock) {
mRedrawNeeded = true;
drawFrameLocked();
}
}
void drawFrameLocked() {
if (!mVisible) {
if (DEBUG) {
Log.d(TAG, "Suppressed drawFrame since wallpaper is not visible.");
}
return;
}
if (!mRedrawNeeded && !mOffsetsChanged) {
if (DEBUG) {
Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
+ "and offsets have not changed.");
}
return;
}
SurfaceHolder sh = getSurfaceHolder();
final Rect frame = sh.getSurfaceFrame();
final Drawable background = mBackground;
final int dw = frame.width();
final int dh = frame.height();
final int bw = background != null ? background.getIntrinsicWidth() : 0;
final int bh = background != null ? background.getIntrinsicHeight() : 0;
final int availw = dw - bw;
final int availh = dh - bh;
int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
mOffsetsChanged = false;
if (!mRedrawNeeded
&& xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
if (DEBUG) {
Log.d(TAG, "Suppressed drawFrame since the image has not "
+ "actually moved an integral number of pixels.");
}
return;
}
mRedrawNeeded = false;
mLastXTranslation = xPixels;
mLastYTranslation = yPixels;
Canvas c = sh.lockCanvas();
if (c != null) {
try {
if (DEBUG) {
Log.d(TAG, "Redrawing: xPixels=" + xPixels + ", yPixels=" + yPixels);
}
c.translate(xPixels, yPixels);
if (availw < 0 || availh < 0) {
c.save(Canvas.CLIP_SAVE_FLAG);
c.clipRect(0, 0, bw, bh, Op.DIFFERENCE);
c.drawColor(0xff000000);
c.restore();
}
if (background != null) {
background.draw(c);
}
} finally {
sh.unlockCanvasAndPost(c);
}
}
}
void updateWallpaperLocked() {
Throwable exception = null;
try {
mBackground = mWallpaperManager.getFastDrawable();
} catch (RuntimeException e) {
exception = e;
} catch (OutOfMemoryError e) {
exception = e;
}
if (exception != null) {
mBackground = null;
// Note that if we do fail at this, and the default wallpaper can't
// be loaded, we will go into a cycle. Don't do a build where the
// default wallpaper can't be loaded.
Log.w(TAG, "Unable to load wallpaper!", exception);
try {
mWallpaperManager.clear();
} catch (IOException ex) {
// now we're really screwed.
Log.w(TAG, "Unable reset to default wallpaper!", ex);
}
}
mRedrawNeeded = true;
}
}
}