/* * Created by LuaView. * Copyright (c) 2017, Alibaba Group. All rights reserved. * * This source code is licensed under the MIT. * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree. */ package com.taobao.luaview.global; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.text.TextUtils; import com.taobao.luaview.scriptbundle.LuaScriptManager; import com.taobao.luaview.scriptbundle.ScriptBundle; import com.taobao.luaview.scriptbundle.ScriptFile; import com.taobao.luaview.scriptbundle.asynctask.ScriptBundleLoadTask; import com.taobao.luaview.scriptbundle.asynctask.SimpleTask1; import com.taobao.luaview.scriptbundle.asynctask.delegate.ScriptBundleLoadDelegate; import com.taobao.luaview.util.AssetUtil; import com.taobao.luaview.util.DrawableUtil; import com.taobao.luaview.util.FileUtil; import com.taobao.luaview.util.IOUtil; import com.taobao.luaview.util.LogUtil; import com.taobao.luaview.util.ParamUtil; import com.taobao.luaview.util.TypefaceUtil; import com.taobao.luaview.view.imageview.BaseImageView; import org.luaj.vm2.lib.ResourceFinder; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * 给定相对路径,找对应的资源(脚本、图片、字体等) * * @author song * @date 16/1/5 */ public class LuaResourceFinder implements ResourceFinder { public static final String DEFAULT_MAIN_ENTRY = "main.lua";//默认的脚本入口,在加载folder或者bundle的时候会默认加载该名称的脚本 private static final String FILE_PATH_ANDROID_ASSET = "file:///android_asset/"; private Context mContext; //内存脚本 private ScriptBundle mScriptBundle; //加载的uri(可以是url,也可以是包名称) private String mUri; //基础scriptPath private String mBaseScriptFolderPath;//默认cache目录 private String mBaseBundlePath;//Bundle文件系统路径 private String mBaseAssetPath;//asset下路径 //callback for drawable finder public interface DrawableFindCallback { public void onStart(String urlOrPath); public void onFinish(Drawable result); } public LuaResourceFinder(Context context) { if (context != null) { this.mContext = context.getApplicationContext(); } } public void setScriptBundle(ScriptBundle scriptBundle) { mScriptBundle = scriptBundle; } public void setUri(String uri) { mUri = uri; mBaseScriptFolderPath = LuaScriptManager.getBaseScriptFolderPath(); mBaseBundlePath = LuaScriptManager.buildScriptBundleFolderPath(uri); mBaseAssetPath = FileUtil.getAssetFolderPath(uri);//脚本默认放在asset目录下 } public String getUri() { return mUri; } public String getBaseBundlePath() { return mBaseBundlePath; } /** * find Script in given name or path * * @param nameOrPath * @return */ @Override public InputStream findResource(final String nameOrPath) { if (mScriptBundle != null && mScriptBundle.containsKey(nameOrPath)) {//from cache, 这里直接使用文件名称来处理 LogUtil.d("[findResource-ScriptBundle]", nameOrPath); final ScriptFile scriptFile = mScriptBundle.getScriptFile(nameOrPath); return scriptFile.getInputStream(); } if (LuaScriptManager.isLuaEncryptScript(nameOrPath)) {//.lv TODO 这里需要去掉,不再有本地lv文件 return ScriptBundleLoadDelegate.loadEncryptScript(mContext, findFile(nameOrPath)); } else {//.lua 或者 输入folder名字(其实是lvbundle的名字,如ppt440), 则加载 main.lua final String newName = LuaScriptManager.isLuaScript(nameOrPath) ? nameOrPath : DEFAULT_MAIN_ENTRY;//如果是脚本则加载,否则加载main.lua InputStream inputStream = ScriptBundleLoadDelegate.loadEncryptScript(mContext, findFile(LuaScriptManager.changeSuffix(newName, LuaScriptManager.POSTFIX_LV))); if (inputStream == null) {//如果.lv不存在,则尝试读取.lua inputStream = findFile(newName); } return inputStream; } } /** * find drawable async * * @param nameOrPath * @param callback */ public void findDrawable(final String nameOrPath, final DrawableFindCallback callback) { new SimpleTask1<Drawable>() { @Override protected void onPreExecute() { super.onPreExecute(); if (callback != null) { callback.onStart(nameOrPath); } } @Override protected Drawable doInBackground(Object... params) { return findDrawable(nameOrPath); } @Override protected void onPostExecute(Drawable drawable) { super.onPostExecute(drawable); if (callback != null) { callback.onFinish(drawable); } } }.executeInPool(); } /** * find drawable async * * @param imageView * @param nameOrPath */ public void findDrawable(final BaseImageView imageView, final String nameOrPath) { new SimpleTask1<Drawable>() { @Override protected void onPreExecute() { super.onPreExecute(); if (imageView != null && nameOrPath != null) { imageView.setTag(Constants.RES_LV_TAG_URL, nameOrPath); } } @Override protected Drawable doInBackground(Object... params) { return findDrawable(nameOrPath); } @Override protected void onPostExecute(Drawable drawable) { super.onPostExecute(drawable); if (imageView != null && nameOrPath != null && nameOrPath.equals(imageView.getTag(Constants.RES_LV_TAG_URL))) { imageView.setImageDrawable(drawable); } } }.executeInPool(); } /** * 在 res 或者 asset 或者 文件系统 找drawable * TODO 异步 * * @param nameOrPath * @return */ public Drawable findDrawable(final String nameOrPath) { Drawable drawable = null; if (!TextUtils.isEmpty(nameOrPath)) { final String drawableName = FileUtil.hasPostfix(nameOrPath) ? nameOrPath : ParamUtil.getFileNameWithPostfix(nameOrPath, "png");//如果没有后缀,则处理成.png String filepath = buildPathInSdcardIfExists(drawableName); if (filepath != null) {//从filepath加载 drawable = DrawableUtil.getByPath(filepath); } if (drawable == null) {//从asset加载 filepath = buildPathInAssetsIfExists(drawableName); if (filepath != null) { drawable = DrawableUtil.getAssetByPath(mContext, drawableName); } } if (drawable == null) {//直接使用name加载 drawable = DrawableUtil.getByName(mContext, nameOrPath); } } return drawable; } /** * 在 asset 或者 文件系统 找字体文件 * TODO 优化 * * @param nameOrPath * @return */ public Typeface findTypeface(final String nameOrPath) { Typeface typeface = null; if (!TextUtils.isEmpty(nameOrPath)) { final String typefaceNameOrPath = FileUtil.hasPostfix(nameOrPath) ? nameOrPath : ParamUtil.getFileNameWithPostfix(nameOrPath, "ttf");//如果没有后缀,则处理成.ttf String filepath = buildPathInSdcardIfExists(typefaceNameOrPath); if (filepath != null) {//从文件系统加载 typeface = TypefaceUtil.create(filepath); } if (typeface == null) {//从asset下加载 filepath = buildPathInAssetsIfExists(typefaceNameOrPath); if (filepath != null) { typeface = TypefaceUtil.create(mContext, filepath); } } if (typeface == null) {//default name typeface = TypefaceUtil.create(mContext, nameOrPath); } } return typeface != null ? typeface : Typeface.DEFAULT; } /** * 在 文件系统 或者 asset下 找资源 * TODO 异步 * * @param nameOrPath * @return */ public InputStream findFile(final String nameOrPath) { InputStream inputStream = null; if (!TextUtils.isEmpty(nameOrPath)) { String filepath = buildPathInSdcardIfExists(nameOrPath); if (filepath != null) {//从文件系统加载 inputStream = FileUtil.open(filepath); } if (inputStream == null) {//从asset下加载 filepath = buildPathInAssetsIfExists(nameOrPath); if (filepath != null) { inputStream = AssetUtil.open(mContext, filepath); } } if(inputStream == null){//直接使用name加载 inputStream = AssetUtil.open(mContext, nameOrPath); } } return inputStream; } /** * exists * * @param nameOrPath * @return */ public boolean exists(final String nameOrPath) { if (!TextUtils.isEmpty(nameOrPath)) { String fullPath = buildSecurePathInSdcard(nameOrPath); if (fullPath != null) {//文件 return FileUtil.exists(fullPath); } else { fullPath = buildSecurePathInAssets(nameOrPath); return AssetUtil.exists(mContext, fullPath); } } return false; } /** * 获取文件的完整路径 * * @param nameOrPath * @return */ public String buildFullPathInBundleOrAssets(final String nameOrPath) { if (!TextUtils.isEmpty(nameOrPath)) { String fullPath = buildPathInBundleFolder(nameOrPath); if (!FileUtil.exists(fullPath)) { fullPath = buildPathInAssets(nameOrPath); } return fullPath; } return null; } /** * 找文件在SD卡的路径,如果存在在获取 * * @param nameOrPath * @return */ private String buildPathInSdcardIfExists(final String nameOrPath) { String filepath = buildPathInBundleFolder(nameOrPath); if (FileUtil.exists(filepath)) {//check bundle folder return filepath; } if (filepath == null) {//check script folder filepath = buildPathInRootFolder(nameOrPath); if (FileUtil.exists(filepath)) { return filepath; } } return null; } /** * 获取安全路径(不一定在bundle下面,但必须在script目录下) * * @param nameOrPath * @return */ public String buildSecurePathInSdcard(final String nameOrPath) {//主要用在文件操作 String path = buildPathInBundleFolder(nameOrPath); if (path != null) {//处理../../../ TODO 这里如何应对全局路径 final String canonicalPath = FileUtil.getCanonicalPath(path); if (canonicalPath != null && canonicalPath.startsWith(mBaseScriptFolderPath)) { return path; } else { LogUtil.e("[LuaView-Error buildSecurePathInSdcard error]", nameOrPath, "must in folder", mBaseScriptFolderPath); return null; } } return null; } /** * build file path * * @param nameOrPath * @return */ public String buildPathInBundleFolder(final String nameOrPath) { String result = null; if(nameOrPath != null && nameOrPath.startsWith("/")){//如果反斜杠开头,则直接返回,TODO 待优化 return nameOrPath; } if (!TextUtils.isEmpty(mBaseBundlePath)) { if (!FileUtil.isContainsFolderPath(nameOrPath, mBaseBundlePath)) {//不带基础路径的情况 final String filePath = FileUtil.buildPath(mBaseBundlePath, nameOrPath); LogUtil.d("[buildPathInBundleFolder-FileSystem]", filePath); result = filePath; } else { LogUtil.d("[buildPathInBundleFolder-FileSystem]", nameOrPath); result = nameOrPath; } } return result; } /** * build file path * * @param nameOrPath * @return */ public String buildPathInRootFolder(final String nameOrPath) { String result = null; if(nameOrPath != null && nameOrPath.startsWith("/")){//如果反斜杠开头,则直接返回,TODO 待优化 return nameOrPath; } if (!TextUtils.isEmpty(mBaseScriptFolderPath)) { if (!FileUtil.isContainsFolderPath(nameOrPath, mBaseScriptFolderPath)) {//不带基础路径的情况 final String filePath = FileUtil.buildPath(mBaseScriptFolderPath, nameOrPath); LogUtil.d("[buildPathInBundleFolder-FileSystem]", filePath); result = filePath; } else { LogUtil.d("[buildPathInBundleFolder-FileSystem]", nameOrPath); result = nameOrPath; } } return result; } /** * 获取安全路径 * * @param nameOrPath * @return */ public String buildSecurePathInAssets(final String nameOrPath) {//主要用在文件操作 String path = buildPathInAssets(nameOrPath); if (path != null) {//处理../../../ final String canonicalPath = FileUtil.getCanonicalPath(path); return canonicalPath != null && canonicalPath.startsWith(mBaseAssetPath) ? path : null; } return null; } /** * build path in assets * * @param nameOrPath * @return */ private String buildPathInAssetsIfExists(final String nameOrPath) { String filepath = buildPathInAssets(nameOrPath); if (AssetUtil.exists(mContext, filepath)) { return filepath; } else if (AssetUtil.exists(mContext, nameOrPath)) { return nameOrPath; } return null; } /** * 找文件在asset下的路径 * * @param nameOrPath * @return */ private String buildPathInAssets(final String nameOrPath) { String result = null; if(nameOrPath != null && nameOrPath.startsWith("/")){//如果反斜杠开头,则直接返回,TODO 待优化 return nameOrPath; } if (!TextUtils.isEmpty(mBaseAssetPath) && !FileUtil.isContainsFolderPath(nameOrPath, mBaseAssetPath)) {//不带基础路径,或者不在asset下 final String assetFilePath = FileUtil.buildPath(mBaseAssetPath, nameOrPath); LogUtil.d("[buildPathInAssets-Assets]", assetFilePath); result = assetFilePath; } else { LogUtil.d("[buildPathInAssets-Assets]", nameOrPath); result = nameOrPath; } return result; } public byte[] readFromAssets(String name) { AssetManager assetManager = this.mContext.getAssets(); InputStream inputStream = null; try { inputStream = assetManager.open(name); return IOUtil.toBytes(inputStream); } catch (IOException e) { e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (Exception e) { e.printStackTrace(); } } return null; } }