視頻壁紙,對於動態壁紙開發,就得用到WallpaperService;
manifest配置:
<!-- 配置實時壁紙Service --> <service android:name="com.mill.wpengine.VideoLiveWallpaper" android:label="@string/app_name" android:exported="true" android:permission="android.permission.BIND_WALLPAPER" > <!-- 爲實時壁紙配置intent-filter --> <intent-filter> <action android:name="android.service.wallpaper.WallpaperService" /> </intent-filter> <!-- 爲實時壁紙配置meta-data --> <meta-data android:name="android.service.wallpaper" android:resource="@xml/livewallpaper" /> </service>注意:meta-data的 xml文件,主要是在 系統設置->顯示->動態壁紙裏面會顯示icon和說明;
<?xml version="1.0" encoding="utf-8"?> <wallpaper xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/app_name" android:thumbnail="@mipmap/ic_launcher" />
設置壁紙需要權限:
<uses-permission android:name="android.permission.SET_WALLPAPER" />
因爲用到視頻,所以要用到MediaPlayer;而MediaPlayer 播放本地視頻,有時候莫名會黑屏(暫時沒找到原因,可能原因IO問題);
暫時解決方案:OnError方法裏面,先銷燬,再清空壁紙,再重新設置壁紙;
package com.mill.wpengine; import android.app.WallpaperInfo; import android.app.WallpaperManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.media.MediaPlayer; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.service.wallpaper.WallpaperService; import android.view.SurfaceHolder; import android.widget.Toast; import java.util.Date; /** * 視頻壁紙引擎服務 * * 多進程sp訪問,主工程裏面調用: * SharedPreferences sp = context.getSharedPreferences(SP_EXPORT_FILE, Context.MODE_MULTI_PROCESS | Context.MODE_WORLD_READABLE); sp.edit().putString(SP_KEY_FIRST_SET_FILEPATH, videoFilePath).commit(); */ public class VideoLiveWallpaper extends WallpaperService { public static final String TAG = "VideoLiveWallpaper"; public static final String VIDEO_WP_ENGINE_PRE = "video_wp_engine"; public static final String AP_APK_NAME = "<主工程包名>"; public static final String SP_VIDEO_FILE_DEFAULT = "<默認視頻路徑,建議放遠程路徑>"; public static final String SP_EXPORT_FILE = "plugin_vwp_export"; public static final String SP_KEY_FIRST_SET_FILEPATH = "KEY_FIRST_SET_FILEPATH"; public static final String SP_KEY_VIDEODESKTOP_USED_LAST_TIME = "VideoDesktop_Used_Last_Time"; public static final String SP_KEY_ALIVE_AP_LAST_TIME = "alive_ap_last_time"; public final static String THREAD_NAME_ALIVE_AP = "alive_thread"; // 視頻壁紙引擎APK,拉活 public static final String VIDEO_PARAMS_CONTROL_ACTION = "com.zhy.livewallpaper"; public static final String KEY_VOLUME_WPSETTING = "KEY_Volume_WPSetting"; public static final String KEY_ACTION = "action"; public static final int ACTION_VOICE_SILENCE = 110; public static final int ACTION_VOICE_NORMAL = 111; private String mVideoFilePath = null; private VideoEngine mEngine; private MediaPlayer mMediaPlayer; //拉活 線程 private HandlerThread mHandlerThread; private Handler mHandler; private Runnable mAliveRunnable; private boolean isAliveThreadRun = false; public Engine onCreateEngine() { mEngine = new VideoEngine(); if (mVideoFilePath == null) { mVideoFilePath = getSPPath(); } return mEngine; } /** * 獲取本地存的路徑 */ public String getSPPath() { String filePath = null; try { Context c = getApplicationContext().createPackageContext(AP_APK_NAME, Context.CONTEXT_IGNORE_SECURITY); SharedPreferences sp = c.getSharedPreferences(SP_EXPORT_FILE, Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS); filePath = sp.getString(SP_KEY_FIRST_SET_FILEPATH, null); if (filePath != null) { getApplicationContext().getSharedPreferences(VideoLiveWallpaper.VIDEO_WP_ENGINE_PRE, MODE_PRIVATE).edit().putString(SP_KEY_FIRST_SET_FILEPATH, filePath).commit(); } } catch (Exception e) { e.printStackTrace(); } /** * 引擎保存的SP裏面取 */ if (filePath == null) { filePath = getApplicationContext().getSharedPreferences(VideoLiveWallpaper.VIDEO_WP_ENGINE_PRE, MODE_PRIVATE).getString(SP_KEY_FIRST_SET_FILEPATH, null); } /** * 默認壁紙 * 暫時先放個網絡路徑 * 防止系統設置-顯示-動態壁紙,選擇後是黑的 */ if (filePath == null) { filePath = SP_VIDEO_FILE_DEFAULT; } return filePath; } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } @Override public void onCreate() { super.onCreate(); mHandlerThread = new HandlerThread(THREAD_NAME_ALIVE_AP); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); mAliveRunnable = new Runnable() { @Override public void run() { // 拉活代碼...... isAliveThreadRun = false; } }; } @Override public void onDestroy() { if (mEngine != null) { mEngine.destroy(); } if (mHandlerThread != null) { mAliveRunnable = null; mHandler.removeCallbacksAndMessages(null); mHandler = null; mHandlerThread.quit(); mHandlerThread = null; } super.onDestroy(); } private class VideoEngine extends Engine implements MediaPlayer.OnErrorListener { private BroadcastReceiver mVideoParamsControlReceiver; @Override public void onCreate(SurfaceHolder surfaceHolder) { super.onCreate(surfaceHolder); IntentFilter intentFilter = new IntentFilter(VIDEO_PARAMS_CONTROL_ACTION); registerReceiver(mVideoParamsControlReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int action = intent.getIntExtra(KEY_ACTION, -1); getApplicationContext().getSharedPreferences(VideoLiveWallpaper.VIDEO_WP_ENGINE_PRE, MODE_PRIVATE).edit().putInt(KEY_VOLUME_WPSETTING, action).commit(); setVolumeByWPSetting(); } }, intentFilter); } /** * 根據視頻桌面插件裏面的設置來判斷 * 本地存儲下狀態,方便開機時 */ private void setVolumeByWPSetting() { int action = getApplicationContext().getSharedPreferences(VideoLiveWallpaper.VIDEO_WP_ENGINE_PRE, MODE_PRIVATE).getInt(KEY_VOLUME_WPSETTING, -1); switch (action) { case ACTION_VOICE_NORMAL: if (mMediaPlayer != null) { mMediaPlayer.setVolume(1.0f, 1.0f); } break; case ACTION_VOICE_SILENCE: if (mMediaPlayer != null) { mMediaPlayer.setVolume(0, 0); } break; } } @Override public void onDestroy() { if (mVideoParamsControlReceiver != null) { unregisterReceiver(mVideoParamsControlReceiver); } super.onDestroy(); } @Override public void onVisibilityChanged(boolean visible) { if (mMediaPlayer != null) { if (visible) { aliveAp(); // checkAPInstalled(); stateUsed(); startCheckPath(); } else { mMediaPlayer.pause(); } } } /** * 是否安裝了pkgName * * @param pkgName * @return */ private boolean isAppInstalled(String pkgName) { boolean result = false; try { result = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_DISABLED_COMPONENTS) != null; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 檢測主工程是否安裝了,否則清掉視頻壁紙 */ private void checkAPInstalled() { if (!isAppInstalled(AP_APK_NAME) && isLiveWallpaperRunning()) { //卸載了,則清掉視頻壁紙 try { WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext()); wallpaperManager.clear(); } catch (Exception e) { e.printStackTrace(); } } } /** * 拉活 主工程 */ private void aliveAp() { //拉活 // long lastTime = getApplicationContext().getSharedPreferences(VideoLiveWallpaper.VIDEO_WP_ENGINE_PRE, MODE_PRIVATE).getLong(SP_KEY_ALIVE_AP_LAST_TIME, 0); // if (!DateUtils.isToday(lastTime)) { // getApplicationContext().getSharedPreferences(VideoLiveWallpaper.VIDEO_WP_ENGINE_PRE, MODE_PRIVATE).edit().putLong(SP_KEY_ALIVE_AP_LAST_TIME, System.currentTimeMillis()).commit(); // } if (mHandler != null) { if (!isAliveThreadRun) { isAliveThreadRun = true; mHandler.post(mAliveRunnable); } } } /** * 視頻壁紙 是否啓動了 * * @return */ private boolean isLiveWallpaperRunning() { WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext());// 得到壁紙管理器 WallpaperInfo wallpaperInfo = wallpaperManager.getWallpaperInfo();// 如果系統使用的壁紙是動態壁紙話則返回該動態壁紙的信息,否則會返回null if (wallpaperInfo != null) { // 如果是動態壁紙,則得到該動態壁紙的包名,並與想知道的動態壁紙包名做比較 String currentLiveWallpaperPackageName = wallpaperInfo.getPackageName(); if (currentLiveWallpaperPackageName.equals(getPackageName())) { return true; } } return false; } /** * 每天打點一次,用戶還在使用視頻桌面 */ private void stateUsed() { if (isLiveWallpaperRunning()) { Date date = new Date(); String curDate = date.getYear() + "-" + date.getMonth() + "-" + date.getDay(); String lastDate = getApplicationContext().getSharedPreferences(VideoLiveWallpaper.VIDEO_WP_ENGINE_PRE, MODE_PRIVATE).getString(SP_KEY_VIDEODESKTOP_USED_LAST_TIME, null); if (!curDate.equals(lastDate)) { getApplicationContext().getSharedPreferences(VideoLiveWallpaper.VIDEO_WP_ENGINE_PRE, MODE_PRIVATE).edit().putString(SP_KEY_VIDEODESKTOP_USED_LAST_TIME, curDate).commit(); //打點...... } } } private void startCheckPath() { if (mMediaPlayer != null) { String spPath = getSPPath(); if (spPath != null && !spPath.equals(mVideoFilePath)) { mVideoFilePath = spPath; try { mMediaPlayer.reset(); mMediaPlayer.setDataSource(mVideoFilePath); if (Build.VERSION.SDK_INT >= 16) { mMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); } mMediaPlayer.setLooping(true); setVolumeByWPSetting(); mMediaPlayer.prepare(); mMediaPlayer.start(); } catch (Exception e) { e.printStackTrace(); onError(mMediaPlayer, 0, 0); } } else { mMediaPlayer.start(); } } } @Override public void onSurfaceCreated(SurfaceHolder holder) { super.onSurfaceCreated(holder); destroy(); if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); mMediaPlayer.setOnErrorListener(this); mMediaPlayer.setSurface(holder.getSurface()); } startCheckPath(); } public boolean onError(MediaPlayer mp, int what, int extra) { //視頻播放,莫名其妙 黑屏,重新設置; Toast.makeText(getApplicationContext(), R.string.video_wallpaper_play_error, Toast.LENGTH_LONG).show(); try { //銷燬內存 destroy(); //清除壁紙 WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext()); wallpaperManager.clear(); //重新設置視頻壁紙 Intent localIntent = new Intent(); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { localIntent.setAction(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER); localIntent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT , new ComponentName(getApplicationContext(), VideoLiveWallpaper.class)); } else { localIntent.setAction(WallpaperManager.ACTION_LIVE_WALLPAPER_CHOOSER); } localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); localIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); getApplicationContext().startActivity(localIntent); } catch (Exception e) { e.printStackTrace(); } return true; } @Override public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { super.onSurfaceChanged(holder, format, width, height); } @Override public void onSurfaceDestroyed(SurfaceHolder holder) { super.onSurfaceDestroyed(holder); } public void destroy() { if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; } //path清空 mVideoFilePath = null; } } }
MediaPlayer的銷燬,最好放到Service的OnDestroy裏面,因爲第一次設置動態壁紙,需要調起系統設置壁紙頁面,設置成功的時候,Engine實際是有兩個(一個系統設置頁面的,一個桌面的),而Service只有一個,系統設置頁面退出後onSurfaceDestroyed會延後調用;防止一些bug;
源碼地址:https://github.com/miLLlulei/WallpaperEngine ,歡迎大家來star;
下面介紹一些其他方法:
/** * 獲取系統默認圖片壁紙 * * @return */ public static String getDefaultWallpaperInfo() { InputStream is = null; Bitmap bmWp = null; try { final String path = HideApiHelper.SystemProperties.get("ro.config.wallpaper"); if (!TextUtils.isEmpty(path)) { // Log.d(TAG, "getDefaultWallpaperInfo path = " + path); final File file = new File(path); if (file.exists()) { is = new FileInputStream(file); } } else { int resourceId = ContextUtils.getApplicationContext().getResources().getIdentifier("default_wallpaper", "drawable", "android"); is = ContextUtils.getApplicationContext().getResources().openRawResource(resourceId); } if (is != null) { BitmapFactory.Options options = new BitmapFactory.Options(); bmWp = BitmapFactory.decodeStream(is, null, options); return FLAG_WP_BITMAP + FLAG_WP_SPLIT + bmWp.getWidth() + FLAG_WP_SPLIT + bmWp.getHeight() + FLAG_WP_SPLIT + BitmapUtils.getBitmapSize(bmWp) + FLAG_WP_SPLIT + bmWp.getPixel(1, 1) ; } } catch (Throwable e) { Log.d(TAG, "Can't decode stream", e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } if (bmWp != null) { bmWp.recycle(); } } return SP_KEY_WP_SETED_STR_DEFAULT; }
/** * 獲取當前壁紙是什麼 */ public static String getCurWallpaperStr() { String wpInfo = SP_KEY_WP_SETED_STR_DEFAULT; try { WallpaperManager wallpaperManager = WallpaperManager.getInstance(ContextUtils.getApplicationContext()); WallpaperInfo liveWp = wallpaperManager.getWallpaperInfo(); if (liveWp == null) { BitmapDrawable drWp = (BitmapDrawable) wallpaperManager.getDrawable(); if (drWp != null) { Bitmap bmWp = drWp.getBitmap(); wpInfo = FLAG_WP_BITMAP + FLAG_WP_SPLIT + bmWp.getWidth() + FLAG_WP_SPLIT + bmWp.getHeight() + FLAG_WP_SPLIT + BitmapUtils.getBitmapSize(bmWp) + FLAG_WP_SPLIT + bmWp.getPixel(1, 1) ; } } else { wpInfo = FLAG_WP_LIVE + FLAG_WP_SPLIT + liveWp.getPackageName() + FLAG_WP_SPLIT + liveWp.getServiceName(); } } catch (Exception e) { //魅族手機,getCurrentWallpaperLocked()可能報錯java.io.IOException: broken file descriptor e.printStackTrace(); } return wpInfo; }
調起系統設置壁紙頁面,動態壁紙的設置,必須調起系統的:
try { Intent localIntent = new Intent(); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {//ICE_CREAM_SANDWICH_MR1 15 localIntent.setAction(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);//android.service.wallpaper.CHANGE_LIVE_WALLPAPER localIntent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT , new ComponentName(pkgName, className)); } else { localIntent.setAction(WallpaperManager.ACTION_LIVE_WALLPAPER_CHOOSER);//android.service.wallpaper.LIVE_WALLPAPER_CHOOSER } localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); localIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); context.startActivity(localIntent); } catch (Exception e) { e.printStackTrace(); }