视频壁纸,对于动态壁纸开发,就得用到WallpaperService;
manifest配置:
注意:meta-data的 xml文件,主要是在 系统设置->显示->动态壁纸里面会显示icon和说明;
设置壁纸需要权限:
因为用到视频,所以要用到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();
}