淺談視頻壁紙

視頻壁紙,對於動態壁紙開發,就得用到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();
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章