Live Wallpaperで動画ファイルをそのまま表示する(とりあえずできたよ編)
Android OS 2.1からLive Wallpaper(ライブ壁紙)の機能が追加されていますが、いざLive Wallpaperを自分で作る、となると結構面倒です。
実装方法の詳細は、「Android 2.1の新機能「Live Wallpaper」で作る、美しく燃える“待ち受け”」に詳しいですが、WallpaperService.Engineの各種メソッドをオーバーライドして、SurfaceHolderを経由してCanvasに描画処理を行うことで、アニメーションを実現するのが常套手段のようです。
ですがこの方法はかなり面倒です。単にアニメーションを再生したいだけなのに、Javaでゴリゴリプログラムを書きたくない。
そもそも、mp4とかの動画ファイルが素材としてあるんだったらそれをそのまま表示できないのでしょうか?
というわけで色々試してみます。
MediaPlayerクラスで再生している動画はSurfaceHolderに対して表示できるので、まずはその方法で試してみます。
MediaPlayerにSurfaceHolderをそのままsetDiaplayしてやる
public class LiveWallpaperTest extends WallpaperService { ~~ class LiveEngineTest extends Engine { private MediaPlayer mp; @Override public void onCreate(SurfaceHolder holder) { super.onCreate(holder); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); mp = new MediaPlayer(); mp.setDisplay(holder); mp.setOnPreparedListener(new OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.start(); } }); try { mp.setDataSource(LiveWallpaperTest.this, Uri.parse("content://media/external/video/media/1")); } catch (Exception e) { Log.e(TAG, "error"); } mp.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mp.stop(); } }); } // MediaPlayerをpreparaeする部分は省略 } ~~ }
ダメでした。logcatには以下のようなエラーが出力されます。
E/AndroidRuntime( 754): java.lang.UnsupportedOperationException: Wallpapers do not support keep screen on E/AndroidRuntime( 754): at android.service.wallpaper.WallpaperService$Engine$2.setKeepScreenOn(WallpaperService.java:202) E/AndroidRuntime( 754): at android.media.MediaPlayer.updateSurfaceScreenOn(MediaPlayer.java:895) E/AndroidRuntime( 754): at android.media.MediaPlayer.setDisplay(MediaPlayer.java:573)
ということで、MediaPlayer.setDisplayの中で自動的に、SurfaceHolder.setKeepScreenOnが呼ばれますが、WallpaperのSurfaceHolderはこのメソッドをサポートしていないのです。
ここで一工夫します。Wallpaper用のSurfaceHolderをそのまま使っていると上記の例外が避けられないので、独自のSurfaceHolderのWrapperを自分でこしらえます。
独自のSurfaceHolder WapperをMediaPlayerにsetDiaplayしてやる
class MySurfaceHolder implements SurfaceHolder { private SurfaceHolder surfaceHolder; public MySurfaceHolder(SurfaceHolder surfaceHolder) { this.surfaceHolder = surfaceHolder; } @Override public void addCallback(Callback callback) { surfaceHolder.addCallback(callback); } @Override public Surface getSurface() { return surfaceHolder.getSurface(); } @Override public Rect getSurfaceFrame() { return surfaceHolder.getSurfaceFrame(); } @Override public boolean isCreating() { return surfaceHolder.isCreating(); } @Override public Canvas lockCanvas() { return surfaceHolder.lockCanvas(); } @Override public Canvas lockCanvas(Rect dirty) { return surfaceHolder.lockCanvas(dirty); } @Override public void removeCallback(Callback callback) { surfaceHolder.removeCallback(callback); } @Override public void setFixedSize(int width, int height) { surfaceHolder.setFixedSize(width, height); } @Override public void setFormat(int format) { surfaceHolder.setFormat(format); } @Override public void setKeepScreenOn(boolean screenOn) { return; } @Override public void setSizeFromLayout() { surfaceHolder.setSizeFromLayout(); } @Override public void setType(int type) { surfaceHolder.setType(type); } @Override public void unlockCanvasAndPost(Canvas canvas) { surfaceHolder.unlockCanvasAndPost(canvas); } }
見ての通り、setKeepScreenOnメソッドの実装を空にしている以外は、全てもとのSurfaceHolderに丸投げするだけです。
こいつを使ってもう一度動画を再生してみましょう。
public class LiveWallpaperTest extends WallpaperService { ~~ class LiveEngineTest extends Engine { private MediaPlayer mp; @Override public void onCreate(SurfaceHolder holder) { super.onCreate(holder); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); mp = new MediaPlayer(); mp.setDisplay(new MySurfaceHolder(holder)); mp.setOnPreparedListener(new OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.start(); } }); try { mp.setDataSource(LiveWallpaperTest.this, Uri.parse("content://media/external/video/media/1")); } catch (Exception e) { Log.e(TAG, "error"); } mp.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mp.stop(); } }); } @Override public void onDestroy() { super.onDestroy(); if (mp != null) { mp.stop(); mp.release(); } } @Override public void onVisibilityChanged(boolean visible) { super.onVisibilityChanged(visible); if (visible) { play(); } } private void play() { if (mp.isPlaying()) { mp.stop(); } try { mp.prepareAsync(); } catch (IllegalArgumentException e) { Log.e(TAG, "error"); } catch (SecurityException e) { Log.e(TAG, "error"); } catch (IllegalStateException e) { Log.e(TAG, "error"); } } } ~~ }
さてどうでしょうか?
見事、Live Wallpaperとして動画が表示されています!
まとめ
- Live wallpaperのSurfaceHolderにMediaPlayerで再生した動画を表示しようとするとエラーでこける。
- SurfaceHolderをそのまま使うんじゃなくて、独自のWrapperを用意すればMediaPlayerで再生した動画をそのままライブ壁紙に使える。
という話でした。
単に動画を表示するだけのライブ壁紙はつまらないですが、面白いアプリを作る一手段としてなら使えるんじゃないでしょうか。(電池をどれくらい食うか心配ですが。。)