diff --git a/app/src/main/java/com/github/tvbox/osc/ui/fragment/PlayFragment.java b/app/src/main/java/com/github/tvbox/osc/ui/fragment/PlayFragment.java new file mode 100644 index 00000000..0104bcd4 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/ui/fragment/PlayFragment.java @@ -0,0 +1,1225 @@ +package com.github.tvbox.osc.ui.fragment; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.net.http.SslError; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.ConsoleMessage; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.SslErrorHandler; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + +import com.github.catvod.crawler.Spider; +import com.github.tvbox.osc.R; +import com.github.tvbox.osc.api.ApiConfig; +import com.github.tvbox.osc.base.BaseLazyFragment; +import com.github.tvbox.osc.bean.ParseBean; +import com.github.tvbox.osc.bean.SourceBean; +import com.github.tvbox.osc.bean.VodInfo; +import com.github.tvbox.osc.cache.CacheManager; +import com.github.tvbox.osc.event.RefreshEvent; +import com.github.tvbox.osc.player.controller.VodController; +import com.github.tvbox.osc.player.thirdparty.MXPlayer; +import com.github.tvbox.osc.player.thirdparty.ReexPlayer; +import com.github.tvbox.osc.util.AdBlocker; +import com.github.tvbox.osc.util.DefaultConfig; +import com.github.tvbox.osc.util.HawkConfig; +import com.github.tvbox.osc.util.LOG; +import com.github.tvbox.osc.util.MD5; +import com.github.tvbox.osc.util.PlayerHelper; +import com.github.tvbox.osc.util.XWalkUtils; +import com.github.tvbox.osc.util.thunder.Thunder; +import com.github.tvbox.osc.viewmodel.SourceViewModel; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.callback.AbsCallback; +import com.lzy.okgo.model.HttpHeaders; +import com.lzy.okgo.model.Response; +import com.orhanobut.hawk.Hawk; + +import org.greenrobot.eventbus.EventBus; +import org.json.JSONException; +import org.json.JSONObject; +import org.xwalk.core.XWalkJavascriptResult; +import org.xwalk.core.XWalkResourceClient; +import org.xwalk.core.XWalkSettings; +import org.xwalk.core.XWalkUIClient; +import org.xwalk.core.XWalkView; +import org.xwalk.core.XWalkWebResourceRequest; +import org.xwalk.core.XWalkWebResourceResponse; + +import java.io.ByteArrayInputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import me.jessyan.autosize.AutoSize; +import xyz.doikki.videoplayer.player.ProgressManager; +import xyz.doikki.videoplayer.player.VideoView; + +public class PlayFragment extends BaseLazyFragment { + private VideoView mVideoView; + private TextView mPlayLoadTip; + private ImageView mPlayLoadErr; + private ProgressBar mPlayLoading; + private VodController mController; + private SourceViewModel sourceViewModel; + private Handler mHandler; + + @Override + protected int getLayoutResID() { + return R.layout.activity_play; + } + + @Override + protected void init() { + initView(); + initViewModel(); + initData(); + } + + private void initView() { + mHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(@NonNull Message msg) { + switch (msg.what) { + case 100: + stopParse(); + errorWithRetry("嗅探错误", false); + break; + } + return false; + } + }); + mVideoView = findViewById(R.id.mVideoView); + mPlayLoadTip = findViewById(R.id.play_load_tip); + mPlayLoading = findViewById(R.id.play_loading); + mPlayLoadErr = findViewById(R.id.play_load_error); + mController = new VodController(requireContext()); + mController.setCanChangePosition(true); + mController.setEnableInNormal(true); + mController.setGestureEnabled(true); + ProgressManager progressManager = new ProgressManager() { + @Override + public void saveProgress(String url, long progress) { + CacheManager.save(MD5.string2MD5(url), progress); + } + + @Override + public long getSavedProgress(String url) { + int st = 0; + try { + st = mVodPlayerCfg.getInt("st"); + } catch (JSONException e) { + e.printStackTrace(); + } + long skip = st * 1000; + if (CacheManager.getCache(MD5.string2MD5(url)) == null) { + return skip; + } + long rec = (long) CacheManager.getCache(MD5.string2MD5(url)); + if (rec < skip) + return skip; + return rec; + } + }; + mVideoView.setProgressManager(progressManager); + mController.setListener(new VodController.VodControlListener() { + @Override + public void playNext(boolean rmProgress) { + String preProgressKey = progressKey; + PlayFragment.this.playNext(); + if (rmProgress && preProgressKey != null) + CacheManager.delete(MD5.string2MD5(preProgressKey), 0); + } + + @Override + public void playPre() { + PlayFragment.this.playPrevious(); + } + + @Override + public void changeParse(ParseBean pb) { + autoRetryCount = 0; + doParse(pb); + } + + @Override + public void updatePlayerCfg() { + mVodInfo.playerCfg = mVodPlayerCfg.toString(); + EventBus.getDefault().post(new RefreshEvent(RefreshEvent.TYPE_REFRESH, mVodPlayerCfg)); + } + + @Override + public void replay() { + autoRetryCount = 0; + play(); + } + + @Override + public void errReplay() { + errorWithRetry("视频播放出错", false); + } + }); + mVideoView.setVideoController(mController); + } + + void setTip(String msg, boolean loading, boolean err) { + mPlayLoadTip.setText(msg); + mPlayLoadTip.setVisibility(View.VISIBLE); + mPlayLoading.setVisibility(loading ? View.VISIBLE : View.GONE); + mPlayLoadErr.setVisibility(err ? View.VISIBLE : View.GONE); + } + + void hideTip() { + mPlayLoadTip.setVisibility(View.GONE); + mPlayLoading.setVisibility(View.GONE); + mPlayLoadErr.setVisibility(View.GONE); + } + + void errorWithRetry(String err, boolean finish) { + if (!autoRetry()) { + requireActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + if (finish) { + Toast.makeText(mContext, err, Toast.LENGTH_SHORT).show(); + } else { + setTip(err, false, true); + } + } + }); + } + } + + void playUrl(String url, HashMap headers) { + requireActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + stopParse(); + if (mVideoView != null) { + mVideoView.release(); + if (url != null) { + try { + int playerType = mVodPlayerCfg.getInt("pl"); + if (playerType >= 10) { + VodInfo.VodSeries vs = mVodInfo.seriesMap.get(mVodInfo.playFlag).get(mVodInfo.playIndex); + String playTitle = mVodInfo.name + " " + vs.name; + setTip("调用外部播放器" + PlayerHelper.getPlayerName(playerType) + "进行播放", true, false); + boolean callResult = false; + switch (playerType) { + case 10: { + callResult = MXPlayer.run(requireActivity(), url, playTitle, playSubtitle, headers); + break; + } + case 11: { + callResult = ReexPlayer.run(requireActivity(), url, playTitle, playSubtitle, headers); + break; + } + } + setTip("调用外部播放器" + PlayerHelper.getPlayerName(playerType) + (callResult ? "成功" : "失败"), callResult, !callResult); + return; + } + } catch (JSONException e) { + e.printStackTrace(); + } + hideTip(); + PlayerHelper.updateCfg(mVideoView, mVodPlayerCfg); + mVideoView.setProgressKey(progressKey); + if (headers != null) { + mVideoView.setUrl(url, headers); + } else { + mVideoView.setUrl(url); + } + mVideoView.start(); + mController.resetSpeed(); + } + } + } + }); + } + + private void initViewModel() { + sourceViewModel = new ViewModelProvider(this).get(SourceViewModel.class); + sourceViewModel.playResult.observe(this, new Observer() { + @Override + public void onChanged(JSONObject info) { + if (info != null) { + try { + progressKey = info.optString("proKey", null); + boolean parse = info.optString("parse", "1").equals("1"); + boolean jx = info.optString("jx", "0").equals("1"); + playSubtitle = info.optString("subt", /*"https://dash.akamaized.net/akamai/test/caption_test/ElephantsDream/ElephantsDream_en.vtt"*/""); + String playUrl = info.optString("playUrl", ""); + String flag = info.optString("flag"); + String url = info.getString("url"); + HashMap headers = null; + if (info.has("header")) { + try { + JSONObject hds = new JSONObject(info.getString("header")); + Iterator keys = hds.keys(); + while (keys.hasNext()) { + String key = keys.next(); + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(key, hds.getString(key)); + } + } catch (Throwable th) { + + } + } + if (parse || jx) { + boolean userJxList = (playUrl.isEmpty() && ApiConfig.get().getVipParseFlags().contains(flag)) || jx; + initParse(flag, userJxList, playUrl, url); + } else { + mController.showParse(false); + playUrl(playUrl + url, headers); + } + } catch (Throwable th) { + errorWithRetry("获取播放信息错误", true); + } + } else { + errorWithRetry("获取播放信息错误", true); + } + } + }); + } + + public void setData(Bundle bundle) { + mVodInfo = (VodInfo) bundle.getSerializable("VodInfo"); + sourceKey = bundle.getString("sourceKey"); + sourceBean = ApiConfig.get().getSource(sourceKey); + initPlayerCfg(); + play(); + } + + private void initData() { + /*Intent intent = getIntent(); + if (intent != null && intent.getExtras() != null) { + + }*/ + } + + void initPlayerCfg() { + try { + mVodPlayerCfg = new JSONObject(mVodInfo.playerCfg); + } catch (Throwable th) { + mVodPlayerCfg = new JSONObject(); + } + try { + if (!mVodPlayerCfg.has("pl")) { + mVodPlayerCfg.put("pl", Hawk.get(HawkConfig.PLAY_TYPE, 1)); + } + if (!mVodPlayerCfg.has("pr")) { + mVodPlayerCfg.put("pr", Hawk.get(HawkConfig.PLAY_RENDER, 0)); + } + if (!mVodPlayerCfg.has("ijk")) { + mVodPlayerCfg.put("ijk", Hawk.get(HawkConfig.IJK_CODEC, "")); + } + if (!mVodPlayerCfg.has("sc")) { + mVodPlayerCfg.put("sc", Hawk.get(HawkConfig.PLAY_SCALE, 0)); + } + if (!mVodPlayerCfg.has("sp")) { + mVodPlayerCfg.put("sp", 1.0f); + } + if (!mVodPlayerCfg.has("st")) { + mVodPlayerCfg.put("st", 60); + } + if (!mVodPlayerCfg.has("et")) { + mVodPlayerCfg.put("et", 60); + } + } catch (Throwable th) { + + } + mController.setPlayerConfig(mVodPlayerCfg); + } + + public boolean onBackPressed() { + if (mController.onBackPressed()) { + return true; + } + return false; + } + + public boolean dispatchKeyEvent(KeyEvent event) { + if (event != null) { + if (mController.onKeyEvent(event)) { + return true; + } + } + return false; + } + + @Override + public void onPause() { + super.onPause(); + if (mVideoView != null) { + mVideoView.pause(); + } + } + + @Override + public void onResume() { + super.onResume(); + if (mVideoView != null) { + mVideoView.resume(); + } + } + + @Override + public void onHiddenChanged(boolean hidden) { + if (hidden) { + if (mVideoView != null) { + mVideoView.pause(); + } + } else { + if (mVideoView != null) { + mVideoView.resume(); + } + } + super.onHiddenChanged(hidden); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (mVideoView != null) { + mVideoView.release(); + mVideoView = null; + } + stopLoadWebView(true); + stopParse(); + } + + private VodInfo mVodInfo; + private JSONObject mVodPlayerCfg; + private String sourceKey; + private SourceBean sourceBean; + + private void playNext() { + boolean hasNext = true; + if (mVodInfo == null || mVodInfo.seriesMap.get(mVodInfo.playFlag) == null) { + hasNext = false; + } else { + hasNext = mVodInfo.playIndex + 1 < mVodInfo.seriesMap.get(mVodInfo.playFlag).size(); + } + if (!hasNext) { + Toast.makeText(requireContext(), "已经是最后一集了!", Toast.LENGTH_SHORT).show(); + return; + } + mVodInfo.playIndex++; + play(); + } + + private void playPrevious() { + boolean hasPre = true; + if (mVodInfo == null || mVodInfo.seriesMap.get(mVodInfo.playFlag) == null) { + hasPre = false; + } else { + hasPre = mVodInfo.playIndex - 1 >= 0; + } + if (!hasPre) { + Toast.makeText(requireContext(), "已经是第一集了!", Toast.LENGTH_SHORT).show(); + return; + } + mVodInfo.playIndex--; + play(); + } + + private int autoRetryCount = 0; + + boolean autoRetry() { + if (autoRetryCount < 3) { + autoRetryCount++; + play(); + return true; + } else { + autoRetryCount = 0; + return false; + } + } + + public void play() { + VodInfo.VodSeries vs = mVodInfo.seriesMap.get(mVodInfo.playFlag).get(mVodInfo.playIndex); + EventBus.getDefault().post(new RefreshEvent(RefreshEvent.TYPE_REFRESH, mVodInfo.playIndex)); + setTip("正在获取播放信息", true, false); + String playTitleInfo = mVodInfo.name + " " + vs.name; + mController.setTitle(playTitleInfo); + + playUrl(null, null); + String progressKey = mVodInfo.sourceKey + mVodInfo.id + mVodInfo.playFlag + mVodInfo.playIndex; + if (Thunder.play(vs.url, new Thunder.ThunderCallback() { + @Override + public void status(int code, String info) { + if (code < 0) { + setTip(info, false, true); + } else { + setTip(info, true, false); + } + } + + @Override + public void list(String playList) { + } + + @Override + public void play(String url) { + playUrl(url, null); + } + })) { + mController.showParse(false); + return; + } + sourceViewModel.getPlay(sourceKey, mVodInfo.playFlag, progressKey, vs.url); + } + + private String playSubtitle; + private String progressKey; + private String parseFlag; + private String webUrl; + + private void initParse(String flag, boolean useParse, String playUrl, final String url) { + parseFlag = flag; + webUrl = url; + ParseBean parseBean = null; + mController.showParse(useParse); + if (useParse) { + parseBean = ApiConfig.get().getDefaultParse(); + } else { + if (playUrl.startsWith("json:")) { + parseBean = new ParseBean(); + parseBean.setType(1); + parseBean.setUrl(playUrl.substring(5)); + } else if (playUrl.startsWith("parse:")) { + String parseRedirect = playUrl.substring(6); + for (ParseBean pb : ApiConfig.get().getParseBeanList()) { + if (pb.getName().equals(parseRedirect)) { + parseBean = pb; + break; + } + } + } + if (parseBean == null) { + parseBean = new ParseBean(); + parseBean.setType(0); + parseBean.setUrl(playUrl); + } + } + loadFound = false; + doParse(parseBean); + } + + JSONObject jsonParse(String input, String json) throws JSONException { + JSONObject jsonPlayData = new JSONObject(json); + String url = jsonPlayData.getString("url"); + String msg = jsonPlayData.optString("msg", ""); + if (url.startsWith("//")) { + url = "https:" + url; + } + if (!url.startsWith("http")) { + return null; + } + JSONObject headers = new JSONObject(); + String ua = jsonPlayData.optString("user-agent", ""); + if (ua.trim().length() > 0) { + headers.put("User-Agent", " " + ua); + } + String referer = jsonPlayData.optString("referer", ""); + if (referer.trim().length() > 0) { + headers.put("Referer", " " + referer); + } + JSONObject taskResult = new JSONObject(); + taskResult.put("header", headers); + taskResult.put("url", url); + return taskResult; + } + + void stopParse() { + mHandler.removeMessages(100); + stopLoadWebView(false); + loadFound = false; + OkGo.getInstance().cancelTag("json_jx"); + if (parseThreadPool != null) { + try { + parseThreadPool.shutdown(); + parseThreadPool = null; + } catch (Throwable th) { + th.printStackTrace(); + } + } + } + + ExecutorService parseThreadPool; + + private void doParse(ParseBean pb) { + stopParse(); + if (pb.getType() == 0) { + setTip("正在嗅探播放地址", true, false); + mHandler.removeMessages(100); + mHandler.sendEmptyMessageDelayed(100, 20 * 1000); + loadWebView(pb.getUrl() + webUrl); + } else if (pb.getType() == 1) { // json 解析 + setTip("正在解析播放地址", true, false); + // 解析ext + HttpHeaders reqHeaders = new HttpHeaders(); + try { + JSONObject jsonObject = new JSONObject(pb.getExt()); + if (jsonObject.has("header")) { + JSONObject headerJson = jsonObject.optJSONObject("header"); + Iterator keys = headerJson.keys(); + while (keys.hasNext()) { + String key = keys.next(); + reqHeaders.put(key, headerJson.optString(key, "")); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + OkGo.get(pb.getUrl() + webUrl) + .tag("json_jx") + .headers(reqHeaders) + .execute(new AbsCallback() { + @Override + public String convertResponse(okhttp3.Response response) throws Throwable { + if (response.body() != null) { + return response.body().string(); + } else { + throw new IllegalStateException("网络请求错误"); + } + } + + @Override + public void onSuccess(Response response) { + String json = response.body(); + try { + JSONObject rs = jsonParse(webUrl, json); + HashMap headers = null; + if (rs.has("header")) { + try { + JSONObject hds = rs.getJSONObject("header"); + Iterator keys = hds.keys(); + while (keys.hasNext()) { + String key = keys.next(); + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(key, hds.getString(key)); + } + } catch (Throwable th) { + + } + } + playUrl(rs.getString("url"), headers); + } catch (Throwable e) { + e.printStackTrace(); + errorWithRetry("解析错误", false); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + errorWithRetry("解析错误", false); + } + }); + } else if (pb.getType() == 2) { // json 扩展 + setTip("正在解析播放地址", true, false); + parseThreadPool = Executors.newSingleThreadExecutor(); + LinkedHashMap jxs = new LinkedHashMap<>(); + for (ParseBean p : ApiConfig.get().getParseBeanList()) { + if (p.getType() == 1) { + jxs.put(p.getName(), p.mixUrl()); + } + } + parseThreadPool.execute(new Runnable() { + @Override + public void run() { + JSONObject rs = ApiConfig.get().jsonExt(pb.getUrl(), jxs, webUrl); + if (rs == null || !rs.has("url")) { + errorWithRetry("解析错误", false); + } else { + HashMap headers = null; + if (rs.has("header")) { + try { + JSONObject hds = rs.getJSONObject("header"); + Iterator keys = hds.keys(); + while (keys.hasNext()) { + String key = keys.next(); + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(key, hds.getString(key)); + } + } catch (Throwable th) { + + } + } + if (rs.has("jxFrom")) { + requireActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(mContext, "解析来自:" + rs.optString("jxFrom"), Toast.LENGTH_SHORT).show(); + } + }); + } + boolean parseWV = rs.optInt("parse", 0) == 1; + if (parseWV) { + String wvUrl = DefaultConfig.checkReplaceProxy(rs.optString("url", "")); + loadUrl(wvUrl); + } else { + playUrl(rs.optString("url", ""), headers); + } + } + } + }); + } else if (pb.getType() == 3) { // json 聚合 + setTip("正在解析播放地址", true, false); + parseThreadPool = Executors.newSingleThreadExecutor(); + LinkedHashMap> jxs = new LinkedHashMap<>(); + String extendName = ""; + for (ParseBean p : ApiConfig.get().getParseBeanList()) { + HashMap data = new HashMap(); + data.put("url", p.getUrl()); + if (p.getUrl().equals(pb.getUrl())) { + extendName = p.getName(); + } + data.put("type", p.getType() + ""); + data.put("ext", p.getExt()); + jxs.put(p.getName(), data); + } + String finalExtendName = extendName; + parseThreadPool.execute(new Runnable() { + @Override + public void run() { + JSONObject rs = ApiConfig.get().jsonExtMix(parseFlag + "111", pb.getUrl(), finalExtendName, jxs, webUrl); + if (rs == null || !rs.has("url")) { + errorWithRetry("解析错误", false); + } else { + if (rs.has("parse") && rs.optInt("parse", 0) == 1) { + requireActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + String mixParseUrl = DefaultConfig.checkReplaceProxy(rs.optString("url", "")); + stopParse(); + setTip("正在嗅探播放地址", true, false); + mHandler.removeMessages(100); + mHandler.sendEmptyMessageDelayed(100, 20 * 1000); + loadWebView(mixParseUrl); + } + }); + } else { + HashMap headers = null; + if (rs.has("header")) { + try { + JSONObject hds = rs.getJSONObject("header"); + Iterator keys = hds.keys(); + while (keys.hasNext()) { + String key = keys.next(); + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(key, hds.getString(key)); + } + } catch (Throwable th) { + + } + } + if (rs.has("jxFrom")) { + requireActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(mContext, "解析来自:" + rs.optString("jxFrom"), Toast.LENGTH_SHORT).show(); + } + }); + } + playUrl(rs.optString("url", ""), headers); + } + } + } + }); + } + } + + // webview + private XWalkView mXwalkWebView; + private XWalkWebClient mX5WebClient; + private WebView mSysWebView; + private SysWebClient mSysWebClient; + private Map loadedUrls = new HashMap<>(); + private boolean loadFound = false; + + void loadWebView(String url) { + if (mSysWebView == null && mXwalkWebView == null) { + boolean useSystemWebView = Hawk.get(HawkConfig.PARSE_WEBVIEW, true); + if (!useSystemWebView) { + XWalkUtils.tryUseXWalk(mContext, new XWalkUtils.XWalkState() { + @Override + public void success() { + initWebView(false); + loadUrl(url); + } + + @Override + public void fail() { + Toast.makeText(mContext, "XWalkView不兼容,已替换为系统自带WebView", Toast.LENGTH_SHORT).show(); + initWebView(true); + loadUrl(url); + } + + @Override + public void ignore() { + Toast.makeText(mContext, "XWalkView运行组件未下载,已替换为系统自带WebView", Toast.LENGTH_SHORT).show(); + initWebView(true); + loadUrl(url); + } + }); + } else { + initWebView(true); + loadUrl(url); + } + } else { + loadUrl(url); + } + } + + void initWebView(boolean useSystemWebView) { + if (useSystemWebView) { + mSysWebView = new MyWebView(mContext); + configWebViewSys(mSysWebView); + } else { + mXwalkWebView = new MyXWalkView(mContext); + configWebViewX5(mXwalkWebView); + } + } + + void loadUrl(String url) { + requireActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + if (mXwalkWebView != null) { + mXwalkWebView.stopLoading(); + mXwalkWebView.clearCache(true); + mXwalkWebView.loadUrl(url); + } + if (mSysWebView != null) { + mSysWebView.stopLoading(); + mSysWebView.clearCache(true); + mSysWebView.loadUrl(url); + } + } + }); + } + + void stopLoadWebView(boolean destroy) { + requireActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + + if (mXwalkWebView != null) { + mXwalkWebView.stopLoading(); + mXwalkWebView.loadUrl("about:blank"); + if (destroy) { + mXwalkWebView.clearCache(true); + mXwalkWebView.removeAllViews(); + mXwalkWebView.onDestroy(); + mXwalkWebView = null; + } + } + if (mSysWebView != null) { + mSysWebView.stopLoading(); + mSysWebView.loadUrl("about:blank"); + if (destroy) { + mSysWebView.clearCache(true); + mSysWebView.removeAllViews(); + mSysWebView.destroy(); + mSysWebView = null; + } + } + } + }); + } + + boolean checkVideoFormat(String url) { + if (sourceBean.getType() == 3) { + Spider sp = ApiConfig.get().getCSP(sourceBean); + if (sp != null && sp.manualVideoCheck()) + return sp.isVideoFormat(url); + } + return DefaultConfig.isVideoFormat(url); + } + + class MyWebView extends WebView { + public MyWebView(@NonNull Context context) { + super(context); + } + + @Override + public void setOverScrollMode(int mode) { + super.setOverScrollMode(mode); + if (mContext instanceof Activity) + AutoSize.autoConvertDensityOfCustomAdapt((Activity) mContext, PlayFragment.this); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return false; + } + } + + class MyXWalkView extends XWalkView { + public MyXWalkView(Context context) { + super(context); + } + + @Override + public void setOverScrollMode(int mode) { + super.setOverScrollMode(mode); + if (mContext instanceof Activity) + AutoSize.autoConvertDensityOfCustomAdapt((Activity) mContext, PlayFragment.this); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return false; + } + } + + private void configWebViewSys(WebView webView) { + if (webView == null) { + return; + } + ViewGroup.LayoutParams layoutParams = Hawk.get(HawkConfig.DEBUG_OPEN, false) + ? new ViewGroup.LayoutParams(800, 400) : + new ViewGroup.LayoutParams(1, 1); + webView.setFocusable(false); + webView.setFocusableInTouchMode(false); + webView.clearFocus(); + webView.setOverScrollMode(View.OVER_SCROLL_ALWAYS); + requireActivity().addContentView(webView, layoutParams); + /* 添加webView配置 */ + final WebSettings settings = webView.getSettings(); + settings.setNeedInitialFocus(false); + settings.setAllowContentAccess(true); + settings.setAllowFileAccess(true); + settings.setAllowUniversalAccessFromFileURLs(true); + settings.setAllowFileAccessFromFileURLs(true); + settings.setDatabaseEnabled(true); + settings.setDomStorageEnabled(true); + settings.setJavaScriptEnabled(true); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + settings.setMediaPlaybackRequiresUserGesture(false); + } + if (Hawk.get(HawkConfig.DEBUG_OPEN, false)) { + settings.setBlockNetworkImage(false); + } else { + settings.setBlockNetworkImage(true); + } + settings.setUseWideViewPort(true); + settings.setDomStorageEnabled(true); + settings.setJavaScriptCanOpenWindowsAutomatically(true); + settings.setSupportMultipleWindows(false); + settings.setLoadWithOverviewMode(true); + settings.setBuiltInZoomControls(true); + settings.setSupportZoom(false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + } + settings.setCacheMode(WebSettings.LOAD_NO_CACHE); + /* 添加webView配置 */ + //设置编码 + settings.setDefaultTextEncodingName("utf-8"); + settings.setUserAgentString(webView.getSettings().getUserAgentString()); + // settings.setUserAgentString(ANDROID_UA); + + webView.setWebChromeClient(new WebChromeClient() { + @Override + public boolean onConsoleMessage(ConsoleMessage consoleMessage) { + return false; + } + + @Override + public boolean onJsAlert(WebView view, String url, String message, JsResult result) { + return true; + } + + @Override + public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { + return true; + } + + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + return true; + } + }); + mSysWebClient = new SysWebClient(); + webView.setWebViewClient(mSysWebClient); + webView.setBackgroundColor(Color.BLACK); + } + + private class SysWebClient extends WebViewClient { + + @Override + public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) { + sslErrorHandler.proceed(); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return false; + } + + WebResourceResponse checkIsVideo(String url, HashMap headers) { + if (url.endsWith("/favicon.ico")) { + return new WebResourceResponse("image/png", null, null); + } + LOG.i("shouldInterceptRequest url:" + url); + boolean ad; + if (!loadedUrls.containsKey(url)) { + ad = AdBlocker.isAd(url); + loadedUrls.put(url, ad); + } else { + ad = loadedUrls.get(url); + } + + if (!ad && !loadFound) { + if (checkVideoFormat(url)) { + mHandler.removeMessages(100); + loadFound = true; + if (headers != null && !headers.isEmpty()) { + playUrl(url, headers); + } else { + playUrl(url, null); + } + stopLoadWebView(false); + } + } + + return ad || loadFound ? + AdBlocker.createEmptyResource() : + null; + } + + @Nullable + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + WebResourceResponse response = checkIsVideo(url, null); + if (response == null) + return super.shouldInterceptRequest(view, url); + else + return response; + } + + @Nullable + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + String url = ""; + try { + url = request.getUrl().toString(); + } catch (Throwable th) { + + } + HashMap webHeaders = new HashMap<>(); + try { + Map hds = request.getRequestHeaders(); + for (String k : hds.keySet()) { + if (k.equalsIgnoreCase("user-agent") + || k.equalsIgnoreCase("referer") + || k.equalsIgnoreCase("origin")) { + webHeaders.put(k, " " + hds.get(k)); + } + } + } catch (Throwable th) { + + } + WebResourceResponse response = checkIsVideo(url, webHeaders); + if (response == null) + return super.shouldInterceptRequest(view, request); + else + return response; + } + + @Override + public void onLoadResource(WebView webView, String url) { + super.onLoadResource(webView, url); + } + } + + private void configWebViewX5(XWalkView webView) { + if (webView == null) { + return; + } + ViewGroup.LayoutParams layoutParams = Hawk.get(HawkConfig.DEBUG_OPEN, false) + ? new ViewGroup.LayoutParams(800, 400) : + new ViewGroup.LayoutParams(1, 1); + webView.setFocusable(false); + webView.setFocusableInTouchMode(false); + webView.clearFocus(); + webView.setOverScrollMode(View.OVER_SCROLL_ALWAYS); + requireActivity().addContentView(webView, layoutParams); + /* 添加webView配置 */ + final XWalkSettings settings = webView.getSettings(); + settings.setAllowContentAccess(true); + settings.setAllowFileAccess(true); + settings.setAllowUniversalAccessFromFileURLs(true); + settings.setAllowFileAccessFromFileURLs(true); + settings.setDatabaseEnabled(true); + settings.setDomStorageEnabled(true); + settings.setJavaScriptEnabled(true); + + if (Hawk.get(HawkConfig.DEBUG_OPEN, false)) { + settings.setBlockNetworkImage(false); + } else { + settings.setBlockNetworkImage(true); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + settings.setMediaPlaybackRequiresUserGesture(false); + } + settings.setUseWideViewPort(true); + settings.setDomStorageEnabled(true); + settings.setJavaScriptCanOpenWindowsAutomatically(true); + settings.setSupportMultipleWindows(false); + settings.setLoadWithOverviewMode(true); + settings.setBuiltInZoomControls(true); + settings.setSupportZoom(false); + settings.setCacheMode(WebSettings.LOAD_NO_CACHE); + // settings.setUserAgentString(ANDROID_UA); + + webView.setBackgroundColor(Color.BLACK); + webView.setUIClient(new XWalkUIClient(webView) { + @Override + public boolean onConsoleMessage(XWalkView view, String message, int lineNumber, String sourceId, ConsoleMessageType messageType) { + return false; + } + + @Override + public boolean onJsAlert(XWalkView view, String url, String message, XWalkJavascriptResult result) { + return true; + } + + @Override + public boolean onJsConfirm(XWalkView view, String url, String message, XWalkJavascriptResult result) { + return true; + } + + @Override + public boolean onJsPrompt(XWalkView view, String url, String message, String defaultValue, XWalkJavascriptResult result) { + return true; + } + }); + mX5WebClient = new XWalkWebClient(webView); + webView.setResourceClient(mX5WebClient); + } + + private class XWalkWebClient extends XWalkResourceClient { + public XWalkWebClient(XWalkView view) { + super(view); + } + + @Override + public void onDocumentLoadedInFrame(XWalkView view, long frameId) { + super.onDocumentLoadedInFrame(view, frameId); + } + + @Override + public void onLoadStarted(XWalkView view, String url) { + super.onLoadStarted(view, url); + } + + @Override + public void onLoadFinished(XWalkView view, String url) { + super.onLoadFinished(view, url); + } + + @Override + public void onProgressChanged(XWalkView view, int progressInPercent) { + super.onProgressChanged(view, progressInPercent); + } + + @Override + public XWalkWebResourceResponse shouldInterceptLoadRequest(XWalkView view, XWalkWebResourceRequest request) { + String url = request.getUrl().toString(); + // suppress favicon requests as we don't display them anywhere + if (url.endsWith("/favicon.ico")) { + return createXWalkWebResourceResponse("image/png", null, null); + } + LOG.i("shouldInterceptLoadRequest url:" + url); + boolean ad; + if (!loadedUrls.containsKey(url)) { + ad = AdBlocker.isAd(url); + loadedUrls.put(url, ad); + } else { + ad = loadedUrls.get(url); + } + if (!ad && !loadFound) { + if (checkVideoFormat(url)) { + mHandler.removeMessages(100); + loadFound = true; + HashMap webHeaders = new HashMap<>(); + try { + Map hds = request.getRequestHeaders(); + for (String k : hds.keySet()) { + if (k.equalsIgnoreCase("user-agent") + || k.equalsIgnoreCase("referer") + || k.equalsIgnoreCase("origin")) { + webHeaders.put(k, " " + hds.get(k)); + } + } + } catch (Throwable th) { + + } + if (webHeaders != null && !webHeaders.isEmpty()) { + playUrl(url, webHeaders); + } else { + playUrl(url, null); + } + stopLoadWebView(false); + } + } + return ad || loadFound ? + createXWalkWebResourceResponse("text/plain", "utf-8", new ByteArrayInputStream("".getBytes())) : + super.shouldInterceptLoadRequest(view, request); + } + + @Override + public boolean shouldOverrideUrlLoading(XWalkView view, String s) { + return false; + } + + @Override + public void onReceivedSslError(XWalkView view, ValueCallback callback, SslError error) { + callback.onReceiveValue(true); + } + } + +} \ No newline at end of file