package skin.support; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.os.AsyncTask; import android.support.annotation.Nullable; import android.text.TextUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import skin.support.app.SkinLayoutInflater; import skin.support.observe.SkinObservable; import skin.support.utils.SkinConstants; import skin.support.utils.SkinFileUtils; import skin.support.utils.SkinLog; import skin.support.utils.SkinPreference; import skin.support.content.res.SkinCompatResources; /** * Created by ximsfei on 17-1-10. */ public class SkinCompatManager extends SkinObservable { private static volatile SkinCompatManager sInstance; private final Object mLock = new Object(); private final Context mAppContext; private boolean mLoading = false; private List<SkinLayoutInflater> mInflaters = new ArrayList<>(); private List<SkinLayoutInflater> mHookInflaters = new ArrayList<>(); public interface SkinLoaderListener { void onStart(); void onSuccess(); void onFailed(String errMsg); } public static SkinCompatManager init(Context context) { if (sInstance == null) { synchronized (SkinCompatManager.class) { if (sInstance == null) { sInstance = new SkinCompatManager(context); } } } return sInstance; } public static SkinCompatManager getInstance() { return sInstance; } private SkinCompatManager(Context context) { mAppContext = context.getApplicationContext(); SkinPreference.init(mAppContext); SkinCompatResources.init(mAppContext); } public SkinCompatManager addInflater(SkinLayoutInflater inflater) { mInflaters.add(inflater); return this; } public List<SkinLayoutInflater> getInflaters() { return mInflaters; } public SkinCompatManager addHookInflater(SkinLayoutInflater inflater) { mHookInflaters.add(inflater); return this; } public List<SkinLayoutInflater> getHookInflaters() { return mHookInflaters; } public String getCurSkinName() { return SkinPreference.getInstance().getSkinName(); } public void restoreDefaultTheme() { loadSkin(""); } public AsyncTask loadSkin() { String skin = SkinPreference.getInstance().getSkinName(); if (TextUtils.isEmpty(skin)) { return null; } return loadSkin(skin, null); } public AsyncTask loadSkin(SkinLoaderListener listener) { String skin = SkinPreference.getInstance().getSkinName(); if (TextUtils.isEmpty(skin)) { return null; } return loadSkin(skin, listener); } public AsyncTask loadSkin(String skinName) { return loadSkin(skinName, null); } public AsyncTask loadSkin(String skinName, final SkinLoaderListener listener) { return new SkinLoadTask(listener).execute(skinName); } private class SkinLoadTask extends AsyncTask<String, Void, String> { private final SkinLoaderListener mListener; public SkinLoadTask(SkinLoaderListener listener) { mListener = listener; } protected void onPreExecute() { if (mListener != null) { mListener.onStart(); } } @Override protected String doInBackground(String... params) { synchronized (mLock) { while (mLoading) { try { mLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } mLoading = true; } try { if (params.length == 1) { if (TextUtils.isEmpty(params[0])) { SkinCompatResources.getInstance().setSkinResource( mAppContext.getResources(), mAppContext.getPackageName()); return params[0]; } SkinLog.d("skinPkgPath", params[0]); String skinPkgPath = SkinFileUtils.getSkinDir(mAppContext) + File.separator + params[0]; // ToDo 方便调试, 每次点击都从assets中读取 // if (!isSkinExists(params[0])) { copySkinFromAssets(params[0]); if (!isSkinExists(params[0])) { return null; } // } String pkgName = initSkinPackageName(skinPkgPath); Resources resources = initSkinResources(skinPkgPath); if (resources != null && !TextUtils.isEmpty(pkgName)) { SkinCompatResources.getInstance().setSkinResource(resources, pkgName); return params[0]; } } } catch (Exception e) { e.printStackTrace(); } SkinCompatResources.getInstance().setSkinResource( mAppContext.getResources(), mAppContext.getPackageName()); return null; } protected void onPostExecute(String skinName) { SkinLog.e("skinName = " + skinName); synchronized (mLock) { if (skinName != null) { notifyUpdateSkin(); SkinPreference.getInstance().setSkinName(skinName).commitEditor(); if (mListener != null) mListener.onSuccess(); } else { SkinPreference.getInstance().setSkinName("").commitEditor(); if (mListener != null) mListener.onFailed("皮肤资源获取失败"); } mLoading = false; mLock.notifyAll(); } } } private String initSkinPackageName(String skinPkgPath) { PackageManager mPm = mAppContext.getPackageManager(); PackageInfo info = mPm.getPackageArchiveInfo(skinPkgPath, PackageManager.GET_ACTIVITIES); return info.packageName; } @Nullable private Resources initSkinResources(String skinPkgPath) { try { AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, skinPkgPath); Resources superRes = mAppContext.getResources(); return new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); } catch (Exception e) { e.printStackTrace(); } return null; } private String copySkinFromAssets(String name) { String skinDir = SkinFileUtils.getSkinDir(mAppContext); String skinPath = skinDir + File.separator + name; try { InputStream is = mAppContext.getAssets().open( SkinConstants.SKIN_DEPLOY_PATH + File.separator + name); File fileDir = new File(skinDir); if (!fileDir.exists()) { fileDir.mkdirs(); } OutputStream os = new FileOutputStream(skinPath); int byteCount; byte[] bytes = new byte[1024]; while ((byteCount = is.read(bytes)) != -1) { os.write(bytes, 0, byteCount); } os.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } return skinPath; } public boolean isSkinExists(String skinName) { return !TextUtils.isEmpty(skinName) && new File(SkinFileUtils.getSkinDir(mAppContext) + File.separator + skinName).exists(); } }