From b23e08de5bb8bf904bdecae4a3bb17a22901500c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8E=E4=BF=8A?= <215613905@qq.com> Date: Wed, 5 Mar 2025 11:47:35 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9B=B4=E6=92=AD=E9=85=8D=E7=BD=AE=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E8=AE=BE=E7=BD=AE(=E5=85=BC=E5=AE=B9=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E4=BC=A0=E5=85=A5.txt,.m3u=E7=9A=84=E7=9B=B4=E6=92=AD?= =?UTF-8?q?=E5=9C=B0=E5=9D=80);=E4=BC=98=E5=8C=96=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E7=AD=9B=E9=80=89=E5=8D=B3=E6=97=B6=E7=94=9F=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/github/tvbox/osc/api/ApiConfig.java | 272 +++++++++++++----- .../tvbox/osc/ui/activity/HomeActivity.java | 13 +- .../osc/ui/activity/SettingActivity.java | 5 +- .../github/tvbox/osc/ui/dialog/ApiDialog.java | 47 ++- .../tvbox/osc/ui/dialog/GridFilterDialog.java | 2 +- .../tvbox/osc/ui/fragment/GridFragment.java | 101 +++++-- .../com/github/tvbox/osc/util/FileUtils.java | 42 ++- .../com/github/tvbox/osc/util/HawkConfig.java | 1 + .../tvbox/osc/util/VideoParseRuler.java | 1 + app/src/main/res/layout/dialog_api.xml | 155 +++++++--- app/src/main/res/values/dimens.xml | 1 + gradle.properties | 2 +- 12 files changed, 458 insertions(+), 184 deletions(-) diff --git a/app/src/main/java/com/github/tvbox/osc/api/ApiConfig.java b/app/src/main/java/com/github/tvbox/osc/api/ApiConfig.java index 64577901..481811b5 100644 --- a/app/src/main/java/com/github/tvbox/osc/api/ApiConfig.java +++ b/app/src/main/java/com/github/tvbox/osc/api/ApiConfig.java @@ -20,6 +20,7 @@ import com.github.tvbox.osc.server.ControlManager; import com.github.tvbox.osc.util.AES; import com.github.tvbox.osc.util.AdBlocker; import com.github.tvbox.osc.util.DefaultConfig; +import com.github.tvbox.osc.util.FileUtils; import com.github.tvbox.osc.util.HawkConfig; import com.github.tvbox.osc.util.LOG; import com.github.tvbox.osc.util.M3u8; @@ -73,15 +74,18 @@ public class ApiConfig { private JarLoader jarLoader = new JarLoader(); private JsLoader jsLoader = new JsLoader(); + private Gson gson; private String userAgent = "okhttp/3.15"; private String requestAccept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"; + private String defaultLiveObjString="{\"lives\":[{\"name\":\"txt_m3u\",\"type\":0,\"url\":\"txt_m3u_url\"}]}"; private ApiConfig() { sourceBeanList = new LinkedHashMap<>(); liveChannelGroupList = new ArrayList<>(); parseBeanList = new ArrayList<>(); + gson = new Gson(); } public static ApiConfig get() { @@ -133,23 +137,8 @@ public class ApiConfig { return "".getBytes(); } - public void loadConfig(boolean useCache, LoadConfigCallback callback, Activity activity) { - String apiUrl = Hawk.get(HawkConfig.API_URL, ""); - if (apiUrl.isEmpty()) { - callback.error("-1"); - return; - } - File cache = new File(App.getInstance().getFilesDir().getAbsolutePath() + "/" + MD5.encode(apiUrl)); - if (useCache && cache.exists()) { - try { - parseJson(apiUrl, cache); - callback.success(); - return; - } catch (Throwable th) { - th.printStackTrace(); - } - } - String TempKey = null, configUrl = "", pk = ";pk;"; + private String configUrl(String apiUrl){ + String configUrl = "", pk = ";pk;"; apiUrl=apiUrl.replace("file://", "clan://localhost/"); if (apiUrl.contains(pk)) { String[] a = apiUrl.split(pk); @@ -168,9 +157,98 @@ public class ApiConfig { } else { configUrl = apiUrl; } + return configUrl; + } + private String TempKey = null; + public void loadConfig(boolean useCache, LoadConfigCallback callback, Activity activity) { + String apiUrl = Hawk.get(HawkConfig.API_URL, ""); + if (apiUrl.isEmpty()) { + callback.error("-1"); + return; + } + //独立加载直播配置 + String liveApiUrl = Hawk.get(HawkConfig.LIVE_API_URL, ""); + String liveApiConfigUrl=configUrl(liveApiUrl); + if(!liveApiUrl.isEmpty() && !liveApiUrl.equals(apiUrl)){ + if(liveApiUrl.contains(".txt") || liveApiUrl.contains(".m3u")){ + initLiveSettings(); + defaultLiveObjString = defaultLiveObjString.replace("txt_m3u_url",liveApiConfigUrl); + parseLiveJson(liveApiUrl,defaultLiveObjString); + }else { + File live_cache = new File(App.getInstance().getFilesDir().getAbsolutePath() + "/" + MD5.encode(liveApiUrl)); + if (useCache && live_cache.exists()) { + try { + parseLiveJson(liveApiUrl, live_cache); + } catch (Throwable th) { + th.printStackTrace(); + } + }else { + OkGo.get(liveApiConfigUrl) + .headers("User-Agent", userAgent) + .headers("Accept", requestAccept) + .execute(new AbsCallback() { + @Override + public void onSuccess(Response response) { + try { + String json = response.body(); + parseLiveJson(liveApiUrl, json); + FileUtils.saveCache(live_cache,json); + } catch (Throwable th) { + th.printStackTrace(); + callback.notice("解析直播配置失败"); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + if (live_cache.exists()) { + try { + parseLiveJson(liveApiUrl, live_cache); + callback.success(); + return; + } catch (Throwable th) { + th.printStackTrace(); + } + } + callback.notice("直播配置拉取失败"); + } + + public String convertResponse(okhttp3.Response response) throws Throwable { + String result = ""; + if (response.body() == null) { + result = ""; + }else { + result=response.body().string(); + if (liveApiUrl.startsWith("clan")) { + result = clanContentFix(clanToAddress(liveApiUrl), result); + } + //假相對路徑 + result = fixContentPath(liveApiUrl,result); + } + return result; + } + }); + } + } + } + + File cache = new File(App.getInstance().getFilesDir().getAbsolutePath() + "/" + MD5.encode(apiUrl)); + if (useCache && cache.exists()) { + try { + parseJson(apiUrl, cache); + callback.success(); + return; + } catch (Throwable th) { + th.printStackTrace(); + } + } + + +// String finalApiUrl = apiUrl; + String configUrl=configUrl(apiUrl); String configKey = TempKey; - String finalApiUrl = apiUrl; OkGo.get(configUrl) .headers("User-Agent", userAgent) .headers("Accept", requestAccept) @@ -179,20 +257,8 @@ public class ApiConfig { public void onSuccess(Response response) { try { String json = response.body(); - parseJson(finalApiUrl, json); - try { - File cacheDir = cache.getParentFile(); - if (!cacheDir.exists()) - cacheDir.mkdirs(); - if (cache.exists()) - cache.delete(); - FileOutputStream fos = new FileOutputStream(cache); - fos.write(json.getBytes("UTF-8")); - fos.flush(); - fos.close(); - } catch (Throwable th) { - th.printStackTrace(); - } + parseJson(apiUrl, json); + FileUtils.saveCache(cache,json); callback.success(); } catch (Throwable th) { th.printStackTrace(); @@ -205,7 +271,7 @@ public class ApiConfig { super.onError(response); if (cache.exists()) { try { - parseJson(finalApiUrl, cache); + parseJson(apiUrl, cache); callback.success(); return; } catch (Throwable th) { @@ -223,17 +289,16 @@ public class ApiConfig { result = FindResult(response.body().string(), configKey); } - if (finalApiUrl.startsWith("clan")) { - result = clanContentFix(clanToAddress(finalApiUrl), result); + if (apiUrl.startsWith("clan")) { + result = clanContentFix(clanToAddress(apiUrl), result); } //假相對路徑 - result = fixContentPath(finalApiUrl,result); + result = fixContentPath(apiUrl,result); return result; } }); } - public void loadJar(boolean useCache, String spider, LoadConfigCallback callback) { String[] urls = spider.split(";md5;"); String jarUrl = urls[0]; @@ -312,7 +377,7 @@ public class ApiConfig { } private void parseJson(String apiUrl, String jsonStr) { - JsonObject infoJson = new Gson().fromJson(jsonStr, JsonObject.class); + JsonObject infoJson = gson.fromJson(jsonStr, JsonObject.class); // spider spider = DefaultConfig.safeJsonString(infoJson, "spider", ""); // wallpaper @@ -382,31 +447,45 @@ public class ApiConfig { } // 直播源 - initLiveSettings(); - if(infoJson.has("lives")){ - JsonArray lives_groups=infoJson.get("lives").getAsJsonArray(); - int live_group_index=Hawk.get(HawkConfig.LIVE_GROUP_INDEX,0); - if(live_group_index>lives_groups.size()-1)Hawk.put(HawkConfig.LIVE_GROUP_INDEX,0); - Hawk.put(HawkConfig.LIVE_GROUP_LIST,lives_groups); - //加载多源配置 - try { - ArrayList liveSettingItemList = new ArrayList<>(); - for (int i=0; i< lives_groups.size();i++) { - JsonObject jsonObject = lives_groups.get(i).getAsJsonObject(); - String name = jsonObject.has("name")?jsonObject.get("name").getAsString():"线路"+(i+1); - LiveSettingItem liveSettingItem = new LiveSettingItem(); - liveSettingItem.setItemIndex(i); - liveSettingItem.setItemName(name); - liveSettingItemList.add(liveSettingItem); + if(apiUrl.equals(Hawk.get(HawkConfig.LIVE_API_URL,""))){ + LOG.i("echo-load-config_live"); + initLiveSettings(); + if(infoJson.has("lives")){ + JsonArray lives_groups=infoJson.get("lives").getAsJsonArray(); + int live_group_index=Hawk.get(HawkConfig.LIVE_GROUP_INDEX,0); + if(live_group_index>lives_groups.size()-1)Hawk.put(HawkConfig.LIVE_GROUP_INDEX,0); + Hawk.put(HawkConfig.LIVE_GROUP_LIST,lives_groups); + //加载多源配置 + try { + ArrayList liveSettingItemList = new ArrayList<>(); + for (int i=0; i< lives_groups.size();i++) { + JsonObject jsonObject = lives_groups.get(i).getAsJsonObject(); + String name = jsonObject.has("name")?jsonObject.get("name").getAsString():"线路"+(i+1); + LiveSettingItem liveSettingItem = new LiveSettingItem(); + liveSettingItem.setItemIndex(i); + liveSettingItem.setItemName(name); + liveSettingItemList.add(liveSettingItem); + } + liveSettingGroupList.get(5).setLiveSettingItems(liveSettingItemList); + } catch (Exception e) { + // 捕获任何可能发生的异常 + e.printStackTrace(); } - liveSettingGroupList.get(5).setLiveSettingItems(liveSettingItemList); - } catch (Exception e) { - // 捕获任何可能发生的异常 - e.printStackTrace(); - } - JsonObject livesOBJ = lives_groups.get(live_group_index).getAsJsonObject(); - loadLiveApi(livesOBJ); + JsonObject livesOBJ = lives_groups.get(live_group_index).getAsJsonObject(); + loadLiveApi(livesOBJ); + } + myHosts = new HashMap<>(); + if (infoJson.has("hosts")) { + JsonArray hostsArray = infoJson.getAsJsonArray("hosts"); + for (int i = 0; i < hostsArray.size(); i++) { + String entry = hostsArray.get(i).getAsString(); + String[] parts = entry.split("=", 2); // 只分割一次,防止 value 里有 = + if (parts.length == 2) { + myHosts.put(parts[0], parts[1]); + } + } + } } //video parse rule for host @@ -457,17 +536,7 @@ public class ApiConfig { } } } - myHosts = new HashMap<>(); - if (infoJson.has("hosts")) { - JsonArray hostsArray = infoJson.getAsJsonArray("hosts"); - for (int i = 0; i < hostsArray.size(); i++) { - String entry = hostsArray.get(i).getAsString(); - String[] parts = entry.split("=", 2); // 只分割一次,防止 value 里有 = - if (parts.length == 2) { - myHosts.put(parts[0], parts[1]); - } - } - } + if (infoJson.has("doh")) { String doh_json = infoJson.getAsJsonArray("doh").toString(); Hawk.put(HawkConfig.DOH_JSON,doh_json); @@ -478,7 +547,7 @@ public class ApiConfig { LOG.i("echo-api-config-----------load"); String defaultIJKADS="{\"ijk\":[{\"options\":[{\"name\":\"opensles\",\"category\":4,\"value\":\"0\"},{\"name\":\"framedrop\",\"category\":4,\"value\":\"1\"},{\"name\":\"soundtouch\",\"category\":4,\"value\":\"1\"},{\"name\":\"start-on-prepared\",\"category\":4,\"value\":\"1\"},{\"name\":\"http-detect-rangeupport\",\"category\":1,\"value\":\"0\"},{\"name\":\"fflags\",\"category\":1,\"value\":\"fastseek\"},{\"name\":\"skip_loop_filter\",\"category\":2,\"value\":\"48\"},{\"name\":\"reconnect\",\"category\":4,\"value\":\"1\"},{\"name\":\"enable-accurate-seek\",\"category\":4,\"value\":\"0\"},{\"name\":\"mediacodec\",\"category\":4,\"value\":\"0\"},{\"name\":\"mediacodec-all-videos\",\"category\":4,\"value\":\"0\"},{\"name\":\"mediacodec-auto-rotate\",\"category\":4,\"value\":\"0\"},{\"name\":\"mediacodec-handle-resolution-change\",\"category\":4,\"value\":\"0\"},{\"name\":\"mediacodec-hevc\",\"category\":4,\"value\":\"0\"},{\"name\":\"max-buffer-size\",\"category\":4,\"value\":\"15728640\"}],\"group\":\"软解码\"},{\"options\":[{\"name\":\"opensles\",\"category\":4,\"value\":\"0\"},{\"name\":\"framedrop\",\"category\":4,\"value\":\"1\"},{\"name\":\"soundtouch\",\"category\":4,\"value\":\"1\"},{\"name\":\"start-on-prepared\",\"category\":4,\"value\":\"1\"},{\"name\":\"http-detect-rangeupport\",\"category\":1,\"value\":\"0\"},{\"name\":\"fflags\",\"category\":1,\"value\":\"fastseek\"},{\"name\":\"skip_loop_filter\",\"category\":2,\"value\":\"48\"},{\"name\":\"reconnect\",\"category\":4,\"value\":\"1\"},{\"name\":\"enable-accurate-seek\",\"category\":4,\"value\":\"0\"},{\"name\":\"mediacodec\",\"category\":4,\"value\":\"1\"},{\"name\":\"mediacodec-all-videos\",\"category\":4,\"value\":\"1\"},{\"name\":\"mediacodec-auto-rotate\",\"category\":4,\"value\":\"1\"},{\"name\":\"mediacodec-handle-resolution-change\",\"category\":4,\"value\":\"1\"},{\"name\":\"mediacodec-hevc\",\"category\":4,\"value\":\"1\"},{\"name\":\"max-buffer-size\",\"category\":4,\"value\":\"15728640\"}],\"group\":\"硬解码\"}],\"ads\":[\"mimg.0c1q0l.cn\",\"www.googletagmanager.com\",\"www.google-analytics.com\",\"mc.usihnbcq.cn\",\"mg.g1mm3d.cn\",\"mscs.svaeuzh.cn\",\"cnzz.hhttm.top\",\"tp.vinuxhome.com\",\"cnzz.mmstat.com\",\"www.baihuillq.com\",\"s23.cnzz.com\",\"z3.cnzz.com\",\"c.cnzz.com\",\"stj.v1vo.top\",\"z12.cnzz.com\",\"img.mosflower.cn\",\"tips.gamevvip.com\",\"ehwe.yhdtns.com\",\"xdn.cqqc3.com\",\"www.jixunkyy.cn\",\"sp.chemacid.cn\",\"hm.baidu.com\",\"s9.cnzz.com\",\"z6.cnzz.com\",\"um.cavuc.com\",\"mav.mavuz.com\",\"wofwk.aoidf3.com\",\"z5.cnzz.com\",\"xc.hubeijieshikj.cn\",\"tj.tianwenhu.com\",\"xg.gars57.cn\",\"k.jinxiuzhilv.com\",\"cdn.bootcss.com\",\"ppl.xunzhuo123.com\",\"xomk.jiangjunmh.top\",\"img.xunzhuo123.com\",\"z1.cnzz.com\",\"s13.cnzz.com\",\"xg.huataisangao.cn\",\"z7.cnzz.com\",\"xg.huataisangao.cn\",\"z2.cnzz.com\",\"s96.cnzz.com\",\"q11.cnzz.com\",\"thy.dacedsfa.cn\",\"xg.whsbpw.cn\",\"s19.cnzz.com\",\"z8.cnzz.com\",\"s4.cnzz.com\",\"f5w.as12df.top\",\"ae01.alicdn.com\",\"www.92424.cn\",\"k.wudejia.com\",\"vivovip.mmszxc.top\",\"qiu.xixiqiu.com\",\"cdnjs.hnfenxun.com\",\"cms.qdwght.com\"]}"; - JsonObject defaultJson=new Gson().fromJson(defaultIJKADS, JsonObject.class); + JsonObject defaultJson=gson.fromJson(defaultIJKADS, JsonObject.class); // 广告地址 if(AdBlocker.isEmpty()){ //默认广告拦截 @@ -528,6 +597,60 @@ public class ApiConfig { } } + private void parseLiveJson(String apiUrl, File f) throws Throwable { + BufferedReader bReader = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8")); + StringBuilder sb = new StringBuilder(); + String s = ""; + while ((s = bReader.readLine()) != null) { + sb.append(s + "\n"); + } + bReader.close(); + parseLiveJson(apiUrl, sb.toString()); + } + private void parseLiveJson(String apiUrl, String jsonStr) { + JsonObject infoJson = gson.fromJson(jsonStr, JsonObject.class); + // 直播源 + initLiveSettings(); + if(infoJson.has("lives")){ + JsonArray lives_groups=infoJson.get("lives").getAsJsonArray(); + int live_group_index=Hawk.get(HawkConfig.LIVE_GROUP_INDEX,0); + if(live_group_index>lives_groups.size()-1)Hawk.put(HawkConfig.LIVE_GROUP_INDEX,0); + Hawk.put(HawkConfig.LIVE_GROUP_LIST,lives_groups); + //加载多源配置 + try { + ArrayList liveSettingItemList = new ArrayList<>(); + for (int i=0; i< lives_groups.size();i++) { + JsonObject jsonObject = lives_groups.get(i).getAsJsonObject(); + String name = jsonObject.has("name")?jsonObject.get("name").getAsString():"线路"+(i+1); + LiveSettingItem liveSettingItem = new LiveSettingItem(); + liveSettingItem.setItemIndex(i); + liveSettingItem.setItemName(name); + liveSettingItemList.add(liveSettingItem); + } + liveSettingGroupList.get(5).setLiveSettingItems(liveSettingItemList); + } catch (Exception e) { + // 捕获任何可能发生的异常 + e.printStackTrace(); + } + + JsonObject livesOBJ = lives_groups.get(live_group_index).getAsJsonObject(); + loadLiveApi(livesOBJ); + } + + myHosts = new HashMap<>(); + if (infoJson.has("hosts")) { + JsonArray hostsArray = infoJson.getAsJsonArray("hosts"); + for (int i = 0; i < hostsArray.size(); i++) { + String entry = hostsArray.get(i).getAsString(); + String[] parts = entry.split("=", 2); // 只分割一次,防止 value 里有 = + if (parts.length == 2) { + myHosts.put(parts[0], parts[1]); + } + } + } + LOG.i("echo-api-live-config-----------load"); + } + private final List liveSettingGroupList = new ArrayList<>(); private void initLiveSettings() { ArrayList groupNames = new ArrayList<>(Arrays.asList("线路选择", "画面比例", "播放解码", "超时换源", "偏好设置", "多源切换")); @@ -687,9 +810,8 @@ public class ApiConfig { public interface LoadConfigCallback { void success(); - void retry(); - void error(String msg); + void notice(String msg); } public interface FastParseCallback { diff --git a/app/src/main/java/com/github/tvbox/osc/ui/activity/HomeActivity.java b/app/src/main/java/com/github/tvbox/osc/ui/activity/HomeActivity.java index d545897f..a264f99e 100644 --- a/app/src/main/java/com/github/tvbox/osc/ui/activity/HomeActivity.java +++ b/app/src/main/java/com/github/tvbox/osc/ui/activity/HomeActivity.java @@ -292,8 +292,13 @@ public class HomeActivity extends BaseActivity { } @Override - public void retry() { - + public void notice(String msg) { + mHandler.post(new Runnable() { + @Override + public void run() { + Toast.makeText(HomeActivity.this, msg, Toast.LENGTH_SHORT).show(); + } + }); } @Override @@ -315,11 +320,11 @@ public class HomeActivity extends BaseActivity { TipDialog dialog = null; @Override - public void retry() { + public void notice(String msg) { mHandler.post(new Runnable() { @Override public void run() { - initData(); + Toast.makeText(HomeActivity.this, msg, Toast.LENGTH_SHORT).show(); } }); } diff --git a/app/src/main/java/com/github/tvbox/osc/ui/activity/SettingActivity.java b/app/src/main/java/com/github/tvbox/osc/ui/activity/SettingActivity.java index 8fe585f4..5049333d 100644 --- a/app/src/main/java/com/github/tvbox/osc/ui/activity/SettingActivity.java +++ b/app/src/main/java/com/github/tvbox/osc/ui/activity/SettingActivity.java @@ -46,6 +46,7 @@ public class SettingActivity extends BaseActivity { private String currentApi; private int homeRec; private int dnsOpt; + private String currentLiveApi; @Override protected int getLayoutResID() { @@ -110,6 +111,7 @@ public class SettingActivity extends BaseActivity { homeSourceKey = ApiConfig.get().getHomeSourceBean().getKey(); homeRec = Hawk.get(HawkConfig.HOME_REC, 0); dnsOpt = Hawk.get(HawkConfig.DOH_URL, 0); + currentLiveApi = Hawk.get(HawkConfig.LIVE_API_URL, ""); List sortList = new ArrayList<>(); sortList.add("设置其他"); sortAdapter.setNewData(sortList); @@ -180,7 +182,8 @@ public class SettingActivity extends BaseActivity { if ((homeSourceKey != null && !homeSourceKey.equals(Hawk.get(HawkConfig.HOME_API, ""))) || !currentApi.equals(Hawk.get(HawkConfig.API_URL, "")) || homeRec != Hawk.get(HawkConfig.HOME_REC, 0) || - dnsOpt != Hawk.get(HawkConfig.DOH_URL, 0)) { + dnsOpt != Hawk.get(HawkConfig.DOH_URL, 0) || + !currentLiveApi.equals(Hawk.get(HawkConfig.LIVE_API_URL, ""))) { AppManager.getInstance().finishAllActivity(); if (currentApi.equals(Hawk.get(HawkConfig.API_URL, ""))) { Bundle bundle = new Bundle(); diff --git a/app/src/main/java/com/github/tvbox/osc/ui/dialog/ApiDialog.java b/app/src/main/java/com/github/tvbox/osc/ui/dialog/ApiDialog.java index f90d31ef..049e044e 100644 --- a/app/src/main/java/com/github/tvbox/osc/ui/dialog/ApiDialog.java +++ b/app/src/main/java/com/github/tvbox/osc/ui/dialog/ApiDialog.java @@ -42,6 +42,7 @@ public class ApiDialog extends BaseDialog { private ImageView ivQRCode; private TextView tvAddress; private EditText inputApi; + private EditText inputApiLive; @Subscribe(threadMode = ThreadMode.MAIN) public void refresh(RefreshEvent event) { @@ -57,8 +58,10 @@ public class ApiDialog extends BaseDialog { ivQRCode = findViewById(R.id.ivQRCode); tvAddress = findViewById(R.id.tvAddress); inputApi = findViewById(R.id.input); + inputApiLive = findViewById(R.id.inputLive); //内置网络接口在此处添加 inputApi.setText(Hawk.get(HawkConfig.API_URL, "")); + inputApiLive.setText(Hawk.get(HawkConfig.LIVE_API_URL, Hawk.get(HawkConfig.API_URL))); findViewById(R.id.inputSubmit).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -70,34 +73,59 @@ public class ApiDialog extends BaseDialog { if (history.size() > 30) history.remove(30); Hawk.put(HawkConfig.API_HISTORY, history); +// String newLiveApi = inputApi.getText().toString().trim(); + if(!newApi.equals(Hawk.get(HawkConfig.API_URL, newApi))){ + inputApiLive.setText(newApi); + Hawk.put(HawkConfig.LIVE_API_URL, newApi); + } listener.onchange(newApi); dismiss(); } } }); + findViewById(R.id.inputSubmitLive).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String newApi = inputApiLive.getText().toString().trim(); + if (!newApi.isEmpty()) { + ArrayList history = Hawk.get(HawkConfig.LIVE_API_HISTORY, new ArrayList()); + if (!history.contains(newApi)) + history.add(0, newApi); + if (history.size() > 30) + history.remove(30); + Hawk.put(HawkConfig.LIVE_API_HISTORY, history); + + Hawk.put(HawkConfig.LIVE_API_URL, newApi); + inputApiLive.setText(newApi); + dismiss(); + } + } + }); findViewById(R.id.apiHistory).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - ArrayList history = Hawk.get(HawkConfig.API_HISTORY, new ArrayList()); - if (history.isEmpty()) + ArrayList history = Hawk.get(HawkConfig.LIVE_API_HISTORY, new ArrayList()); + if (history.isEmpty()){ + Toast.makeText(getContext(), "直播历史为空", Toast.LENGTH_SHORT).show(); return; - String current = Hawk.get(HawkConfig.API_URL, ""); + } + String current = Hawk.get(HawkConfig.LIVE_API_URL, ""); int idx = 0; if (history.contains(current)) idx = history.indexOf(current); ApiHistoryDialog dialog = new ApiHistoryDialog(getContext()); - dialog.setTip("历史配置列表"); + dialog.setTip("直播历史配置"); dialog.setAdapter(new ApiHistoryDialogAdapter.SelectDialogInterface() { @Override public void click(String value) { - inputApi.setText(value); - listener.onchange(value); + inputApiLive.setText(value); + Hawk.put(HawkConfig.LIVE_API_URL, value); dialog.dismiss(); } @Override public void del(String value, ArrayList data) { - Hawk.put(HawkConfig.API_HISTORY, data); + Hawk.put(HawkConfig.LIVE_API_HISTORY, data); } }, history, idx); dialog.show(); @@ -144,6 +172,11 @@ public class ApiDialog extends BaseDialog { if (history.size() > 30) history.remove(30); Hawk.put(HawkConfig.API_HISTORY, history); + + if(!newApi.equals(Hawk.get(HawkConfig.API_URL, newApi))){ + inputApiLive.setText(newApi); + Hawk.put(HawkConfig.LIVE_API_URL, newApi); + } listener.onchange(newApi); dismiss(); } diff --git a/app/src/main/java/com/github/tvbox/osc/ui/dialog/GridFilterDialog.java b/app/src/main/java/com/github/tvbox/osc/ui/dialog/GridFilterDialog.java index bedc8107..762ea526 100644 --- a/app/src/main/java/com/github/tvbox/osc/ui/dialog/GridFilterDialog.java +++ b/app/src/main/java/com/github/tvbox/osc/ui/dialog/GridFilterDialog.java @@ -24,7 +24,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; public class GridFilterDialog extends BaseDialog { - private LinearLayout filterRoot; + public LinearLayout filterRoot; public GridFilterDialog(@NonNull @NotNull Context context) { super(context); diff --git a/app/src/main/java/com/github/tvbox/osc/ui/fragment/GridFragment.java b/app/src/main/java/com/github/tvbox/osc/ui/fragment/GridFragment.java index edc88d08..01f19c40 100644 --- a/app/src/main/java/com/github/tvbox/osc/ui/fragment/GridFragment.java +++ b/app/src/main/java/com/github/tvbox/osc/ui/fragment/GridFragment.java @@ -1,11 +1,15 @@ package com.github.tvbox.osc.ui.fragment; +import android.content.Context; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; import android.view.animation.BounceInterpolator; +import androidx.core.content.ContextCompat; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; import com.chad.library.adapter.base.BaseQuickAdapter; import com.github.tvbox.osc.R; @@ -20,17 +24,23 @@ import com.github.tvbox.osc.ui.activity.DetailActivity; import com.github.tvbox.osc.ui.activity.FastSearchActivity; import com.github.tvbox.osc.ui.activity.SearchActivity; import com.github.tvbox.osc.ui.adapter.GridAdapter; +import com.github.tvbox.osc.ui.adapter.GridFilterKVAdapter; import com.github.tvbox.osc.ui.dialog.GridFilterDialog; import com.github.tvbox.osc.ui.tv.widget.LoadMoreView; import com.github.tvbox.osc.util.FastClickCheckUtil; import com.github.tvbox.osc.util.HawkConfig; +import com.github.tvbox.osc.util.LOG; import com.github.tvbox.osc.viewmodel.SourceViewModel; import com.orhanobut.hawk.Hawk; import com.owen.tvrecyclerview.widget.TvRecyclerView; import com.owen.tvrecyclerview.widget.V7GridLayoutManager; import com.owen.tvrecyclerview.widget.V7LinearLayoutManager; + +import java.util.ArrayList; import java.util.Stack; import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; import android.widget.Toast; import org.greenrobot.eventbus.EventBus; @@ -249,7 +259,6 @@ public class GridFragment extends BaseLazyFragment { sourceViewModel.listResult.observe(this, new Observer() { @Override public void onChanged(AbsXml absXml) { -// if(mGridView != null) mGridView.requestFocus(); if (absXml != null && absXml.movie != null && absXml.movie.videoList != null && absXml.movie.videoList.size() > 0) { if (page == 1) { showSuccess(); @@ -259,23 +268,15 @@ public class GridFragment extends BaseLazyFragment { gridAdapter.addData(absXml.movie.videoList); } page++; - maxPage = absXml.movie.pagecount; - - if (maxPage>0 && page > maxPage) { - gridAdapter.loadMoreEnd(); - gridAdapter.setEnableLoadMore(false); - if(page>2)Toast.makeText(getContext(), "最后一页啦", Toast.LENGTH_SHORT).show(); - } else { - gridAdapter.loadMoreComplete(); - gridAdapter.setEnableLoadMore(true); - } + gridAdapter.loadMoreComplete(); + gridAdapter.setEnableLoadMore(true); } else { - if(page == 1){ + if (page == 1) { showEmpty(); - }else{ - Toast.makeText(getContext(), "最后一页啦", Toast.LENGTH_SHORT).show(); - gridAdapter.loadMoreEnd(); + } else if(page > 2){// 只有一页数据时不提示 + Toast.makeText(getContext(), "没有更多了", Toast.LENGTH_SHORT).show(); } + gridAdapter.loadMoreEnd(); gridAdapter.setEnableLoadMore(false); } } @@ -313,17 +314,73 @@ public class GridFragment extends BaseLazyFragment { public void showFilter() { if (!sortData.filters.isEmpty() && gridFilterDialog == null) { gridFilterDialog = new GridFilterDialog(mContext); - gridFilterDialog.setData(sortData); - gridFilterDialog.setOnDismiss(new GridFilterDialog.Callback() { +// gridFilterDialog.setData(sortData); +// gridFilterDialog.setOnDismiss(new GridFilterDialog.Callback() { +// @Override +// public void change() { +// page = 1; +// initData(); +// } +// }); + setFilterDialogData(); + } + if (gridFilterDialog != null) + gridFilterDialog.show(); + } + + public void setFilterDialogData() { + Context context = getContext(); + LayoutInflater inflater = LayoutInflater.from(context); + assert context != null; + final int defaultColor = ContextCompat.getColor(context, R.color.color_FFFFFF); + final int selectedColor = ContextCompat.getColor(context, R.color.color_02F8E1); + // 遍历过滤条件数据 + for (MovieSort.SortFilter filter : sortData.filters) { + View line = inflater.inflate(R.layout.item_grid_filter, gridFilterDialog.filterRoot, false); + TextView filterNameTv = line.findViewById(R.id.filterName); + filterNameTv.setText(filter.name); + TvRecyclerView gridView = line.findViewById(R.id.mFilterKv); + gridView.setHasFixedSize(true); + gridView.setLayoutManager(new V7LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)); + GridFilterKVAdapter adapter = new GridFilterKVAdapter(); + gridView.setAdapter(adapter); + final String key = filter.key; + final ArrayList values = new ArrayList<>(filter.values.keySet()); + final ArrayList keys = new ArrayList<>(filter.values.values()); + adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { + // 用于记录上一次选中的 view + View previousSelectedView = null; @Override - public void change() { - page = 1; - initData(); + public void onItemClick(BaseQuickAdapter adapter, View view, int position) { + String currentSelection = sortData.filterSelect.get(key); + String newSelection = keys.get(position); + if (currentSelection == null || !currentSelection.equals(newSelection)) { + // 更新选中状态 + sortData.filterSelect.put(key, newSelection); + updateViewStyle(view, selectedColor, true); + if (previousSelectedView != null) { + updateViewStyle(previousSelectedView, defaultColor, false); + } + previousSelectedView = view; + } else { + // 取消选中 + sortData.filterSelect.remove(key); + if (previousSelectedView != null) { + updateViewStyle(previousSelectedView, defaultColor, false); + } + previousSelectedView = null; + } + forceRefresh(); + } + private void updateViewStyle(View view, int color, boolean isBold) { + TextView valueTv = view.findViewById(R.id.filterValue); + valueTv.getPaint().setFakeBoldText(isBold); + valueTv.setTextColor(color); } }); + adapter.setNewData(values); + gridFilterDialog.filterRoot.addView(line); } - if (gridFilterDialog != null) - gridFilterDialog.show(); } public void forceRefresh() { diff --git a/app/src/main/java/com/github/tvbox/osc/util/FileUtils.java b/app/src/main/java/com/github/tvbox/osc/util/FileUtils.java index d23baa42..5c08dfe3 100644 --- a/app/src/main/java/com/github/tvbox/osc/util/FileUtils.java +++ b/app/src/main/java/com/github/tvbox/osc/util/FileUtils.java @@ -100,24 +100,6 @@ public class FileUtils { return jsonString; } - public static String getAssetFile(String assetName) throws IOException { - InputStream is = App.getInstance().getAssets().open(assetName); - byte[] data = new byte[is.available()]; - is.read(data); - return new String(data, "UTF-8"); - } - - public static boolean isAssetFile(String name, String path) { - try { - for(String one : App.getInstance().getAssets().list(path)) { - if (one.equals(name)) return true; - } - } catch (Exception e) { - e.printStackTrace(); - } - return false; - } - public static String getRootPath() { return Environment.getExternalStorageDirectory().getAbsolutePath(); } @@ -220,14 +202,6 @@ public class FileUtils { return fileName; } - public static String getFileExt(String fileName){ - if(TextUtils.isEmpty(fileName)) return ""; - int p = fileName.lastIndexOf('.'); - if(p != -1) { - return fileName.substring(p).toLowerCase(); - } - return ""; - } public static boolean hasExtension(String path) { int lastDotIndex = path.lastIndexOf("."); @@ -235,4 +209,20 @@ public class FileUtils { // 如果路径中有点号,并且点号在最后一个斜杠之后,认为有后缀 return lastDotIndex > lastSlashIndex && lastDotIndex < path.length() - 1; } + + public static void saveCache(File cache,String json){ + try { + File cacheDir = cache.getParentFile(); + if (!cacheDir.exists()) + cacheDir.mkdirs(); + if (cache.exists()) + cache.delete(); + FileOutputStream fos = new FileOutputStream(cache); + fos.write(json.getBytes("UTF-8")); + fos.flush(); + fos.close(); + } catch (Throwable th) { + th.printStackTrace(); + } + } } diff --git a/app/src/main/java/com/github/tvbox/osc/util/HawkConfig.java b/app/src/main/java/com/github/tvbox/osc/util/HawkConfig.java index b1e24e2f..e0e1602a 100644 --- a/app/src/main/java/com/github/tvbox/osc/util/HawkConfig.java +++ b/app/src/main/java/com/github/tvbox/osc/util/HawkConfig.java @@ -10,6 +10,7 @@ public class HawkConfig { public static final String EPG_URL = "epg_url"; public static final String SHOW_PREVIEW = "show_preview"; public static final String API_HISTORY = "api_history"; + public static final String LIVE_API_HISTORY = "live_api_history"; public static final String EPG_HISTORY = "epg_history"; public static final String HOME_API = "home_api"; public static final String DEFAULT_PARSE = "parse_default"; diff --git a/app/src/main/java/com/github/tvbox/osc/util/VideoParseRuler.java b/app/src/main/java/com/github/tvbox/osc/util/VideoParseRuler.java index d50757fd..1d8da538 100644 --- a/app/src/main/java/com/github/tvbox/osc/util/VideoParseRuler.java +++ b/app/src/main/java/com/github/tvbox/osc/util/VideoParseRuler.java @@ -14,6 +14,7 @@ public class VideoParseRuler { public static void clearRule() { HOSTS_RULE.clear(); HOSTS_FILTER.clear(); + HOSTS_REGEX.clear(); } public static void addHostRule(String host, ArrayList rule) { diff --git a/app/src/main/res/layout/dialog_api.xml b/app/src/main/res/layout/dialog_api.xml index c12591da..b554f1c6 100644 --- a/app/src/main/res/layout/dialog_api.xml +++ b/app/src/main/res/layout/dialog_api.xml @@ -1,18 +1,18 @@ + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + - - + android:layout_width="@dimen/vs_520" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/vs_20" + android:orientation="vertical" + android:paddingTop="@dimen/vs_20" + android:paddingBottom="@dimen/vs_20" + android:layout_marginLeft="@dimen/vs_20"> - - + + android:layout_marginBottom="@dimen/vs_10" + android:orientation="horizontal"> + android:textSize="@dimen/ts_22" + android:layout_marginRight="@dimen/vs_5" /> + android:id="@+id/storagePermission" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginStart="@dimen/vs_5" + android:background="@drawable/button_dialog_main" + android:focusable="true" + android:gravity="center" + android:padding="@dimen/vs_10" + android:text="存储权限" + android:textColor="@color/color_FFFFFF" + android:textSize="@dimen/ts_22" + android:layout_marginLeft="@dimen/vs_5" /> - - + + android:orientation="horizontal" + android:layout_marginBottom="@dimen/vs_10"> + + + android:textSize="@dimen/ts_22" + android:layout_marginLeft="@dimen/vs_5" /> + + + + + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 420472fc..28417794 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -81,6 +81,7 @@ 410mm 470mm 480mm + 540mm 600mm 610mm 640mm diff --git a/gradle.properties b/gradle.properties index 2b801b14..8e1f9a6b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,4 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true IsDebug=true -org.gradle.jvmargs=-Xmx2048m --add-opens java.base/java.io=ALL-UNNAMED +#org.gradle.jvmargs=-Xmx2048m --add-opens java.base/java.io=ALL-UNNAMED