Android Live Wallpaper
いわゆるライブ壁紙っていう機能。SurfaceView的な感じで壁紙に動的にレンダリングしたりとか出来る
まぁ自宅のメモ用ブログには書いてたけどこっちに書いてなかったので、美人時計サービスを利用して1分毎に動的に壁紙をレンダリングするっていうのをやってみる。ただ、結構雑に作ってあるので(ry
※あくまでLive Wallpaperってどんな感じで作るのかっていう
仕様
Live Wallpaperの仕様じゃなくて美人時計の仕様として http://www.bijint.com/jp/tokei_images/%02d%02d.jpg というような方式でリクエストすれば画像が取れるらしい。これ検証したのが結構前な話になるのだけど、現状でもそれは変わってはいないが昔はリファラーな辺りをチェックされていて美人時計内部からのリクエストだと模倣させないといけなかったような気がするけど現在そういう仕様は存在しない模様
んまぁちょっと余談がありましたが、結局は一分おきに上記のURLにリクエスト飛ばして画像取ってレンダリングするだけ
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0" package="net.kinjouj.app.bijin_clock_wallpaper">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:allowBackup="false">
<service
android:name=".BijinClockLiveWallpaperService"
android:permission="android.permission.BIND_WALLPAPER">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/wallpaper" />
</service>
<activity
android:name=".BijinClockLiveWallpaperPreferenceActivity"
android:exported="true" />
</application>
</manifest>
Live Wallpaperなレンダリングを行うのはサービスなのでそれを定義する。ActivityはPreferenceActivityなやつで今回は使わないので省略
res/xml/wallpaper.xml
<?xml version="1.0" encoding="utf-8" ?>
<wallpaper
xmlns:android="http://schemas.android.com/apk/res/android"
android:thumbnail="@drawable/downloading"
android:settingsActivity="net.kinjouj.app.bijin_clock_wallpaper.BijinClockLiveWallpaperPreferenceActivity" />
android:thumbnailでライブ壁紙一覧で表示される部分に画像を出せる模様。あくまでプレビューとは違うのかも
android:settingsActivityを設定しておく事でライブ壁紙を設置する際に設定を行う事が出来る。その設定は上記で省略したPreferenceActivityを用いる
んまぁあとはWallpaperServiceな実体なサービスクラスを作る
BijinClockLiveWallpaperService.java
package net.kinjouj.app.bijin_clock_wallpaper;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.service.wallpaper.WallpaperService;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.util.Log;
public class BijinClockLiveWallpaperService extends WallpaperService {
private static final String TAG = BijinClockLiveWallpaperService.class.getName();
@Override
public Engine onCreateEngine() {
return new BijinClockLiveWallpaperEngine();
}
private class BijinClockLiveWallpaperEngine extends Engine {
private static final String BASE_URL = "http://www.bijint.com/jp/tokei_images/%02d%02d.jpg";
private boolean enabled = true;
private boolean fetched = false;
private Handler handler = new Handler();
private Bitmap currentBitmap;
private Bitmap nextBitmap;
private Date currentDate;
private Date nextDate;
private final Thread mFetch = new Thread("fetch") {
@Override
public void run() {
Looper.prepare();
try {
if(currentBitmap == null) currentBitmap = getImage(null);
while(enabled) {
if(isVisible()) handler.post(mDraw);
nextDate = calculateDate();
new Thread("fecth_inner") {
@Override
public void run() {
nextBitmap = getImage(nextDate);
}
}.start();
long ellapse = nextDate.getTime() - currentDate.getTime();
Thread.sleep(ellapse);
if(nextBitmap != null) {
currentDate = nextDate;
currentBitmap = nextBitmap;
}
nextBitmap = null;
}
} catch(InterruptedException e) {
e.printStackTrace();
}
Looper.loop();
}
};
private Thread mDraw = new Thread("draw") {
@Override
public void run() {
drawCanvas();
}
};
@Override
public void onCreate(SurfaceHolder holder) {
super.onCreate(holder);
Log.v(TAG, "onCreate");
handler.post(mDraw);
if(!isPreview() && !mFetch.isAlive()) mFetch.start();
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xStep,
float yStep, int xPixels, int yPixels) {
super.onOffsetsChanged(xOffset, yOffset, xStep, yStep, xPixels, yPixels);
handler.post(mDraw);
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
handler.post(mDraw);
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
try {
if(mFetch.isAlive()) {
Looper.myLooper().quit();
mFetch.stop();
}
} finally {
enabled = false;
handler.removeCallbacks(mDraw);
if(!isPreview()) Process.killProcess(Process.myPid());
}
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if(visible)
handler.post(mDraw);
else
handler.removeCallbacks(mDraw);
}
private void drawCanvas() {
if(!isVisible()) return;
SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if(c != null) {
int width = holder.getSurfaceFrame().width();
int height = holder.getSurfaceFrame().height();
if(!isPreview()) {
if(currentBitmap == null) return;
c.drawBitmap(
Bitmap.createScaledBitmap(currentBitmap, width, height, true),
0,
0,
null
);
} else {
Bitmap bmp = BitmapFactory.decodeResource(
getResources(),
R.drawable.downloading
);
c.drawBitmap(
Bitmap.createScaledBitmap(bmp, width, height, true),
0,
0,
null
);
}
}
} finally {
if(c != null) holder.unlockCanvasAndPost(c);
handler.removeCallbacks(mDraw);
}
}
public Date calculateDate() {
if(currentDate == null) {
currentDate = new Date();
return currentDate;
}
Date now = new Date();
now.setSeconds(0);
if((now.getTime() - currentDate.getTime()) > 60000) currentDate = now;
Calendar cal = Calendar.getInstance();
cal.setTime(currentDate);
cal.add(Calendar.MINUTE, 1);
Date date = cal.getTime();
date.setSeconds(0);
return date;
}
@SuppressLint("DefaultLocale")
public Bitmap getImage(Date date) {
if(fetched)
return null;
else
fetched = true;
if(date == null) date = new Date();
Bitmap bmp = null;
try {
String url = String.format(BASE_URL, date.getHours(), date.getMinutes());
HttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(url);
byte[] b = httpClient.execute(
request,
new ResponseHandler<byte[]>() {
@Override
public byte[] handleResponse(HttpResponse response)
throws ClientProtocolException, IOException {
byte[] b = null;
if (response.getStatusLine().getStatusCode() == 200) {
BufferedHttpEntity entity = new BufferedHttpEntity(response.getEntity());
b = EntityUtils.toByteArray(entity);
}
return b;
}
}
);
if (b != null) bmp = BitmapFactory.decodeByteArray(b, 0, b.length);
} catch(IOException e) {
Log.e(TAG, "ERROR", e);
bmp = null;
} finally {
fetched = false;
}
return bmp;
}
}
}
ロックの仕方がまったくダメパターンだけど... (ほぼ数年前に書いたコードをそのまま)
んまぁ一定時間(1分)おきくらいにURLから画像を取得してCanvasにBitmapをレンダリングするだけ。
んまぁあとは自分で検証してください