feat: 升级到v3.1.0
- 实现定时按钮倒计时显示功能 - 适配pixel主题化图标展示 - 优化TimerDialog按钮宽度设计
@@ -0,0 +1,270 @@
|
||||
package com.fongmi.android.tv.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 广告拦截器 - 内置常用广告域名库
|
||||
*/
|
||||
public class AdBlocker {
|
||||
|
||||
/**
|
||||
* 赌博类广告域名(澳门新葡京等)
|
||||
*/
|
||||
private static final List<String> GAMBLING_ADS = Arrays.asList(
|
||||
// 澳门博彩广告
|
||||
".*\\..*葡京.*",
|
||||
".*\\..*皇冠.*",
|
||||
".*\\..*金沙.*",
|
||||
".*\\..*威尼斯人.*",
|
||||
".*\\..*永利.*",
|
||||
".*aomen.*",
|
||||
".*macau.*casino.*",
|
||||
".*xpj.*\\..*",
|
||||
".*xinpujing.*",
|
||||
".*amdc.*\\.com",
|
||||
".*\\.amdc\\.alipay\\.com",
|
||||
|
||||
// 常见博彩推广域名
|
||||
".*\\.bz.*bet.*",
|
||||
".*\\.casino.*",
|
||||
".*\\.poker.*",
|
||||
".*\\.betting.*",
|
||||
".*\\.gamble.*",
|
||||
".*wnsr.*\\..*",
|
||||
".*js[0-9]+\\..*",
|
||||
".*vn[0-9]+\\..*",
|
||||
".*ag[0-9]+\\..*",
|
||||
|
||||
// 具体的博彩广告域名
|
||||
"wan.51img1.com",
|
||||
"iqiyi.hbuioo.com",
|
||||
"vip.ffzyad.com",
|
||||
"https.wshdsm.com",
|
||||
"v.%E7%88%B1%E4%B8%8A%E5%A5%B9%E5%BD%B1%E9%99%A2.com"
|
||||
);
|
||||
|
||||
/**
|
||||
* 通用广告联盟域名
|
||||
*/
|
||||
private static final List<String> GENERAL_ADS = Arrays.asList(
|
||||
// Google广告
|
||||
"googleads.g.doubleclick.net",
|
||||
"adservice.google.com",
|
||||
"pagead2.googlesyndication.com",
|
||||
"www.googletagmanager.com",
|
||||
"static.doubleclick.net",
|
||||
".*\\.doubleclick\\.net",
|
||||
".*\\.googlesyndication\\.com",
|
||||
|
||||
// 百度广告
|
||||
"cpro.baidu.com",
|
||||
"pos.baidu.com",
|
||||
"cbjs.baidu.com",
|
||||
"hm.baidu.com",
|
||||
".*\\.union\\.baidu\\.com",
|
||||
|
||||
// 淘宝/阿里广告
|
||||
"mclick.simba.taobao.com",
|
||||
"simba.m.taobao.com",
|
||||
".*\\.tanx\\.com",
|
||||
".*\\.mmstat\\.com",
|
||||
".*\\.atm\\.youku\\.com",
|
||||
|
||||
// 腾讯广告
|
||||
"mi.gdt.qq.com",
|
||||
"adsmind.gdtimg.com",
|
||||
".*\\.l\\.qq\\.com",
|
||||
"pgdt.gtimg.cn",
|
||||
|
||||
// 其他主流广告联盟
|
||||
"union.meituan.com",
|
||||
"analytics.163.com",
|
||||
"g.163.com",
|
||||
"analytics.126.net",
|
||||
".*\\.irs01\\.com",
|
||||
".*\\.irs01\\.net"
|
||||
);
|
||||
|
||||
/**
|
||||
* 视频平台广告域名
|
||||
*/
|
||||
private static final List<String> VIDEO_ADS = Arrays.asList(
|
||||
// 优酷广告
|
||||
"atm.youku.com",
|
||||
"stat.youku.com",
|
||||
"ad.api.3g.youku.com",
|
||||
"pl.youku.com",
|
||||
"lstat.youku.com",
|
||||
".*\\.atm\\.youku\\.com",
|
||||
|
||||
// 爱奇艺广告
|
||||
"cupid.iqiyi.com",
|
||||
"data.video.iqiyi.com",
|
||||
"msg.71.am",
|
||||
".*\\.cupid\\.iqiyi\\.com",
|
||||
".*\\.data\\.video\\.iqiyi\\.com",
|
||||
|
||||
// 腾讯视频广告
|
||||
"btrace.video.qq.com",
|
||||
"mtrace.video.qq.com",
|
||||
"vv.video.qq.com",
|
||||
"ad.video.qq.com",
|
||||
|
||||
// 芒果TV广告
|
||||
"da.mgtv.com",
|
||||
"ad.hunantv.com",
|
||||
"v2.hunantv.com",
|
||||
|
||||
// 其他视频平台
|
||||
"ark.letv.com",
|
||||
"stat.letv.com",
|
||||
".*\\.beacon\\.qq\\.com"
|
||||
);
|
||||
|
||||
/**
|
||||
* 弹窗广告域名
|
||||
*/
|
||||
private static final List<String> POPUP_ADS = Arrays.asList(
|
||||
// 常见弹窗广告
|
||||
"mimg.0c1q0l.cn",
|
||||
"www.92424.cn",
|
||||
"k.jinxiuzhilv.com",
|
||||
"cdn.bootcss.com",
|
||||
"ppl.xunzhuo.com",
|
||||
"xc.hubeijieshikj.cn",
|
||||
"ssl.kdd.cc",
|
||||
"push.zhanzhang.baidu.com",
|
||||
"cpc.cmbchina.com",
|
||||
"adshow.58.com",
|
||||
|
||||
// 移动端弹窗
|
||||
"afp.csbew.com",
|
||||
"aoodoo.feng.com",
|
||||
"*.popin.cc",
|
||||
"*.supersonicads.com"
|
||||
);
|
||||
|
||||
/**
|
||||
* 恶意网站和钓鱼网站
|
||||
*/
|
||||
private static final List<String> MALICIOUS_ADS = Arrays.asList(
|
||||
".*\\.17un\\.com",
|
||||
".*\\.baidustatic\\.com",
|
||||
".*\\.cnzz\\.com",
|
||||
".*\\.duomeng\\.cn",
|
||||
".*\\.shuzilm\\.cn",
|
||||
".*\\.haoyuemh\\.com",
|
||||
".*\\.571xz\\.com",
|
||||
".*\\.madthumbs\\.com"
|
||||
);
|
||||
|
||||
/**
|
||||
* 跟踪统计域名
|
||||
*/
|
||||
private static final List<String> TRACKING_ADS = Arrays.asList(
|
||||
// 统计跟踪
|
||||
"hm.baidu.com",
|
||||
"tongji.baidu.com",
|
||||
"s95.cnzz.com",
|
||||
"cnzz.com",
|
||||
".*\\.umeng\\.com",
|
||||
".*\\.umtrack\\.com",
|
||||
|
||||
// Google Analytics
|
||||
"www.google-analytics.com",
|
||||
"ssl.google-analytics.com",
|
||||
".*\\.googletagmanager\\.com"
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取所有内置广告域名
|
||||
* @return 完整的广告域名列表
|
||||
*/
|
||||
public static List<String> getAllAdHosts() {
|
||||
return Arrays.asList(
|
||||
// 合并所有列表
|
||||
String.join(",", GAMBLING_ADS),
|
||||
String.join(",", GENERAL_ADS),
|
||||
String.join(",", VIDEO_ADS),
|
||||
String.join(",", POPUP_ADS),
|
||||
String.join(",", MALICIOUS_ADS),
|
||||
String.join(",", TRACKING_ADS)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取赌博类广告域名(澳门新葡京等)
|
||||
*/
|
||||
public static List<String> getGamblingAdHosts() {
|
||||
return GAMBLING_ADS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通用广告联盟域名
|
||||
*/
|
||||
public static List<String> getGeneralAdHosts() {
|
||||
return GENERAL_ADS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频平台广告域名
|
||||
*/
|
||||
public static List<String> getVideoAdHosts() {
|
||||
return VIDEO_ADS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取弹窗广告域名
|
||||
*/
|
||||
public static List<String> getPopupAdHosts() {
|
||||
return POPUP_ADS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取恶意网站域名
|
||||
*/
|
||||
public static List<String> getMaliciousAdHosts() {
|
||||
return MALICIOUS_ADS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取跟踪统计域名
|
||||
*/
|
||||
public static List<String> getTrackingAdHosts() {
|
||||
return TRACKING_ADS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否应该拦截该域名
|
||||
* @param host 要检查的域名
|
||||
* @return true=应该拦截, false=不拦截
|
||||
*/
|
||||
public static boolean shouldBlock(String host) {
|
||||
if (host == null || host.isEmpty()) return false;
|
||||
|
||||
// 检查所有分类
|
||||
return checkInList(host, GAMBLING_ADS) ||
|
||||
checkInList(host, GENERAL_ADS) ||
|
||||
checkInList(host, VIDEO_ADS) ||
|
||||
checkInList(host, POPUP_ADS) ||
|
||||
checkInList(host, MALICIOUS_ADS) ||
|
||||
checkInList(host, TRACKING_ADS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查域名是否在列表中(支持正则)
|
||||
*/
|
||||
private static boolean checkInList(String host, List<String> list) {
|
||||
for (String pattern : list) {
|
||||
if (host.matches(pattern.replace("*", ".*"))) {
|
||||
return true;
|
||||
}
|
||||
if (host.contains(pattern.replace(".*", ""))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,32 +78,41 @@ public class VodConfig {
|
||||
}
|
||||
|
||||
public static void load(Config config, Callback callback) {
|
||||
android.util.Log.d("VodConfig", "load called with config: " + (config != null ? config.toString() : "null"));
|
||||
|
||||
// 参数检查
|
||||
if (config == null || callback == null) {
|
||||
android.util.Log.e("VodConfig", "Invalid parameters: config=" + config + ", callback=" + callback);
|
||||
if (callback != null) {
|
||||
App.post(() -> callback.error("配置参数无效"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
android.util.Log.d("VodConfig", "Parameters valid, proceeding with load");
|
||||
|
||||
// 添加加载状态检查,防止并发加载
|
||||
VodConfig instance = get();
|
||||
synchronized (instance) {
|
||||
if (instance.isLoading) {
|
||||
android.util.Log.d("VodConfig", "Already loading, cancelling previous load");
|
||||
// 如果正在加载,取消之前的加载
|
||||
try {
|
||||
OkHttp.cancel("vod");
|
||||
} catch (Exception e) {
|
||||
android.util.Log.e("VodConfig", "Error cancelling previous load", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
instance.isLoading = true;
|
||||
}
|
||||
|
||||
android.util.Log.d("VodConfig", "Calling instance.clear().config(config).load(callback)");
|
||||
try {
|
||||
instance.clear().config(config).load(callback);
|
||||
} catch (Exception e) {
|
||||
instance.isLoading = false;
|
||||
android.util.Log.e("VodConfig", "Exception during load", e);
|
||||
e.printStackTrace();
|
||||
App.post(() -> callback.error("配置加载失败: " + e.getMessage()));
|
||||
}
|
||||
@@ -224,14 +233,26 @@ public class VodConfig {
|
||||
return;
|
||||
}
|
||||
String spider = Json.safeString(object, "spider");
|
||||
BaseLoader.get().parseJar(spider, true);
|
||||
try {
|
||||
BaseLoader.get().parseJar(spider, true);
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.e("VodConfig", "Failed to parse spider jar: " + spider, e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (JsonElement element : Json.safeListElement(object, "sites")) {
|
||||
Site site = Site.objectFrom(element);
|
||||
if (sites.contains(site)) continue;
|
||||
site.setApi(UrlUtil.convert(site.getApi()));
|
||||
site.setExt(UrlUtil.convert(site.getExt()));
|
||||
site.setJar(parseJar(site, spider));
|
||||
sites.add(site.trans().sync());
|
||||
try {
|
||||
Site site = Site.objectFrom(element);
|
||||
if (sites.contains(site)) continue;
|
||||
site.setApi(UrlUtil.convert(site.getApi()));
|
||||
site.setExt(UrlUtil.convert(site.getExt()));
|
||||
site.setJar(parseJar(site, spider));
|
||||
sites.add(site.trans().sync());
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.e("VodConfig", "Failed to add site: " + element, e);
|
||||
e.printStackTrace();
|
||||
// 继续处理下一个站点
|
||||
}
|
||||
}
|
||||
for (Site site : sites) {
|
||||
if (site.getKey().equals(config.getHome())) {
|
||||
|
||||
@@ -46,10 +46,15 @@ public class JarLoader {
|
||||
}
|
||||
|
||||
private void load(String key, File file) {
|
||||
if (!file.setReadOnly()) return;
|
||||
loaders.put(key, dex(file));
|
||||
invokeInit(key);
|
||||
putProxy(key);
|
||||
try {
|
||||
if (!file.setReadOnly()) return;
|
||||
loaders.put(key, dex(file));
|
||||
invokeInit(key);
|
||||
putProxy(key);
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.e("JarLoader", "Failed to load jar for key: " + key, e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private DexClassLoader dex(File file) {
|
||||
@@ -85,19 +90,24 @@ public class JarLoader {
|
||||
}
|
||||
|
||||
public synchronized void parseJar(String key, String jar) {
|
||||
if (loaders.containsKey(key)) return;
|
||||
String[] texts = jar.split(";md5;");
|
||||
String md5 = texts.length > 1 ? texts[1].trim() : "";
|
||||
if (md5.startsWith("http")) md5 = OkHttp.string(md5).trim();
|
||||
jar = texts[0];
|
||||
if (!md5.isEmpty() && Util.equals(jar, md5)) {
|
||||
load(key, Path.jar(jar));
|
||||
} else if (jar.startsWith("http")) {
|
||||
load(key, download(jar));
|
||||
} else if (jar.startsWith("file")) {
|
||||
load(key, Path.local(jar));
|
||||
} else if (jar.startsWith("assets")) {
|
||||
parseJar(key, UrlUtil.convert(jar));
|
||||
try {
|
||||
if (loaders.containsKey(key)) return;
|
||||
String[] texts = jar.split(";md5;");
|
||||
String md5 = texts.length > 1 ? texts[1].trim() : "";
|
||||
if (md5.startsWith("http")) md5 = OkHttp.string(md5).trim();
|
||||
jar = texts[0];
|
||||
if (!md5.isEmpty() && Util.equals(jar, md5)) {
|
||||
load(key, Path.jar(jar));
|
||||
} else if (jar.startsWith("http")) {
|
||||
load(key, download(jar));
|
||||
} else if (jar.startsWith("file")) {
|
||||
load(key, Path.local(jar));
|
||||
} else if (jar.startsWith("assets")) {
|
||||
parseJar(key, UrlUtil.convert(jar));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
android.util.Log.e("JarLoader", "Failed to parse jar for key: " + key + ", jar: " + jar, e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import androidx.annotation.NonNull;
|
||||
import com.fongmi.android.tv.App;
|
||||
import com.fongmi.android.tv.Constant;
|
||||
import com.fongmi.android.tv.Setting;
|
||||
import com.fongmi.android.tv.api.AdBlocker;
|
||||
import com.fongmi.android.tv.api.config.LiveConfig;
|
||||
import com.fongmi.android.tv.api.config.VodConfig;
|
||||
import com.fongmi.android.tv.impl.ParseCallback;
|
||||
@@ -177,8 +178,13 @@ public class CustomWebView extends WebView implements DialogInterface.OnDismissL
|
||||
}
|
||||
|
||||
private boolean isAd(String host) {
|
||||
// 1. 首先检查内置广告域名库(包含常见的澳门新葡京等赌博广告)
|
||||
if (AdBlocker.shouldBlock(host)) return true;
|
||||
|
||||
// 2. 然后检查用户自定义的广告域名
|
||||
for (String ad : VodConfig.get().getAds()) if (Util.containOrMatch(host, ad)) return true;
|
||||
for (String ad : LiveConfig.get().getAds()) if (Util.containOrMatch(host, ad)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
android:topLeftRadius="8dp"
|
||||
android:topRightRadius="8dp" />
|
||||
|
||||
<solid android:color="#B32196F3" />
|
||||
<solid android:color="#B3000000" />
|
||||
|
||||
<padding
|
||||
android:bottom="4dp"
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/white" />
|
||||
<!-- 使用纯色背景,自动适配深浅色模式 -->
|
||||
<background android:drawable="@color/launcher_background" />
|
||||
<!-- 前景图标:使用 inset 缩小显示(因为图标铺满了画布)-->
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@mipmap/ic_launcher_foreground"
|
||||
android:inset="20%" />
|
||||
</foreground>
|
||||
<!-- 主题图标:也需要使用 inset 保持大小一致 -->
|
||||
<monochrome>
|
||||
<inset
|
||||
android:drawable="@mipmap/ic_launcher_foreground"
|
||||
android:inset="20%" />
|
||||
</monochrome>
|
||||
</adaptive-icon>
|
||||
@@ -1,9 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/white" />
|
||||
<!-- 使用纯色背景,自动适配深浅色模式 -->
|
||||
<background android:drawable="@color/launcher_background" />
|
||||
<!-- 前景图标:使用 inset 缩小显示(因为图标铺满了画布)-->
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@mipmap/ic_launcher_foreground"
|
||||
android:inset="20%" />
|
||||
</foreground>
|
||||
</adaptive-icon>
|
||||
<!-- 主题图标:也需要使用 inset 保持大小一致 -->
|
||||
<monochrome>
|
||||
<inset
|
||||
android:drawable="@mipmap/ic_launcher_foreground"
|
||||
android:inset="20%" />
|
||||
</monochrome>
|
||||
</adaptive-icon>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 998 B |
|
Before Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 998 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 830 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 830 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 830 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 830 B |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,7 @@
|
||||
<resources>
|
||||
|
||||
<!-- Launcher icon background (Dark mode) -->
|
||||
<color name="launcher_background">#222222</color>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
<string name="setting_app">应用设置</string>
|
||||
<string name="setting_network">网络设置</string>
|
||||
<string name="setting_data">数据管理</string>
|
||||
<string name="app_version">v3.0.3</string>
|
||||
<string name="app_version">v3.0.9</string>
|
||||
<string name="about_github">在GitHub上查看</string>
|
||||
|
||||
<!-- Backup & Restore -->
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<string name="setting_app">應用設置</string>
|
||||
<string name="setting_network">網絡設置</string>
|
||||
<string name="setting_data">數據管理</string>
|
||||
<string name="app_version">v3.0.3</string>
|
||||
<string name="app_version">v3.0.9</string>
|
||||
<string name="about_github">在GitHub上查看</string>
|
||||
|
||||
<!-- Backup & Restore -->
|
||||
|
||||
@@ -27,5 +27,8 @@
|
||||
<color name="white_90">#E6FFFFFF</color>
|
||||
<color name="text_toast">#FFEB3B</color>
|
||||
<color name="yellow_500">#FFEB3B</color>
|
||||
|
||||
<!-- Launcher icon background (Light mode) -->
|
||||
<color name="launcher_background">#FFFFFF</color>
|
||||
|
||||
</resources>
|
||||
@@ -92,7 +92,7 @@
|
||||
<string name="setting_choose">Choose</string>
|
||||
<string name="setting_off">Off</string>
|
||||
<string name="setting_on">On</string>
|
||||
<string name="app_version">v3.0.6</string>
|
||||
<string name="app_version">v3.0.9</string>
|
||||
<string name="about_github">View on GitHub</string>
|
||||
|
||||
<!-- Backup & Restore -->
|
||||
@@ -152,7 +152,7 @@
|
||||
<string name="error_device_limit">Device authorization limit reached</string>
|
||||
<string name="error_live_empty">This subscription has no live content</string>
|
||||
<string name="error_no_live">Current source has no live content</string>
|
||||
<string name="error_empty">这里撒子内容都没得~</string>
|
||||
<string name="error_empty">空谷待音~</string>
|
||||
<string name="error_keep_empty">老表~没得收藏哈</string>
|
||||
<string name="error_search_empty">搜索无结果,换个关键词试试</string>
|
||||
<string name="error_detail">No play data</string>
|
||||
@@ -238,8 +238,8 @@
|
||||
<string name="target_size">Target size</string>
|
||||
<string name="scan_result">Scan result</string>
|
||||
|
||||
<string name="source_hint">No video sources added yet\nClick the button below to add</string>
|
||||
<string name="add_source">Add Source</string>
|
||||
<string name="source_hint">空谷无音,待君添源</string>
|
||||
<string name="add_source">添加源</string>
|
||||
|
||||
<!-- 隐私协议相关 -->
|
||||
<string name="privacy_agreement_title">XMBOX软件许可协议</string>
|
||||
|
||||