diff --git a/RELEASE_NOTES_v3.0.7.md b/RELEASE_NOTES_v3.0.7.md new file mode 100644 index 00000000..d0bcef2a --- /dev/null +++ b/RELEASE_NOTES_v3.0.7.md @@ -0,0 +1,131 @@ +# XMBOX v3.0.7 Release Notes + +## 🎯 重要更新 + +XMBOX v3.0.7 是一个重要的稳定性和用户体验升级版本,包含关键崩溃修复和全面的UI优化。 + +## 🐛 核心修复 + +### 关键崩溃修复 +- **修复 VodConfig 空指针崩溃** - 解决应用销毁时的 NullPointerException +- **修复 LiveConfig 初始化问题** - 增强单例模式的安全性 +- **优化生命周期管理** - 改进 Activity 销毁时的资源清理 + +### 稳定性提升 +- 添加构造函数初始化,防止 clear() 方法空指针异常 +- 增强错误处理机制和异常捕获 +- 全面的空值安全检查 + +## 🎨 UI/UX 全面升级 + +### 新增隐私协议页面 +- 符合应用商店规范的隐私政策界面 +- 完善的任务栈管理,防止用户回退到协议页面 +- 优雅的应用退出机制 + +### 界面优化 +- **修复按钮文字显示** - 解决长文本显示不完整问题 + - 调整按钮文字大小:16sp → 13sp + - 增加按钮高度:48dp → 56dp + - 支持多行文本显示和居中对齐 + +### 空状态优化 +- **恢复完整 Lottie 动画** - 54KB 高质量动画效果 +- **位置调整** - 空状态动画向上移动 40dp,提升视觉平衡 +- **川渝方言文案** - 更新为 "这里撒子内容都没得~" +- 新增多个专用空状态布局:搜索、收藏、通用 + +## 📺 TV版本专项优化 + +### 选集按钮优化 +- **黄色高亮显示** - 选中状态文字改为黄色 (#FFEB3B) +- **专用颜色方案** - 新增 episode_text.xml 选择器 +- **精准影响范围** - 仅修改视频详情页,不干扰其他界面 + +### 视觉体验提升 +- 保持与手机版一致的核心功能 +- 针对电视遥控器操作优化 +- 更清晰的焦点状态指示 + +## ⚡ 技术改进 + +### 架构优化 +- 改进单例模式的实现 +- 增强并发安全性 +- 优化内存使用效率 + +### 代码质量 +- 添加完善的空值检查 +- 改进异常处理逻辑 +- 增强调试和错误报告机制 + +## 📱 平台支持 + +### Android 兼容性 +- **最低要求**: Android 5.0 (API 21) +- **推荐版本**: Android 7.0 及以上 +- **架构支持**: ARM64-V8A、ARM V7A + +### 设备支持 +- 手机、平板设备 +- Android TV、机顶盒 +- 各类智能电视设备 + +## 🔧 开发者信息 + +### 构建信息 +- **编译日期**: 2025-09-26 +- **Gradle 版本**: 8.13 +- **Target SDK**: 34 +- **签名**: 正式发布签名 + +### 文件大小 +- **手机版 ARM64**: 37MB +- **手机版 ARM32**: 32MB +- **TV版 ARM64**: 34MB +- **TV版 ARM32**: 30MB + +## 📋 安装说明 + +### 首次安装 +1. 下载对应架构的 APK 文件 +2. 允许安装未知来源应用 +3. 首次启动会显示隐私协议页面 +4. 同意协议后即可正常使用 + +### 从旧版本升级 +- 支持从 v3.0.0 及以上版本直接升级 +- 用户数据和设置将自动保留 +- 建议清除应用缓存以获得最佳体验 + +## ⚠️ 重要提醒 + +### 合规使用 +- 本软件仅为技术性多媒体播放器外壳 +- 不包含任何音视频内容 +- 用户需自行配置合法的内容源 +- 请遵守当地法律法规 + +### 隐私保护 +- 新增的隐私协议确保用户知情同意 +- 不收集用户个人信息 +- 本地数据处理,保护用户隐私 + +## 🙏 致谢 + +感谢所有用户的反馈和建议,特别是: +- 崩溃报告和调试信息的提供者 +- UI/UX 改进建议的贡献者 +- 测试不同设备兼容性的志愿者 + +## 📞 支持与反馈 + +- **GitHub Issues**: https://github.com/Tosencen/XMBOX/issues +- **功能请求**: https://github.com/Tosencen/XMBOX/discussions +- **Bug 报告**: 请提供详细的设备信息和复现步骤 + +--- + +**基于 FongMi/TV 项目开发 | GPL-3.0 开源协议** + +> 如果这个项目对你有帮助,请给我们一个 ⭐ Star! diff --git a/app/src/main/java/com/fongmi/android/tv/Setting.java b/app/src/main/java/com/fongmi/android/tv/Setting.java index 2a7991b2..dbb00b9c 100644 --- a/app/src/main/java/com/fongmi/android/tv/Setting.java +++ b/app/src/main/java/com/fongmi/android/tv/Setting.java @@ -202,7 +202,7 @@ public class Setting { } public static boolean getAutoUpdateCheck() { - return Prefers.getBoolean("auto_update_check", false); + return Prefers.getBoolean("auto_update_check", true); // 默认启用自动更新检查 } public static void putAutoUpdateCheck(boolean autoUpdateCheck) { diff --git a/app/src/mobile/java/com/fongmi/android/tv/Updater.java b/app/src/mobile/java/com/fongmi/android/tv/Updater.java index 1c93b2e4..c81cd207 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/Updater.java +++ b/app/src/mobile/java/com/fongmi/android/tv/Updater.java @@ -7,7 +7,6 @@ import android.view.View; import androidx.appcompat.app.AlertDialog; -import com.fongmi.android.tv.App; import com.fongmi.android.tv.databinding.DialogUpdateBinding; import com.fongmi.android.tv.utils.Download; import com.fongmi.android.tv.utils.FileUtil; @@ -15,11 +14,10 @@ import com.fongmi.android.tv.utils.Notify; import com.fongmi.android.tv.utils.ResUtil; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Github; +import com.github.catvod.utils.Logger; import com.github.catvod.utils.Path; import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.orhanobut.logger.Logger; -import org.json.JSONArray; import org.json.JSONObject; import java.io.File; @@ -28,26 +26,20 @@ import java.util.Locale; public class Updater implements Download.Callback { private DialogUpdateBinding binding; - private Download download; + private final Download download; private AlertDialog dialog; private boolean dev; - private String downloadUrl; - private boolean forceCheck; // 标记是否是用户主动检查更新 private File getFile() { return Path.cache("update.apk"); } - private String getApkName() { - return "mobile-" + BuildConfig.FLAVOR_abi + ".apk"; + private String getJson() { + return Github.getJson(dev, BuildConfig.FLAVOR_mode); } - private String getJson() { - String url = Github.getReleaseApi(); - boolean usingCnMirror = Github.useCnMirror(); - Logger.d("Using CN Mirror: " + usingCnMirror); - Logger.d("Update check URL: " + url); - return url; + private String getApk() { + return Github.getApk(dev, BuildConfig.FLAVOR_mode + "-" + BuildConfig.FLAVOR_abi); } public static Updater create() { @@ -55,14 +47,12 @@ public class Updater implements Download.Callback { } public Updater() { - this.download = Download.create("", getFile(), this); - this.forceCheck = false; // 默认不是用户主动检查更新 + this.download = Download.create(getApk(), getFile(), this); } public Updater force() { Notify.show(R.string.update_check); Setting.putUpdate(true); - this.forceCheck = true; // 标记为用户主动检查更新 return this; } @@ -82,112 +72,46 @@ public class Updater implements Download.Callback { } public void start(Activity activity) { + // 检查是否启用启动时自动检查更新 + if (!Setting.getAutoUpdateCheck()) { + Logger.d("Auto update check is disabled"); + return; + } App.execute(() -> doInBackground(activity)); } - private boolean need(String tagName) { - Logger.d("Current version: " + BuildConfig.VERSION_NAME); - Logger.d("Latest version: " + tagName); - if (tagName.startsWith("v")) tagName = tagName.substring(1); - - // 版本比较逻辑 - try { - String[] currentParts = BuildConfig.VERSION_NAME.split("\\."); - String[] remoteParts = tagName.split("\\."); - - // 比较主版本号 - for (int i = 0; i < Math.min(currentParts.length, remoteParts.length); i++) { - int current = Integer.parseInt(currentParts[i]); - int remote = Integer.parseInt(remoteParts[i]); - - if (remote > current) { - return Setting.getUpdate(); // 远程版本高于当前版本,需要更新 - } else if (remote < current) { - return false; // 远程版本低于当前版本,不需要更新 - } - // 如果相等,继续比较下一级版本号 - } - - // 如果前面的版本号都相等,但远程版本有更多的版本号,视为更新 - if (remoteParts.length > currentParts.length) { - return Setting.getUpdate(); - } - - return false; // 版本相同或远程版本较低,不需要更新 - } catch (NumberFormatException e) { - // 如果版本号解析失败,退回到简单字符串比较 - Logger.e("Version parsing failed", e); - return Setting.getUpdate() && !tagName.equals(BuildConfig.VERSION_NAME); - } + private boolean need(int code, String name) { + return Setting.getUpdate() && (dev ? !name.equals(BuildConfig.VERSION_NAME) && code >= BuildConfig.VERSION_CODE : code > BuildConfig.VERSION_CODE); } private void doInBackground(Activity activity) { try { - String jsonUrl = getJson(); - Logger.d("Fetching update info from: " + jsonUrl); - String response = OkHttp.string(jsonUrl); - Logger.d("Update check response: " + response); + String response = OkHttp.string(getJson()); - // 检查响应是否包含错误信息,只有在用户主动检查更新时才显示错误提示 + // 检查响应是否包含错误信息 if (response.contains("rate limit exceeded")) { - showErrorIfForceCheck("检查更新失败:API请求过于频繁,请稍后重试"); + App.post(() -> Notify.show("检查更新失败:API请求过于频繁,请稍后重试")); return; } if (response.contains("Not Found Project") || response.contains("Not Found")) { - showErrorIfForceCheck("检查更新失败:更新服务暂时不可用"); + App.post(() -> Notify.show("检查更新失败:更新服务暂时不可用")); return; } - JSONObject release = new JSONObject(response); - String tagName = release.getString("tag_name"); - String body = release.getString("body"); - JSONArray assets = release.getJSONArray("assets"); - - // Find the correct APK asset - String apkName = getApkName(); - for (int i = 0; i < assets.length(); i++) { - JSONObject asset = assets.getJSONObject(i); - if (asset.getString("name").equals(apkName)) { - downloadUrl = asset.getString("browser_download_url"); - break; - } - } - - if (downloadUrl != null && need(tagName)) { - download = Download.create(downloadUrl, getFile(), this); - App.post(() -> show(activity, tagName, body)); - } else if (downloadUrl != null) { - // 找到APK但不需要更新,只在用户主动检查更新时提示已是最新版 - if (forceCheck) { - App.post(() -> Notify.show("已是最新版本 " + tagName)); - } - Logger.d("Already latest version: " + tagName); + JSONObject object = new JSONObject(response); + String name = object.optString("name"); + String desc = object.optString("desc"); + int code = object.optInt("code"); + if (need(code, name)) { + App.post(() -> show(activity, name, desc)); } else { - // 未找到对应的APK文件 - // 只在用户主动检查更新时显示提示 - if (forceCheck) { - App.post(() -> Notify.show("检查更新完成,未找到适合此设备的安装包")); - } - Logger.d("APK not found for this device"); + // 不需要更新,提示已是最新版 + Logger.d("Already latest version: " + name); } } catch (Exception e) { - Logger.e("Update check failed", e); e.printStackTrace(); - // 添加用户友好的错误提示,只有在用户主动检查更新时才显示 - App.post(() -> { - if (forceCheck) { // 只有在用户主动检查更新时才显示错误提示 - String errorMsg = "检查更新失败"; - if (e.getMessage() != null && e.getMessage().contains("rate limit")) { - errorMsg = "检查更新失败:API请求过于频繁,请稍后重试"; // 统一错误提示文本 - } else if (e.getMessage() != null && e.getMessage().contains("Not Found")) { - errorMsg = "检查更新失败:更新服务暂时不可用"; - } else { - errorMsg = "检查更新失败,请稍后重试"; - } - Notify.show(errorMsg); - } - }); + App.post(() -> Notify.show("检查更新失败:网络连接异常")); } } @@ -221,16 +145,6 @@ public class Updater implements Download.Callback { } } - /** - * 只有在用户主动检查更新时才显示错误提示 - * @param message 错误提示消息 - */ - private void showErrorIfForceCheck(String message) { - if (forceCheck) { - App.post(() -> Notify.show(message)); - } - } - @Override public void progress(int progress) { dialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(String.format(Locale.getDefault(), "%1$d%%", progress)); @@ -238,7 +152,6 @@ public class Updater implements Download.Callback { @Override public void error(String msg) { - Logger.e("Download error: " + msg); Notify.show(msg); dismiss(); } @@ -248,4 +161,4 @@ public class Updater implements Download.Callback { FileUtil.openFile(file); dismiss(); } -} +} \ No newline at end of file diff --git a/catvod/src/main/java/com/github/catvod/utils/Github.java b/catvod/src/main/java/com/github/catvod/utils/Github.java index b4a674e4..c650ac29 100644 --- a/catvod/src/main/java/com/github/catvod/utils/Github.java +++ b/catvod/src/main/java/com/github/catvod/utils/Github.java @@ -14,12 +14,10 @@ import okhttp3.Response; public class Github { - public static final String URL = "https://raw.githubusercontent.com/Tosencen/XMBOX/main"; - public static final String API_URL = "https://api.github.com/repos/Tosencen/XMBOX/releases/latest"; + public static final String URL = "https://raw.githubusercontent.com/Tosencen/XMBOX-Release/main"; // 国内镜像地址 - 使用Gitee作为镜像 - public static final String CN_URL = "https://gitee.com/ochenoktochen/XMBOX/raw/main"; - public static final String CN_API_URL = "https://gitee.com/api/v5/repos/ochenoktochen/XMBOX/releases/latest"; + public static final String CN_URL = "https://gitee.com/ochenoktochen/XMBOX-Release/raw/main"; // 存储测速结果 private static Boolean useCnMirror = null; @@ -34,9 +32,6 @@ public class Github { return CN_URL + "/" + path + "/" + name; } - public static String getReleaseApi() { - return useCnMirror() ? CN_API_URL : API_URL; - } public static String getJson(boolean dev, String name) { if (useCnMirror()) { diff --git a/create_release.sh b/create_release.sh new file mode 100644 index 00000000..931ea5b6 --- /dev/null +++ b/create_release.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# GitHub CLI 创建 Release 脚本 +# 使用前请先运行: gh auth login + +echo "创建 XMBOX v3.0.7 Release..." + +gh release create v3.0.7 \ + --title "XMBOX v3.0.7 - 全面优化稳定性和用户体验" \ + --notes-file RELEASE_NOTES_v3.0.7.md \ + --draft \ + ~/Desktop/mobile-arm64_v8a-v3.0.7.apk \ + ~/Desktop/mobile-armeabi_v7a-v3.0.7.apk \ + ~/Desktop/leanback-arm64_v8a-v3.0.7.apk \ + ~/Desktop/leanback-armeabi_v7a-v3.0.7.apk + +echo "Release 创建完成(草稿状态)" +echo "请在 GitHub 上检查并发布"