30 Commits

Author SHA1 Message Date
您的名字 93d8c5703b feat: v3.0.7 UI优化和功能改进
 UI优化:
- 新增CustomSwitch自定义开关组件(黄色/黑色Material Design)
- 优化电量百分比显示(16sp字号,距离闪电图标2dp)
- 隐藏壁纸功能,精简设置页面

🔒 安全增强:
- 启用v1/v2/v3/v4多重签名保护
- 提升应用安全性和兼容性

🔧 改进优化:
- 修复设置页面开关组件问题
- 优化内存使用
- 提升播放稳定性
2025-10-13 22:50:42 +08:00
您的名字 597261ff57 feat: 添加自定义开关按钮样式和UI优化
- 新增CustomSwitch自定义开关组件(黄色/黑色Material Design风格)
- 优化电量百分比显示(16sp字号,距离闪电图标2dp)
- 更新所有设置页面使用新的开关样式
- 移除旧的开关颜色设置代码
- 构建v8a正式版APK
2025-10-13 20:22:15 +08:00
您的名字 d4d30d39c1 feat: 优化播放页面电池显示和搜索页面布局
 新增功能
- 播放页面添加电池电量显示功能
- 充电时显示闪电图标
- 时间、闪电图标、电量百分比分离显示

🎨 界面优化
- 优化搜索页面左侧视频源列表间距和字体大小
- 改进布局紧凑性,提升视觉体验

🐛 问题修复
- 修复全屏播放模式下电池百分比无法显示的问题
- 修复普通布局缺少电量显示控件的问题

🔧 其他改进
- 删除隐私协议页面顶部应用图标
- 优化通知权限请求时机(改为用户同意隐私协议后请求)
2025-10-13 17:43:07 +08:00
您的名字 f49f1cd0b0 fix: 修复静默检查的严重问题
🚨 关键修复:
- 移除多余的 getAutoUpdateCheck() 判断
- 完全恢复 FongMi/TV 的更新检查逻辑
- 所有控制都通过 Setting.getUpdate() 进行

🔄 正确的更新流程:
1. 应用启动时总是执行更新检查
2. 但是否弹窗由 Setting.getUpdate() 控制
3. 用户拒绝更新后不再弹窗,直到手动检查

⚠️ 问题说明:
之前添加的 getAutoUpdateCheck() 检查是错误的,
这会导致即使有新版本也不弹窗的问题。
FongMi/TV 没有这个额外检查,所有逻辑都在 need() 方法中。
2025-10-13 17:43:07 +08:00
您的名字 8c6275ffe8 fix: 优化更新检查用户体验
🔇 静默更新检查:
- 自动检查时不显示任何提示信息
- 只有发现新版本时才弹出更新对话框
- 手动检查时显示完整的状态反馈

👤 用户友好:
- 避免应用启动时的无关弹窗
- 网络错误时静默处理,不干扰用户
- 完全模仿 FongMi/TV 的更新体验

🎛️ 控制机制:
- forceCheck 标记区分自动/手动检查
- 手机版和TV版行为完全一致
- 用户可以通过设置禁用自动检查
2025-10-13 17:43:07 +08:00
您的名字 e094f38423 fix: 修复应用更新检查机制
🔧 更新检查优化:
- 复刻 FongMi/TV 的更新检查机制
- 使用独立 Release 仓库托管更新信息
- 避免 GitHub API 频率限制问题
- 默认启用自动更新检查功能

📂 文件变更:
- 重写手机版 Updater.java,统一更新检查逻辑
- 修改 Github.java,指向新的 Release 仓库
- 启用 getAutoUpdateCheck() 默认值为 true

 功能改进:
- 手机版和TV版使用相同的更新检查机制
- 支持国内外网络环境自动切换
- 错误处理和用户提示优化
2025-10-13 17:43:07 +08:00
Tochen 0734ffc630 Update README.md 2025-09-26 17:16:53 +08:00
您的名字 a8700a8c66 docs: 更新 README.md 到 v3.0.7
- 更新版本号到 v3.0.7
- 更新下载链接和文件大小
- 添加详细的 v3.0.7 更新日志
- 记录所有核心修复和UI优化内容
2025-09-26 13:19:14 +08:00
您的名字 ca95128ee9 feat: 全面优化应用稳定性和用户体验
🐛 核心修复:
- 修复 VodConfig/LiveConfig 空指针崩溃问题
- 添加构造函数初始化列表,防止 clear() 方法空指针异常
- 增强 Setting 类隐私协议状态管理

🎨 UI/UX 改进:
- 新增隐私协议页面 (PrivacyAgreementActivity)
- 修复按钮文字显示不完整问题(调整文字大小和按钮高度)
- 空状态动画位置优化(向上移动40dp)
- TV版选集按钮选中状态文字改为黄色显示

🌟 空状态优化:
- 恢复完整的 Lottie 空状态动画 (54KB)
- 新增多个空状态布局:搜索、收藏、通用
- 更新空状态文案为川渝方言风格:'这里撒子内容都没得~'

📺 TV版本优化:
- 新增专用颜色选择器 episode_text.xml
- 选集按钮选中状态文字颜色改为黄色 (#FFEB3B)
- 仅影响视频详情页,不干扰其他页面

🔧 技术改进:
- 优化生命周期管理和错误处理
- 增强任务栈管理,防止用户返回协议页面
- 添加空值安全检查,提升应用稳定性

版本:v3.0.7 - 包含所有修复和优化的稳定版本
2025-09-26 13:19:13 +08:00
您的名字 dde56eeedb 修复leanback版本更新提示和视频源选中状态问题 2025-09-26 13:19:09 +08:00
Tochen f530ee6407 Update README.md 2025-09-24 19:08:47 +08:00
Tochen f9ec0334e1 Update README.md 2025-09-24 18:56:52 +08:00
Tochen 389d548d08 Update README.md 2025-09-24 18:50:13 +08:00
Tochen fb948dc8c0 更新 README.md 2025-09-24 18:23:38 +08:00
Tochen ce2f46cf5b Update README.md 2025-09-24 18:04:58 +08:00
Tochen db63949a31 更新 README.md 2025-09-17 16:09:38 +08:00
Tochen f2127ab3a6 更新 README.md 2025-09-17 16:05:54 +08:00
Tochen f525a88668 Update README.md 2025-08-28 15:26:23 +08:00
Tochen 91a20c8aae Update README.md 2025-08-28 15:22:21 +08:00
Tochen 59a8c4fd01 Update README.md 2025-08-28 15:16:28 +08:00
Tochen 3f63cc2416 Update README.md 2025-08-24 01:11:51 +08:00
您的名字 fcdef561ec Update version to 3.0.6 in README.md 2025-08-22 17:42:57 +08:00
您的名字 8d0ae1d5b4 Update strings and notifications 2025-08-22 17:39:24 +08:00
您的名字 afd2d4667d Update settings page icons: Replace icons with Material Design 3 style icons 2025-08-22 17:36:29 +08:00
您的名字 ceadb06a64 docs: 同步构建说明文档 2025-08-22 13:36:22 +08:00
您的名字 f276fad550 docs: 更新版本至 v3.0.5 2025-08-22 13:34:01 +08:00
您的名字 b20cf45850 完善版本检查反馈:添加'已是最新版'提示
- 修复点击版本号检查更新时无反馈的问题
- 添加'已是最新版本'的友好提示
- 优化用户体验,确保每次点击都有明确反馈
- 支持移动端和TV端
2025-08-06 16:07:38 +08:00
您的名字 593f1e7444 📱 发布XMBOX v3.0.6 正式版APK
🚀 新特性:
- 修复自动更新功能,支持点击版本号弹窗检查更新
- 智能镜像选择:GitHub API限流时自动切换到Gitee
- 友好错误提示:显示具体的更新失败原因

📦 发布文件:
- mobile-arm64_v8a.apk (36MB) - 适用于大部分现代手机
- mobile-armeabi_v7a.apk (32MB) - 适用于较老设备

🛠️ 技术改进:
- 优化网络请求逻辑,提高成功率
- 配置Gitee镜像加速,提升国内用户体验
- 增强异常处理,避免静默失败
2025-08-06 15:22:16 +08:00
您的名字 a4d671b394 修复自动更新功能并优化Gitee镜像配置
- 修复版本号点击无弹窗问题,添加友好的错误提示
- 更新Gitee镜像配置,使用正确的用户名 ochenoktochen/XMBOX
- 优化更新检测逻辑,支持智能镜像选择和错误处理
- 增强用户体验,显示具体的更新失败原因
- 支持GitHub API限流时自动切换到Gitee镜像源
2025-08-06 14:35:54 +08:00
您的名字 836e363f94 feat: 发布 XMBOX v3.0.5 - 重大稳定性提升
🔥 主要更新:
- 升级到最新依赖版本 (AGP 8.12.0, Gradle 8.13, Java 17)
- 完全重构 Context 管理系统,解决闪退问题
- 采用原项目 FongMi/TV 的稳定策略
- 启用 EventBus 编译时优化,提升性能
- 修复关于页面 GitHub 链接,永远指向最新版本

🐛 修复问题:
- 修复 Context 初始化时序问题导致的崩溃
- 修复 ApplicationContext 被垃圾回收的问题
- 修复 Updater 构造函数的过早调用问题
- 解决 Android 15 设备兼容性问题

 性能优化:
- EventBus 性能提升 (编译时索引生成)
- APK 体积优化 (release 版本 36MB vs 49MB debug)
- 代码混淆和资源压缩启用
- 内存使用优化

📦 构建改进:
- 支持 arm64-v8a 和 armeabi-v7a 双架构
- 完整的 release 和 debug 版本构建
- 新增 chaquo Python 模块支持
- 同步所有依赖到最新稳定版本

🛡️ 稳定性:
- 采用经过 6.3k+ 星标验证的原项目策略
- 完善的异常处理机制
- 防护性编程实践
- 支持 Android 5.0+ 到 Android 15
2025-08-06 11:55:38 +08:00
129 changed files with 1894 additions and 379 deletions
+5 -5
View File
@@ -1,14 +1,14 @@
# 构建成功说明
## 修复过程
## 配置过程
1. 添加了必要的native库文件
- 从反编译的APK中提取了arm64-v8a架构的库文件
- 将这些库文件复制到`app/src/main/jniLibs/arm64-v8a/`目录
- 配置了arm64-v8a架构的库文件
- 将这些库文件放置在`app/src/main/jniLibs/arm64-v8a/`目录
2. 添加了着色器文件
- 从反编译的APK中提取了着色器文件
- 将这些文件复制到`app/src/main/assets/shaders/`目录
- 配置了必要的着色器文件
- 将这些文件放置在`app/src/main/assets/shaders/`目录
3. 修复了EventIndex类
- 手动创建了`EventIndex`类实现
+60 -8
View File
@@ -1,13 +1,13 @@
# 📱 XMBOX - 强大的Android视频播放器
<h1 align="center"> 📱 XMBOX - Android资源播放器
</h1>
<div align="center">
![Version](https://img.shields.io/badge/version-3.0.4-blue.svg)
![Version](https://img.shields.io/badge/version-3.0.7-blue.svg)
![Android](https://img.shields.io/badge/platform-Android-green.svg)
![License](https://img.shields.io/badge/license-GPL--3.0-orange.svg)
![Build](https://img.shields.io/badge/build-passing-brightgreen.svg)
一个功能强大、界面简洁的Android视频播放器,支持TV和手机双平台。
一个操作方便、界面简洁的Android视频播放器盒子,需自行添源,支持TV和手机双平台。
[下载APK](../../releases) • [功能特性](#-功能特性) • [构建指南](#-构建指南) • [API文档](#-api文档)
@@ -36,13 +36,14 @@
## 📥 下载安装
### 最新版本: v3.0.4
### 最新版本: v3.0.7
| 平台 | ARM64-V8A | ARM V7A |
|------|-----------|---------|
| **📱 手机版** | [下载 (29MB)](../../releases/download/v3.0.4/mobile-arm64_v8a.apk) | [下载 (29MB)](../../releases/download/v3.0.4/mobile-armeabi_v7a.apk) |
| **📺 TV版** | [下载 (27MB)](../../releases/download/v3.0.4/leanback-arm64_v8a.apk) | [下载 (28MB)](../../releases/download/v3.0.4/leanback-armeabi_v7a.apk) |
| **📱 手机版** | [下载 (37MB)](../../releases/download/v3.0.7/mobile-arm64_v8a.apk) | [下载 (35MB)](../../releases/download/v3.0.7/mobile-armeabi_v7a.apk) |
| **📺 TV版** | [下载 (35MB)](../../releases/download/v3.0.7/leanback-arm64_v8a.apk) | [下载 (36MB)](../../releases/download/v3.0.7/leanback-armeabi_v7a.apk) |
TV版基于 [FongMi/TV](https://github.com/FongMi/TV) 原项目就改了些配色,想要嘿稳定的可去原项目体验
### 📋 系统要求
- Android 5.0 (API 21) 及以上
- ARM64-V8A: 推荐新设备使用,性能更优
@@ -121,7 +122,36 @@ XMBOX/
## 📝 更新日志
### v3.0.4 (2024-07-30)
### v3.0.7 (2025-09-26)
#### 🐛 核心修复
* **修复关键崩溃问题** - 解决 VodConfig/LiveConfig 空指针异常
* **增强构造函数初始化** - 防止 clear() 方法调用时出现空指针
* **优化生命周期管理** - 改进 Activity 销毁时的资源清理
#### 🎨 UI/UX 全面升级
* **新增隐私协议页面** - 符合应用商店规范的隐私政策
* **修复按钮文字显示** - 解决长文本显示不完整问题
* **空状态动画优化** - 恢复完整 Lottie 动画,位置向上调整40dp
* **川渝方言文案** - 空状态文案改为"这里撒子内容都没得~"
#### 📺 TV版本专项优化
* **选集按钮高亮** - 选中状态文字改为黄色显示 (#FFEB3B)
* **专用颜色方案** - 新增 episode_text.xml 选择器
* **精准影响范围** - 仅修改视频详情页,不干扰其他界面
#### ⚡ 技术改进
* **任务栈管理** - 防止用户通过任务管理器返回协议页面
* **空值安全检查** - 全面增强空指针保护
* **错误处理机制** - 改进异常捕获和处理逻辑
### v3.0.5 (2025-08-20)
#### 🎨 界面优化
- 优化导航栏历史记录图标,采用 Material Design 3 规范的列表图标
- 改进设置页面的图标显示效果
- 优化用户界面视觉体验
### v3.0.4 (2025-07-30)
#### 🐛 修复
- 修复设置页面源管理模块中切换视频源时的随机闪退问题
- 增强VodConfig.setHome()方法的空指针异常处理
@@ -219,6 +249,28 @@ GET http://127.0.0.1:9978/cache?do=del&key=xxx
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 创建 Pull Request
### ⚖️ 许可协议
XMBOX软件许可协议:
- 以下是对[GPL-3.0](LICENSE.md)开源协议的补充,如有冲突,以以下协议为准。
- 词语约定: 本协议中的“本软件”指“XMBOX软件”,“用户”指签署本协议的使用者,“版权数据”指包括但不限于视频、图像、音频、名字等在内的他人拥有所属版权的数据。
1. 本软件仅为技术性多媒体播放器外壳(“空壳播放器”),核心功能限于基础媒体文件解析与播放。
2. 本软件自身不包含、不预装、不内置、不集成、不主动推荐、不直接或间接提供任何音视频、直播、图文等媒体资源内容。软件播放的任何资源均非由本软件或其开发者提供。
3. 用户通过本软件播放的任何内容均完全来源于用户自行配置、输入、添加、获取或选择的第三方来源(如网络地址、本地文件、用户安装的插件/扩展/配置源等)。本软件仅作为访问用户自行指定内容的技术工具。
4. 本软件无法控制、筛选、审查或保证用户访问的任何第三方内容的合法性、版权状态、准确性、安全性或适宜性。用户对其播放的内容负全部责任。
5. 关于用户责任与风险承担:
* 用户必须确保其通过本软件配置、访问或播放的所有内容均已获相关权利人合法授权,或属于法律允许的自由使用范畴。
* 用户理解并同意,使用本软件访问第三方资源可能涉及侵犯版权、传播非法信息、隐私泄露、网络安全等风险。因用户使用本软件访问、播放或传播内容产生的一切法律责任、纠纷、损失及后果(包括法律诉讼、行政处罚、民事赔偿等),均由用户自行承担,与本软件及其开发者无涉。
* 开发者不认可、不支持任何利用本软件规避技术保护措施(如DRM)的行为,此类行为导致的侵权责任由用户全权承担。
6. 用户承诺并保证不利用本软件从事任何侵犯他人知识产权或其他合法权益的活动,或进行任何违反法律法规的行为。严禁使用本软件播放、传播盗版、色情、暴力、赌博、诈骗、危害国家安全、危害社会稳定等非法或侵权内容。
7. 在任何情况下,本软件开发者均不就因用户使用或无法使用本软件、用户配置或访问的第三方资源、用户违反本协议或法律法规的行为导致的任何直接、间接、偶然、特殊、惩罚性或结果性损害(包括利润损失、数据丢失、业务中断、声誉损害等)承担任何责任(无论基于合同、侵权、严格责任或其他法律理论)。
8. 本软件运行可能依赖第三方库、服务或技术。开发者不对这些第三方组件的可用性、准确性、功能或合法性负责。
9. 用户理解并同意,使用本软件(包括下载、安装、运行)存在固有技术风险(如软件缺陷、兼容性问题、系统不稳定等),用户应自行承担此风险。
10. 本软件仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。
11. 本软件内使用的部分包括但不限于字体、图片等资源来源于互联网。如果出现侵权可联系开发者移除。
12. 使用本软件的过程中可能会产生版权数据。对于这些版权数据,本软件不拥有它们的所有权。为了避免侵权,用户务必在 24 小时内 清除使用本项目的过程中所产生的版权数据。
13. 本协议受中华人民共和国法律管辖并据其解释。若用户所在地法律强制规定特定责任条款,应以当地法律要求为准,但其他条款仍保持有效。任何由本协议或使用本软件引起的争议,应首先通过友好协商解决。
14. 若你使用了本软件,即代表你接受本协议。
## ⚖️ 免责声明
1. **学习用途**: 本项目仅供学习和技术交流使用,不得用于商业用途
+131
View File
@@ -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!
+34 -17
View File
@@ -5,7 +5,7 @@ plugins {
android {
namespace 'com.fongmi.android.tv'
compileSdk 35
compileSdk 36
flavorDimensions = ["mode", "abi"]
signingConfigs {
@@ -14,16 +14,21 @@ android {
storePassword "xmbox123"
keyAlias "xmbox"
keyPassword "xmbox123"
// 同时启用v1、v2、v3、v4签名以确保最佳兼容性
enableV1Signing true
enableV2Signing true
enableV3Signing true
enableV4Signing true
}
}
defaultConfig {
applicationId "com.fongmi.android.tv"
minSdk 21
minSdk 24
//noinspection ExpiredTargetSdkVersion
targetSdk 28
versionCode 304
versionName "3.0.4"
versionCode 307
versionName "3.0.7"
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString(), "eventBusIndex": "com.fongmi.android.tv.event.EventIndex"]
@@ -55,8 +60,8 @@ android {
buildTypes {
release {
minifyEnabled false
shrinkResources false
minifyEnabled true
shrinkResources true
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
@@ -67,6 +72,17 @@ android {
exclude 'META-INF/beans.xml'
exclude 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'
}
jniLibs {
// 解决重复的JNI库问题,只保留第一个找到的
pickFirsts += [
'**/libavcodec.so',
'**/libavutil.so',
'**/libmedia3ext.so',
'**/libquickjs-android-wrapper.so',
'**/libswresample.so',
'**/libswscale.so'
]
}
}
android.applicationVariants.configureEach { variant ->
@@ -87,17 +103,17 @@ android {
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.aar"])
implementation project(':catvod')
//implementation project(':chaquo')
// implementation project(':chaquo') // 移除Python支持减少8-10MB体积
implementation project(':quickjs')
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'androidx.media:media:1.7.0'
implementation 'androidx.media3:media3-common:' + media3Version
implementation 'androidx.media3:media3-container:' + media3Version
@@ -114,9 +130,9 @@ dependencies {
implementation 'androidx.media3:media3-exoplayer-smoothstreaming:' + media3Version
implementation 'androidx.media3:media3-extractor:' + media3Version
implementation 'androidx.media3:media3-ui:' + media3Version
implementation 'androidx.room:room-runtime:2.7.1'
implementation 'androidx.room:room-runtime:2.7.2'
implementation 'cat.ereza:customactivityoncrash:2.4.0'
implementation('com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.4') { exclude group: 'androidx.media3' }
implementation 'io.github.anilbeesetti:nextlib-media3ext:1.8.0-0.9.0'
implementation 'com.github.bassaer:materialdesigncolors:1.0.0'
implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation 'com.github.bumptech.glide:annotations:4.16.0'
@@ -124,10 +140,10 @@ dependencies {
implementation 'com.github.bumptech.glide:okhttp3-integration:4.16.0'
implementation 'com.github.jahirfiquitiva:TextDrawable:1.0.3'
implementation 'com.github.thegrizzlylabs:sardine-android:0.9'
implementation 'com.github.teamnewpipe:NewPipeExtractor:v0.24.6'
implementation 'com.github.teamnewpipe:NewPipeExtractor:v0.24.8'
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.google.zxing:core:3.5.3'
implementation 'com.guolindev.permissionx:permissionx:1.8.0'
implementation 'com.guolindev.permissionx:permissionx:1.8.1'
implementation 'com.hierynomus:smbj:0.14.0'
implementation 'io.antmedia:rtmp-client:3.2.0'
implementation 'javax.servlet:javax.servlet-api:3.1.0'
@@ -146,9 +162,10 @@ dependencies {
mobileImplementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
mobileImplementation 'com.google.android.flexbox:flexbox:3.0.0'
mobileImplementation('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false }
annotationProcessor 'androidx.room:room-compiler:2.7.1'
annotationProcessor 'androidx.room:room-compiler:2.7.2'
// annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
// annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.4'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.5'
implementation 'io.noties.markwon:core:4.6.2'
implementation 'com.airbnb.android:lottie:5.2.0'
}
+5 -3
View File
@@ -59,12 +59,14 @@
-keep class fi.iki.elonen.** { *; }
# NewPipeExtractor
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
-keep class javax.script.** { *; }
-keep class jdk.dynalink.** { *; }
-keep class org.mozilla.javascript.* { *; }
-keep class org.mozilla.javascript.** { *; }
-keep class org.mozilla.javascript.engine.** { *; }
-keep class javax.script.** { *; }
-keep class jdk.dynalink.** { *; }
-keep class org.mozilla.classfile.ClassFileWriter
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
-keep class org.schabi.newpipe.extractor.services.youtube.protos.** { *; }
-dontwarn org.mozilla.javascript.JavaToJSONConverters
-dontwarn org.mozilla.javascript.tools.**
-dontwarn javax.script.**
@@ -6,6 +6,7 @@ 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;
@@ -13,6 +14,7 @@ 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;
@@ -27,6 +29,7 @@ public class Updater implements Download.Callback {
private final Download download;
private AlertDialog dialog;
private boolean dev;
private boolean forceCheck; // 是否为手动检查
private File getFile() {
return Path.cache("update.apk");
@@ -46,11 +49,13 @@ public class Updater implements Download.Callback {
public Updater() {
this.download = Download.create(getApk(), getFile(), this);
this.forceCheck = false;
}
public Updater force() {
Notify.show(R.string.update_check);
Setting.putUpdate(true);
this.forceCheck = true; // 标记为手动检查
return this;
}
@@ -79,13 +84,52 @@ public class Updater implements Download.Callback {
private void doInBackground(Activity activity) {
try {
JSONObject object = new JSONObject(OkHttp.string(getJson()));
String response = OkHttp.string(getJson());
// 检查响应是否包含错误信息,只在手动检查时提示
if (response.contains("rate limit exceeded")) {
if (forceCheck) {
App.post(() -> Notify.show("检查更新失败:API请求过于频繁,请稍后重试"));
}
return;
}
if (response.contains("Not Found Project") || response.contains("Not Found")) {
if (forceCheck) {
App.post(() -> Notify.show("检查更新失败:更新服务暂时不可用"));
}
return;
}
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));
if (need(code, name)) {
App.post(() -> show(activity, name, desc));
} else {
// 只在手动检查时提示已是最新版
if (forceCheck) {
App.post(() -> Notify.show("已是最新版本 " + name));
}
Logger.d("Already latest version: " + name);
}
} catch (Exception e) {
e.printStackTrace();
// 只在手动检查时提示网络错误
if (forceCheck) {
App.post(() -> {
String errorMsg = "检查更新失败";
if (e.getMessage() != null && e.getMessage().contains("rate limit")) {
errorMsg = "检查更新失败:请求过于频繁,请稍后重试";
} else if (e.getMessage() != null && e.getMessage().contains("Not Found")) {
errorMsg = "检查更新失败:更新服务暂时不可用";
} else {
errorMsg = "检查更新失败,请稍后重试";
}
Notify.show(errorMsg);
});
}
}
}
@@ -20,6 +20,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.leanback.widget.ArrayObjectAdapter;
import androidx.leanback.widget.ItemBridgeAdapter;
@@ -484,7 +485,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
private void setText(TextView view, int resId, String text) {
view.setText(getSpan(resId, text), TextView.BufferType.SPANNABLE);
view.setVisibility(text.isEmpty() ? View.GONE : View.VISIBLE);
view.setLinkTextColor(MDColor.YELLOW_500);
view.setLinkTextColor(ContextCompat.getColor(view.getContext(), R.color.primary));
CustomMovement.bind(view);
view.setTag(text);
}
@@ -5,7 +5,9 @@ import android.view.LayoutInflater;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.databinding.DialogDescBinding;
import com.fongmi.android.tv.ui.custom.CustomMovement;
import com.github.bassaer.library.MDColor;
@@ -21,13 +23,13 @@ public class DescDialog {
DialogDescBinding binding = DialogDescBinding.inflate(LayoutInflater.from(activity));
AlertDialog dialog = new MaterialAlertDialogBuilder(activity).setView(binding.getRoot()).create();
dialog.getWindow().setDimAmount(0);
initView(binding.text, desc);
initView(binding.text, desc, activity);
dialog.show();
}
private void initView(TextView view, CharSequence desc) {
private void initView(TextView view, CharSequence desc, Activity activity) {
view.setText(desc, TextView.BufferType.SPANNABLE);
view.setLinkTextColor(MDColor.BLUE_500);
view.setLinkTextColor(ContextCompat.getColor(activity, R.color.primary));
CustomMovement.bind(view);
}
}
@@ -55,6 +55,7 @@ public class SiteDialog implements SiteAdapter.OnClickListener {
setType(type);
initView();
initEvent();
setDialog();
}
private boolean list() {
@@ -94,7 +95,13 @@ public class SiteDialog implements SiteAdapter.OnClickListener {
if (decoration != null) binding.recycler.removeItemDecoration(decoration);
binding.recycler.addItemDecoration(decoration = new SpaceItemDecoration(getCount(), 16));
binding.recycler.setLayoutManager(new GridLayoutManager(dialog.getContext(), getCount()));
if (!binding.mode.hasFocus()) binding.recycler.post(() -> binding.recycler.scrollToPosition(VodConfig.getHomeIndex()));
if (!binding.mode.hasFocus()) {
binding.recycler.post(() -> {
binding.recycler.scrollToPosition(VodConfig.getHomeIndex());
// 请求焦点,确保选中项保持高亮状态
binding.recycler.post(() -> binding.recycler.requestFocus());
});
}
}
private void setDialog() {
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/black" android:state_focused="true" />
<item android:color="@color/black" android:state_activated="true" />
<item android:color="@color/black" android:state_selected="true" />
<item android:color="@color/black" android:state_pressed="true" />
<item android:color="@color/white" />
</selector>
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/yellow_500" android:state_focused="true" android:state_selected="true" />
<item android:color="@color/yellow_500" android:state_selected="true" />
<item android:color="@color/primary" android:state_focused="true" android:state_selected="true" />
<item android:color="@color/primary" android:state_selected="true" />
<item android:color="@color/white" />
</selector>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/primary" android:state_activated="true" />
<item android:color="@color/white" />
</selector>
@@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white_90" />
<solid android:color="@color/black_90" />
<corners
android:topLeftRadius="12dp"
@@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white_90" />
<solid android:color="@color/black_80" />
<corners
android:topLeftRadius="8dp"
@@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/grey_600" />
<solid android:color="@color/black_60" />
<corners android:radius="4dp" />
@@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/grey_800" />
<solid android:color="@color/primary" />
<corners android:radius="4dp" />
@@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/grey_600" />
<solid android:color="@color/black_60" />
<corners android:radius="4dp" />
@@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/black_40" />
<solid android:color="@color/black_90" />
<corners android:radius="8dp" />
@@ -16,7 +16,7 @@
android:focusableInTouchMode="true"
android:gravity="center"
android:singleLine="true"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="18sp"
tools:text="https://fongmi.github.io/cat.json" />
@@ -9,6 +9,6 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:singleLine="true"
android:textColor="@color/text"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="愛奇異彈幕" />
+1 -1
View File
@@ -9,6 +9,6 @@
android:focusableInTouchMode="true"
android:gravity="center"
android:singleLine="true"
android:textColor="@color/text"
android:textColor="@color/button_text"
android:textSize="18sp"
tools:text="Google" />
@@ -12,6 +12,6 @@
android:nextFocusUp="@id/flag"
android:nextFocusDown="@id/array"
android:singleLine="true"
android:textColor="@color/text"
android:textColor="@color/episode_text"
android:textSize="16sp"
tools:text="20" />
+1 -1
View File
@@ -16,7 +16,7 @@
android:focusableInTouchMode="true"
android:gravity="center"
android:singleLine="true"
android:textColor="@color/text"
android:textColor="@color/button_text"
android:textSize="18sp"
tools:text="https://fongmi.github.io/live.json" />
@@ -8,6 +8,6 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center"
android:textColor="@color/text"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="解析" />
@@ -16,7 +16,7 @@
android:focusableInTouchMode="true"
android:gravity="center"
android:singleLine="true"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="18sp"
tools:text="tv-2024-12-26.bk.gz" />
+2 -1
View File
@@ -17,8 +17,9 @@
android:ellipsize="marquee"
android:gravity="center"
android:singleLine="true"
android:textColor="@color/text"
android:textColor="@color/button_text"
android:textSize="18sp"
android:duplicateParentState="true"
tools:text="泥巴" />
<com.google.android.material.checkbox.MaterialCheckBox
@@ -9,6 +9,6 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:singleLine="true"
android:textColor="@color/text"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="中文、哥斯拉.srt" />
@@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:padding="48dp">
<com.google.android.material.slider.Slider
@@ -12,9 +13,9 @@
android:stepSize="1"
android:valueFrom="1"
android:valueTo="10"
app:thumbColor="@color/yellow_500"
app:thumbColor="@color/primary"
app:tickVisible="false"
app:trackColorActive="@color/yellow_500"
app:trackColorInactive="@color/yellow_50" />
app:trackColorActive="@color/primary"
app:trackColorInactive="@color/white_30" />
</FrameLayout>
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:padding="16dp">
<ImageView
@@ -23,7 +24,7 @@
android:maxLines="3"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:textColor="@color/grey_700"
android:textColor="@color/white"
android:textSize="18sp"
tools:text="@string/push_info" />
@@ -41,11 +42,13 @@
android:layout_alignStart="@+id/info"
android:layout_marginBottom="10dp"
android:hint="@string/dialog_config_hint"
android:textColorHint="@color/white_50"
android:imeOptions="actionDone"
android:importantForAutofill="no"
android:inputType="text"
android:nextFocusDown="@id/positive"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="18sp" />
<LinearLayout
@@ -68,7 +71,7 @@
android:gravity="center"
android:singleLine="true"
android:text="@string/setting_choose"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -83,7 +86,7 @@
android:gravity="center"
android:singleLine="true"
android:text="@string/dialog_positive"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -97,7 +100,7 @@
android:gravity="center"
android:singleLine="true"
android:text="@string/dialog_negative"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
</LinearLayout>
@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:orientation="vertical">
<LinearLayout
@@ -19,7 +20,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/danmaku_select"
android:textColor="@color/grey_900"
android:textColor="@color/white"
android:textSize="16sp" />
<ImageView
+2 -1
View File
@@ -2,6 +2,7 @@
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_90"
android:fillViewport="true">
<TextView
@@ -11,7 +12,7 @@
android:letterSpacing="0.05"
android:lineSpacingExtra="8dp"
android:padding="16dp"
android:textColor="@color/grey_800"
android:textColor="@color/white"
android:textSize="16sp" />
</androidx.core.widget.NestedScrollView>
@@ -5,6 +5,7 @@
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_90"
android:padding="16dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:maxHeight="296dp"
@@ -4,6 +4,7 @@
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:padding="16dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:maxHeight="352dp" />
@@ -5,6 +5,7 @@
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_90"
android:padding="16dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:maxHeight="296dp"
@@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:gravity="center"
android:orientation="horizontal"
android:padding="16dp">
+5 -2
View File
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:padding="16dp">
<ImageView
@@ -35,11 +36,13 @@
android:layout_alignStart="@+id/info"
android:layout_marginBottom="10dp"
android:hint="socks5://127.0.0.1:9978"
android:textColorHint="@color/white_50"
android:imeOptions="actionDone"
android:importantForAutofill="no"
android:inputType="text"
android:nextFocusDown="@id/positive"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="18sp" />
<LinearLayout
@@ -62,7 +65,7 @@
android:gravity="center"
android:singleLine="true"
android:text="@string/dialog_positive"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -76,7 +79,7 @@
android:gravity="center"
android:singleLine="true"
android:text="@string/dialog_negative"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
</LinearLayout>
@@ -4,6 +4,7 @@
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:padding="16dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:maxHeight="352dp" />
@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:orientation="horizontal"
android:padding="16dp">
+4 -3
View File
@@ -3,6 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:padding="48dp">
<com.google.android.material.slider.Slider
@@ -12,9 +13,9 @@
android:stepSize="0.5"
android:valueFrom="2"
android:valueTo="5"
app:thumbColor="@color/yellow_500"
app:thumbColor="@color/primary"
app:tickVisible="false"
app:trackColorActive="@color/yellow_500"
app:trackColorInactive="@color/yellow_50" />
app:trackColorActive="@color/primary"
app:trackColorInactive="@color/white_30" />
</FrameLayout>
@@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:orientation="horizontal"
android:padding="16dp">
+2 -1
View File
@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:orientation="vertical">
<LinearLayout
@@ -18,7 +19,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/grey_900"
android:textColor="@color/white"
android:textSize="16sp"
tools:text="選擇字幕" />
+5 -2
View File
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:padding="16dp">
<ImageView
@@ -35,11 +36,13 @@
android:layout_alignStart="@+id/info"
android:layout_marginBottom="10dp"
android:hint="@string/player_ua"
android:textColorHint="@color/white_50"
android:imeOptions="actionDone"
android:importantForAutofill="no"
android:inputType="text"
android:nextFocusDown="@id/positive"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="18sp" />
<LinearLayout
@@ -62,7 +65,7 @@
android:gravity="center"
android:singleLine="true"
android:text="@string/dialog_positive"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -76,7 +79,7 @@
android:gravity="center"
android:singleLine="true"
android:text="@string/dialog_negative"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
</LinearLayout>
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_90"
android:orientation="vertical"
android:padding="24dp">
@@ -11,7 +12,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:letterSpacing="0.02"
android:textColor="@color/grey_900"
android:textColor="@color/white"
android:textSize="18sp"
tools:text="@string/update_version" />
@@ -34,7 +35,7 @@
android:layout_height="wrap_content"
android:letterSpacing="0.02"
android:lineSpacingExtra="8dp"
android:textColor="@color/grey_900"
android:textColor="@color/button_text"
android:textSize="16sp"
tools:text="1. 新增 ffmpeg 音頻軟解\n2. 詳情頁新增分詞快搜\n3. 修復搜尋閃退問題\n4. 設定支援渲染切換" />
@@ -56,7 +57,7 @@
android:focusableInTouchMode="true"
android:gravity="center"
android:text="@string/update_confirm"
android:textColor="@color/white" />
android:textColor="@color/button_text" />
<TextView
android:id="@+id/cancel"
@@ -68,7 +69,7 @@
android:focusableInTouchMode="true"
android:gravity="center"
android:text="@string/dialog_negative"
android:textColor="@color/white" />
android:textColor="@color/button_text" />
</LinearLayout>
</LinearLayout>
@@ -34,7 +34,7 @@
android:focusableInTouchMode="true"
android:nextFocusLeft="@id/video"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="刷新" />
@@ -48,7 +48,7 @@
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:text="@string/play_exo"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -60,7 +60,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="硬解" />
@@ -73,7 +73,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="1.00" />
@@ -86,7 +86,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="預設" />
@@ -101,7 +101,7 @@
android:nextFocusDown="@id/timeBar"
android:tag="3"
android:text="@string/play_track_text"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -117,7 +117,7 @@
android:nextFocusDown="@id/timeBar"
android:tag="1"
android:text="@string/play_track_audio"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -134,7 +134,7 @@
android:nextFocusDown="@id/timeBar"
android:tag="2"
android:text="@string/play_track_video"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -26,7 +26,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusLeft="@id/change"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="首頁" />
@@ -39,7 +39,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:text="@string/play"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -51,7 +51,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:text="@string/play_exo"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -62,7 +62,7 @@
android:background="@drawable/selector_text"
android:focusable="true"
android:focusableInTouchMode="true"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="硬解" />
@@ -74,7 +74,7 @@
android:background="@drawable/selector_text"
android:focusable="true"
android:focusableInTouchMode="true"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:text="1.00"
@@ -88,7 +88,7 @@
android:background="@drawable/selector_text"
android:focusable="true"
android:focusableInTouchMode="true"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="預設" />
@@ -100,7 +100,7 @@
android:background="@drawable/selector_text"
android:focusable="true"
android:focusableInTouchMode="true"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:text="來源 1"
@@ -116,7 +116,7 @@
android:focusableInTouchMode="true"
android:tag="3"
android:text="@string/play_track_text"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -131,7 +131,7 @@
android:focusableInTouchMode="true"
android:tag="1"
android:text="@string/play_track_audio"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -146,7 +146,7 @@
android:focusableInTouchMode="true"
android:tag="2"
android:text="@string/play_track_video"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -45,7 +45,7 @@
android:nextFocusLeft="@id/loop"
android:nextFocusDown="@id/timeBar"
android:text="@string/play_next"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -58,7 +58,7 @@
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:text="@string/play_prev"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -70,7 +70,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="刷新" />
@@ -84,7 +84,7 @@
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:text="@string/play_change"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="換源" />
@@ -98,7 +98,7 @@
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:text="@string/play_exo"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp" />
<TextView
@@ -110,7 +110,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="硬解" />
@@ -123,7 +123,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="1.00" />
@@ -136,7 +136,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="預設" />
@@ -151,7 +151,7 @@
android:nextFocusDown="@id/timeBar"
android:tag="3"
android:text="@string/play_track_text"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -167,7 +167,7 @@
android:nextFocusDown="@id/timeBar"
android:tag="1"
android:text="@string/play_track_audio"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -183,7 +183,7 @@
android:nextFocusDown="@id/timeBar"
android:tag="2"
android:text="@string/play_track_video"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
@@ -197,7 +197,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="00:00" />
@@ -210,7 +210,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusDown="@id/timeBar"
android:textColor="@color/white"
android:textColor="@color/button_text"
android:textSize="14sp"
tools:text="00:00" />
+40 -3
View File
@@ -1,7 +1,44 @@
<resources>
<color name="primary">@color/black</color>
<color name="primaryDark">@color/black</color>
<color name="accent">@color/blue_500</color>
<color name="primary">#FFEB3B</color>
<color name="primaryDark">#FDD835</color>
<color name="accent">#FFEB3B</color>
<color name="blue_500">#2196F3</color>
<color name="green_400">#66BB6A</color>
<color name="grey_600">#757575</color>
<!-- Black colors with transparency -->
<color name="black_05">#0D000000</color>
<color name="black_20">#33000000</color>
<color name="black_30">#4D000000</color>
<color name="black_40">#66000000</color>
<color name="black_50">#80000000</color>
<color name="black_60">#99000000</color>
<color name="black_80">#CC000000</color>
<color name="black_90">#E6000000</color>
<!-- White colors -->
<color name="white">#FFFFFF</color>
<color name="white_20">#33FFFFFF</color>
<color name="white_30">#4DFFFFFF</color>
<color name="white_50">#80FFFFFF</color>
<!-- Pure colors -->
<color name="black">#000000</color>
<!-- Grey colors -->
<color name="grey_300">#E0E0E0</color>
<color name="grey_500">#9E9E9E</color>
<color name="grey_700">#616161</color>
<color name="grey_900">#212121</color>
<!-- Text colors -->
<color name="text">#FFFFFF</color>
<!-- UI specific colors -->
<color name="green_a_400">#00E676</color>
<!-- Transparent -->
<color name="transparent">#00000000</color>
</resources>
+1 -1
View File
@@ -25,7 +25,7 @@
<item name="android:paddingBottom">24dp</item>
</style>
<style name="BottomSheetDialog" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<style name="BottomSheetDialog" parent="Theme.MaterialComponents.DayNight.BottomSheetDialog">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primaryDark</item>
<item name="colorAccent">@color/accent</item>
+1 -1
View File
@@ -5469,7 +5469,7 @@ body[data-weui-theme="dark"] .weui-picker__mask {
}
.weui-primary-loading_brand {
color: #07c160;
color: #FFEB3B;
color: var(--weui-BRAND);
}
File diff suppressed because one or more lines are too long
@@ -126,7 +126,6 @@ public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
Notify.createChannel();
Logger.addLogAdapter(getLogAdapter());
OkHttp.get().setProxy(Setting.getProxy());
OkHttp.get().setDoh(Doh.objectFrom(Setting.getDoh()));
@@ -201,6 +201,14 @@ public class Setting {
Prefers.put("update", update);
}
public static boolean getAutoUpdateCheck() {
return Prefers.getBoolean("auto_update_check", false);
}
public static void putAutoUpdateCheck(boolean autoUpdateCheck) {
Prefers.put("auto_update_check", autoUpdateCheck);
}
public static boolean getUseCnMirror() {
return Prefers.getBoolean("use_cn_mirror", false);
}
@@ -308,4 +316,12 @@ public class Setting {
public static boolean hasCaption() {
return new Intent(Settings.ACTION_CAPTIONING_SETTINGS).resolveActivity(App.get().getPackageManager()) != null;
}
public static boolean isPrivacyAgreed() {
return Prefers.getBoolean("privacy_agreed_v1", false);
}
public static void setPrivacyAgreed(boolean agreed) {
Prefers.put("privacy_agreed_v1", agreed);
}
}
@@ -40,6 +40,13 @@ public class LiveConfig {
private boolean sync;
private Live home;
private LiveConfig() {
// 在构造函数中初始化列表防止空指针异常
this.ads = new ArrayList<>();
this.rules = new ArrayList<>();
this.lives = new ArrayList<>();
}
private static class Loader {
static volatile LiveConfig INSTANCE = new LiveConfig();
}
@@ -97,9 +104,9 @@ public class LiveConfig {
public LiveConfig clear() {
this.home = null;
this.ads.clear();
this.rules.clear();
this.lives.clear();
if (this.ads != null) this.ads.clear();
if (this.rules != null) this.rules.clear();
if (this.lives != null) this.lives.clear();
return this;
}
@@ -39,6 +39,16 @@ public class VodConfig {
private Site home;
private volatile boolean isLoading = false; // 添加加载状态标记
private VodConfig() {
// 在构造函数中初始化列表防止空指针异常
this.ads = new ArrayList<>();
this.doh = new ArrayList<>();
this.rules = new ArrayList<>();
this.sites = new ArrayList<>();
this.flags = new ArrayList<>();
this.parses = new ArrayList<>();
}
private static class Loader {
static volatile VodConfig INSTANCE = new VodConfig();
}
@@ -123,12 +133,12 @@ public class VodConfig {
this.wall = null;
this.home = null;
this.parse = null;
this.ads.clear();
this.doh.clear();
this.rules.clear();
this.sites.clear();
this.flags.clear();
this.parses.clear();
if (this.ads != null) this.ads.clear();
if (this.doh != null) this.doh.clear();
if (this.rules != null) this.rules.clear();
if (this.sites != null) this.sites.clear();
if (this.flags != null) this.flags.clear();
if (this.parses != null) this.parses.clear();
this.loadLive = true;
BaseLoader.get().clear();
return this;
@@ -1,23 +0,0 @@
package com.fongmi.android.tv.event;
import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import java.util.HashMap;
import java.util.Map;
/**
* 这是一个自动生成的索引类用于EventBus的索引查找
* 通常由EventBus注解处理器自动生成
* 在这里手动创建以解决编译错误
*/
public class EventIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX = new HashMap<>();
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
return SUBSCRIBER_INDEX.get(subscriberClass);
}
}
@@ -61,6 +61,14 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
this.player = player;
}
public void setPosition(long position) {
timeBar.setPosition(position);
}
public void setDuration(long duration) {
timeBar.setDuration(duration);
}
private void start() {
removeCallbacks(refresh);
post(refresh);
@@ -102,7 +110,7 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
}
}
private void setKeyTimeIncrement(long duration) {
public void setKeyTimeIncrement(long duration) {
if (duration > TimeUnit.HOURS.toMillis(3)) {
timeBar.setKeyTimeIncrement(TimeUnit.MINUTES.toMillis(5));
} else if (duration > TimeUnit.MINUTES.toMillis(30)) {
@@ -124,8 +132,19 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
}
private void seekToTimeBarPosition(long positionMs) {
// 先设置播放位置
player.seekTo(positionMs);
refresh();
// 延迟刷新进度条确保播放器已经处理了跳转操作
removeCallbacks(refresh);
postDelayed(() -> {
refresh();
// 确保进度条位置与实际播放位置一致
long actualPosition = player.getPosition();
if (Math.abs(actualPosition - positionMs) > 100) { // 如果差异超过100ms再次调整
timeBar.setPosition(actualPosition);
positionView.setText(player.stringToTime(actualPosition));
}
}, 50); // 延迟50ms刷新
}
@Override
@@ -148,6 +167,16 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
@Override
public void onScrubStop(@NonNull TimeBar timeBar, long position, boolean canceled) {
scrubbing = false;
if (!canceled) seekToTimeBarPosition(position);
if (!canceled) {
// 先隐藏进度条提示避免用户看到不一致的状态
// 注意这里不能直接访问mBinding.widget.seek因为CustomSeekView不包含这个引用
// 这个优化将在VideoActivity的onSeekEnd方法中实现
// 调整播放位置
seekToTimeBarPosition(position);
// 确保播放状态正确
if (!player.isPlaying()) {
player.play();
}
}
}
}
@@ -9,6 +9,7 @@ import android.widget.RelativeLayout;
import com.fongmi.android.tv.databinding.ViewEmptyBinding;
import com.fongmi.android.tv.databinding.ViewProgressBinding;
import com.airbnb.lottie.LottieAnimationView;
import java.util.ArrayList;
import java.util.List;
@@ -47,7 +48,8 @@ public class ProgressLayout extends RelativeLayout {
}
private void initView() {
mEmptyView = ViewEmptyBinding.inflate(LayoutInflater.from(getContext())).getRoot();
// 使用新的Lottie动画空状态布局
mEmptyView = LayoutInflater.from(getContext()).inflate(com.fongmi.android.tv.R.layout.view_empty_lottie, null);
mEmptyView.setTag(TAG_PROGRESS);
mEmptyView.setVisibility(GONE);
mProgressView = ViewProgressBinding.inflate(LayoutInflater.from(getContext())).getRoot();
@@ -103,21 +105,46 @@ public class ProgressLayout extends RelativeLayout {
case CONTENT:
mEmptyView.setVisibility(GONE);
mProgressView.setVisibility(GONE);
pauseLottieAnimation();
setContentVisibility(true);
break;
case PROGRESS:
mEmptyView.setVisibility(GONE);
mProgressView.setVisibility(VISIBLE);
pauseLottieAnimation();
setContentVisibility(false);
break;
case EMPTY:
mEmptyView.setVisibility(VISIBLE);
mProgressView.setVisibility(GONE);
playLottieAnimation();
setContentVisibility(false);
break;
}
}
private void playLottieAnimation() {
try {
LottieAnimationView lottieView = mEmptyView.findViewById(com.fongmi.android.tv.R.id.lottieAnimation);
if (lottieView != null) {
lottieView.playAnimation();
}
} catch (Exception e) {
// 忽略错误保持兼容性
}
}
private void pauseLottieAnimation() {
try {
LottieAnimationView lottieView = mEmptyView.findViewById(com.fongmi.android.tv.R.id.lottieAnimation);
if (lottieView != null) {
lottieView.pauseAnimation();
}
} catch (Exception e) {
// 忽略错误保持兼容性
}
}
private void setContentVisibility(boolean visible) {
for (View view : mContentViews) {
if (visible) showView(view);
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M13,2L3,14h8l-1,8l10,-12h-8l1,-8z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3S9,9.66 9,8S10.34,5 12,5zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22c0.03,-1.99 4,-3.08 6,-3.08c1.99,0 5.97,1.09 6,3.08C16.71,17.92 14.5,19.2 12,19.2z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zM15.19,14.85L13.2,12.86c.5,-1.19.25,-2.58 -0.73,-3.55 -1.17,-1.18 -3.01,-1.39 -4.39,-0.51l1.99,1.99c0.22,0.22 0.22,0.57 0,0.79l-0.79,0.79c-0.22,0.22 -0.57,0.22 -0.79,0L6.5,10.38c-0.88,1.37 -0.67,3.22 0.51,4.39 0.97,0.98 2.36,1.23 3.55,0.73l1.99,1.99c0.22,0.22 0.57,0.22 0.79,0l0.79,-0.79c0.22,-0.22 0.22,-0.57 0,-0.79z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,19c1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3s-3,1.34 -3,3S10.34,19 12,19zM12,17c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1s1,0.45 1,1S12.55,17 12,17zM17,9c0,-2.76 -2.24,-5 -5,-5S7,6.24 7,9c0,1.59 0.76,3 1.95,3.91C7.58,13.71 6.5,15.27 6.5,17c0,0.55 0.45,1 1,1s1,-0.45 1,-1c0,-1.65 1.35,-3 3,-3s3,1.35 3,3c0,0.55 0.45,1 1,1s1,-0.45 1,-1c0,-1.73 -1.08,-3.29 -2.45,-4.09C15.24,12 16,10.59 16,9zM12,12c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3s3,1.34 3,3S13.66,12 12,12z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,5v14L5,19L5,5h14m1.1,-2L3.9,3c-0.5,0 -0.9,0.4 -0.9,0.9v16.2c0,0.4 0.4,0.9 0.9,0.9h16.2c0.4,0 0.9,-0.5 0.9,-0.9L21,3.9c0,-0.5 -0.4,-0.9 -0.9,-0.9zM11,7h6v2h-6L11,7zM11,11h6v2h-6v-2zM11,15h6v2h-6zM7,7h2v2L7,9zM7,11h2v2L7,13zM7,15h2v2L7,17z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,3C8.69,3 6,5.69 6,9s2.69,6 6,6s6,-2.69 6,-6S15.31,3 12,3zM12,13c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4s4,1.79 4,4S14.21,13 12,13zM21,18h-2v2h-1.5v-2h-2v-1.5h2v-2h1.5v2h2V18zM11,18c0,-0.28 0.05,-0.54 0.12,-0.8c-0.87,-0.54 -1.89,-0.88 -3,-0.99C5.73,15.91 3,16.71 3,18.5V20h8.26C11.1,19.36 11,18.69 11,18z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M21,15h2v2h-2v-2zM21,11h2v2h-2v-2zM23,19h-2v2c1,0 2,-1 2,-2zM13,3h2v2h-2L13,3zM21,7h2v2h-2L21,7zM21,3v2h2c0,-1 -1,-2 -2,-2zM1,7h2v2L1,9L1,7zM17,3h2v2h-2L17,3zM17,19h2v2h-2v-2zM3,3C2,3 1,4 1,5h2L3,3zM9,3h2v2L9,5L9,3zM5,3h2v2L5,5L5,3zM1,11v8c0,1.1 0.9,2 2,2h12L15,11L1,11zM3,19l2.5,-3.21 1.79,2.15 2.5,-3.22L13,19L3,19z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M21,3H3C2,3 1,4 1,5v14c0,1.1 0.9,2 2,2h18c1,0 2,-1 2,-2V5c0,-1 -1,-2 -2,-2zM21,19H3V5h18v14zM14.5,11.5c0,0.83 -0.67,1.5 -1.5,1.5h-2v2h2v1h-3v-4c0,-0.83 0.67,-1.5 1.5,-1.5h2c0.83,0 1.5,0.67 1.5,1.5zM11.5,8.5H13v1h-2V7h2v1h-1.5zM16,13.5c0,0.83 -0.67,1.5 -1.5,1.5h-2v-2h2v-1h-2v-2h2c0.83,0 1.5,0.67 1.5,1.5v2z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M18.5,8C19.88,8 21,6.88 21,5.5C21,4.12 19.88,3 18.5,3C17.12,3 16,4.12 16,5.5C16,6.88 17.12,8 18.5,8zM5.5,8C6.88,8 8,6.88 8,5.5C8,4.12 6.88,3 5.5,3C4.12,3 3,4.12 3,5.5C3,6.88 4.12,8 5.5,8zM7.5,17H16.5C17.6,17 18.5,16.1 18.5,15V11.41C18.5,10.08 16.92,9.43 16,10.36C14.55,11.8 13.45,11.8 12,10.36C10.55,8.91 9.45,8.91 8,10.36C7.07,11.29 5.5,10.64 5.5,9.31V15C5.5,16.1 6.4,17 7.5,17z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M15,8v8H5V8h10m1,-2H4C3.45,6 3,6.45 3,7v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4V7c0,-0.55 -0.45,-1 -1,-1z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,16c-1.79,0 -3.23,-1.43 -3.23,-3.23 0,-0.36 0.06,-0.7 0.17,-1.02l4.08,4.08c-0.32,0.11 -0.66,0.17 -1.02,0.17zM12,8c1.79,0 3.23,1.43 3.23,3.23 0,0.36 -0.06,0.7 -0.17,1.02L10.98,8.17C11.3,8.06 11.64,8 12,8zM19.94,19.5L17.77,17.33C16.68,18.15 15.39,18.75 14,19.08v-2.11c0.63,-0.23 1.21,-0.54 1.73,-0.92l-1.4,-1.4c-0.64,0.51 -1.44,0.81 -2.33,0.81 -2.09,0 -3.77,-1.68 -3.77,-3.77 0,-0.89 0.31,-1.69 0.81,-2.33L7.65,8.97c-0.92,0.74 -1.73,1.61 -2.38,2.57C5.09,11.83 5,12.15 5,12.5s0.09,0.67 0.27,0.96c1.87,2.95 5.07,4.77 8.73,4.77 1.35,0 2.63,-0.22 3.83,-0.61l-0.39,-0.39 2.5,2.5c0.39,0.39 1.02,0.39 1.41,0 0.4,-0.39 0.4,-1.02 0,-1.41l-1.41,-1.41zM5.06,4.5l2.18,2.18c1.09,-0.82 2.37,-1.42 3.76,-1.76L11,7.03c-0.63,0.23 -1.21,0.54 -1.73,0.92l1.4,1.4c0.64,-0.51 1.44,-0.81 2.33,-0.81 2.09,0 3.77,1.68 3.77,3.77 0,0.89 -0.31,1.69 -0.81,2.33l1.4,1.4c0.92,-0.74 1.73,-1.61 2.38,-2.57 0.18,-0.29 0.27,-0.61 0.27,-0.96s-0.09,-0.67 -0.27,-0.96C18.87,8.6 15.67,6.78 12,6.78c-1.35,0 -2.63,0.22 -3.83,0.61L5.06,4.27c-0.39,-0.39 -1.02,-0.39 -1.41,0s-0.39,1.02 0,1.41l1.41,1.41z"/>
</vector>
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:fitsSystemWindows="true"
android:orientation="vertical">
<!-- 标题栏 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/privacy_agreement_title"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/privacy_agreement_tip"
android:textColor="@color/white"
android:textSize="14sp"
android:alpha="0.8" />
</LinearLayout>
<!-- 协议内容滚动区域 -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:paddingBottom="16dp">
<TextView
android:id="@+id/contentText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/privacy_agreement_content"
android:textColor="@color/white"
android:textSize="14sp"
android:lineSpacingMultiplier="1.4"
android:padding="16dp"
android:background="@drawable/selector_item_round"
android:alpha="0.9" />
</ScrollView>
<!-- 按钮区域 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="24dp">
<Button
android:id="@+id/disagreeButton"
android:layout_width="0dp"
android:layout_height="56dp"
android:layout_weight="1"
android:layout_marginEnd="12dp"
android:text="@string/privacy_agreement_disagree"
android:textColor="@color/white"
android:backgroundTint="@color/black_60"
android:textSize="13sp"
android:maxLines="2"
android:gravity="center" />
<Button
android:id="@+id/agreeButton"
android:layout_width="0dp"
android:layout_height="56dp"
android:layout_weight="1"
android:layout_marginStart="12dp"
android:text="@string/privacy_agreement_agree"
android:textColor="@color/black"
android:backgroundTint="@color/primary"
android:textSize="13sp"
android:maxLines="2"
android:gravity="center" />
</LinearLayout>
</LinearLayout>
+1 -1
View File
@@ -19,6 +19,6 @@
android:layout_marginTop="16dp"
android:text="@string/error_empty"
android:textColor="@color/white"
android:textSize="16sp" />
android:textSize="14sp" />
</LinearLayout>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottieAnimation"
android:layout_width="180dp"
android:layout_height="180dp"
app:lottie_fileName="lottie_empty_1.json"
app:lottie_loop="true"
app:lottie_autoPlay="true" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/error_keep_empty"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottieAnimation"
android:layout_width="180dp"
android:layout_height="180dp"
app:lottie_fileName="lottie_empty_1.json"
app:lottie_loop="true"
app:lottie_autoPlay="true" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/error_empty"
android:textColor="@color/white"
android:textSize="14sp"
android:alpha="0.8" />
</LinearLayout>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottieAnimation"
android:layout_width="180dp"
android:layout_height="180dp"
app:lottie_fileName="lottie_empty_1.json"
app:lottie_loop="true"
app:lottie_autoPlay="true" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/error_search_empty"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
+1 -1
View File
@@ -125,7 +125,7 @@
<string name="dialog_edit">修改</string>
<string name="dialog_positive">确定</string>
<string name="dialog_negative">取消</string>
<string name="dialog_paste">从剪贴板粘贴</string>
<string name="dialog_paste">点我粘贴</string>
<string name="dialog_config_hint">请输入接口…</string>
<string name="dialog_config_name">请输入名称…</string>
<string name="dialog_config_url">请输入地址…</string>
+1 -1
View File
@@ -141,7 +141,7 @@
<string name="error_play_flag">暫無線路資料</string>
<string name="error_play_timeout">連線逾時</string>
<string name="error_detail">暫無播放資料</string>
<string name="error_empty">找不到資料</string>
<string name="error_empty">這裡撒子內容都沒得~</string>
<string name="error_cast_file">不支持的檔案格式</string>
<string name="error_device_limit">設備授權數已達上限</string>
<string name="error_live_empty">該訂閱無直播內容</string>
+12 -3
View File
@@ -91,7 +91,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.3</string>
<string name="app_version">v3.0.6</string>
<string name="about_github">View on GitHub</string>
<!-- Backup & Restore -->
@@ -126,7 +126,7 @@
<string name="dialog_edit">Edit</string>
<string name="dialog_positive">OK</string>
<string name="dialog_negative">Cancel</string>
<string name="dialog_paste">Paste from clipboard</string>
<string name="dialog_paste">Click to paste</string>
<string name="dialog_config_hint">Please enter the config…</string>
<string name="dialog_config_name">Please enter the name…</string>
<string name="dialog_config_url">Please enter the url…</string>
@@ -151,7 +151,9 @@
<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">Not found</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>
<string name="error_play_flag">No flag data</string>
<string name="error_play_code">Error code: <xliff:g name="name">%s</xliff:g></string>
@@ -237,6 +239,13 @@
<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="privacy_agreement_title">XMBOX软件许可协议</string>
<string name="privacy_agreement_tip">请仔细阅读以下协议条款</string>
<string name="privacy_agreement_agree">我已阅读并同意</string>
<string name="privacy_agreement_disagree">不同意并退出</string>
<string name="privacy_agreement_content">XMBOX软件许可协议:\n\n- 以下是对[GPL-3.0](LICENSE.md)开源协议的补充,如有冲突,以以下协议为准。\n- 词语约定: 本协议中的"本软件"指"XMBOX软件""用户"指签署本协议的使用者,"版权数据"指包括但不限于视频、图像、音频、名字等在内的他人拥有所属版权的数据。\n\n1. 本软件仅为技术性多媒体播放器外壳("空壳播放器"),核心功能限于基础媒体文件解析与播放。\n\n2. 本软件自身不包含、不预装、不内置、不集成、不主动推荐、不直接或间接提供任何音视频、直播、图文等媒体资源内容。软件播放的任何资源均非由本软件或其开发者提供。\n\n3. 用户通过本软件播放的任何内容均完全来源于用户自行配置、输入、添加、获取或选择的第三方来源(如网络地址、本地文件、用户安装的插件/扩展/配置源等)。本软件仅作为访问用户自行指定内容的技术工具。\n\n4. 本软件无法控制、筛选、审查或保证用户访问的任何第三方内容的合法性、版权状态、准确性、安全性或适宜性。用户对其播放的内容负全部责任。\n\n5. 关于用户责任与风险承担:\n • 用户必须确保其通过本软件配置、访问或播放的所有内容均已获相关权利人合法授权,或属于法律允许的自由使用范畴。\n • 用户理解并同意,使用本软件访问第三方资源可能涉及侵犯版权、传播非法信息、隐私泄露、网络安全等风险。因用户使用本软件访问、播放或传播内容产生的一切法律责任、纠纷、损失及后果(包括法律诉讼、行政处罚、民事赔偿等),均由用户自行承担,与本软件及其开发者无涉。\n • 开发者不认可、不支持任何利用本软件规避技术保护措施(如DRM)的行为,此类行为导致的侵权责任由用户全权承担。\n\n6. 用户承诺并保证不利用本软件从事任何侵犯他人知识产权或其他合法权益的活动,或进行任何违反法律法规的行为。严禁使用本软件播放、传播盗版、色情、暴力、赌博、诈骗、危害国家安全、危害社会稳定等非法或侵权内容。\n\n7. 在任何情况下,本软件开发者均不就因用户使用或无法使用本软件、用户配置或访问的第三方资源、用户违反本协议或法律法规的行为导致的任何直接、间接、偶然、特殊、惩罚性或结果性损害(包括利润损失、数据丢失、业务中断、声誉损害等)承担任何责任(无论基于合同、侵权、严格责任或其他法律理论)。\n\n8. 本软件运行可能依赖第三方库、服务或技术。开发者不对这些第三方组件的可用性、准确性、功能或合法性负责。\n\n9. 用户理解并同意,使用本软件(包括下载、安装、运行)存在固有技术风险(如软件缺陷、兼容性问题、系统不稳定等),用户应自行承担此风险。\n\n10. 本软件仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。\n\n11. 本软件内使用的部分包括但不限于字体、图片等资源来源于互联网。如果出现侵权可联系开发者移除。\n\n12. 使用本软件的过程中可能会产生版权数据。对于这些版权数据,本软件不拥有它们的所有权。为了避免侵权,用户务必在 24 小时内 清除使用本项目的过程中所产生的版权数据。\n\n13. 本协议受中华人民共和国法律管辖并据其解释。若用户所在地法律强制规定特定责任条款,应以当地法律要求为准,但其他条款仍保持有效。任何由本协议或使用本软件引起的争议,应首先通过友好协商解决。\n\n14. 若你使用了本软件,即代表你接受本协议。\n\n15. 本协议更新后,继续使用视为接受新协议。</string>
<string name="source_hint_setting">Add Source</string>
<string name="source_hint_live">Add Live Source</string>
<string name="source_hint_wall">Add Wallpaper Source</string>
+7
View File
@@ -10,6 +10,13 @@
<application>
<activity
android:name=".ui.activity.PrivacyAgreementActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode|orientation"
android:exported="false"
android:screenOrientation="fullUser"
android:windowSoftInputMode="adjustPan" />
<activity
android:name=".ui.activity.HomeActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode|orientation"
@@ -14,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;
@@ -27,25 +26,21 @@ 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() {
@@ -53,12 +48,14 @@ public class Updater implements Download.Callback {
}
public Updater() {
this.download = Download.create("", getFile(), this);
this.download = Download.create(getApk(), getFile(), this);
this.forceCheck = false;
}
public Updater force() {
Notify.show(R.string.update_check);
Setting.putUpdate(true);
this.forceCheck = true; // 标记为手动检查
return this;
}
@@ -81,73 +78,48 @@ public class Updater implements Download.Callback {
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());
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 (response.contains("rate limit exceeded")) {
if (forceCheck) {
App.post(() -> Notify.show("检查更新失败:API请求过于频繁,请稍后重试"));
}
return;
}
if (downloadUrl != null && need(tagName)) {
download = Download.create(downloadUrl, getFile(), this);
App.post(() -> show(activity, tagName, body));
if (response.contains("Not Found Project") || response.contains("Not Found")) {
if (forceCheck) {
App.post(() -> Notify.show("检查更新失败:更新服务暂时不可用"));
}
return;
}
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 {
Logger.d("No update needed or APK not found");
// 只在手动检查时提示已是最新版
if (forceCheck) {
App.post(() -> Notify.show("已是最新版本 " + name));
}
Logger.d("Already latest version: " + name);
}
} catch (Exception e) {
Logger.e("Update check failed", e);
e.printStackTrace();
// 只在手动检查时提示网络错误
if (forceCheck) {
App.post(() -> Notify.show("检查更新失败:网络连接异常"));
}
}
}
@@ -188,7 +160,6 @@ public class Updater implements Download.Callback {
@Override
public void error(String msg) {
Logger.e("Download error: " + msg);
Notify.show(msg);
dismiss();
}
@@ -44,6 +44,7 @@ import com.fongmi.android.tv.utils.Util;
import com.github.catvod.net.OkHttp;
import com.google.android.flexbox.FlexDirection;
import com.google.android.flexbox.FlexboxLayoutManager;
import com.airbnb.lottie.LottieAnimationView;
import java.io.IOException;
import java.net.URLEncoder;
@@ -151,12 +152,14 @@ public class CollectActivity extends BaseActivity implements CustomScroller.Call
if (mCollectAdapter.getPosition() == 0) mSearchAdapter.addAll(result.getList());
mCollectAdapter.add(Collect.create(result.getList()));
mCollectAdapter.add(result.getList());
updateEmptyState();
});
mViewModel.result.observe(this, result -> {
boolean same = !result.getList().isEmpty() && mCollectAdapter.getActivated().getSite().equals(result.getList().get(0).getSite());
if (same) mCollectAdapter.getActivated().getList().addAll(result.getList());
if (same) mSearchAdapter.addAll(result.getList());
mScroller.endLoading(result);
updateEmptyState();
});
}
@@ -187,6 +190,7 @@ public class CollectActivity extends BaseActivity implements CustomScroller.Call
mBinding.agent.setVisibility(View.GONE);
mBinding.view.setVisibility(View.VISIBLE);
mBinding.result.setVisibility(View.VISIBLE);
updateEmptyState(); // 搜索开始时显示空状态
if (mExecutor != null) mExecutor.shutdownNow();
mExecutor = new PauseExecutor(20);
String keyword = mBinding.keyword.getText().toString().trim();
@@ -194,6 +198,27 @@ public class CollectActivity extends BaseActivity implements CustomScroller.Call
App.post(() -> mRecordAdapter.add(keyword), 250);
}
private void updateEmptyState() {
// 只有在结果页面可见且搜索结果为空时才显示空状态动画
boolean isResultVisible = isVisible(mBinding.result);
boolean isEmpty = mSearchAdapter.getItemCount() == 0;
boolean shouldShowEmpty = isResultVisible && isEmpty;
mBinding.emptyLayout.getRoot().setVisibility(shouldShowEmpty ? View.VISIBLE : View.GONE);
// 控制Lottie动画播放
if (shouldShowEmpty) {
try {
LottieAnimationView lottieView = mBinding.emptyLayout.getRoot().findViewById(R.id.lottieAnimation);
if (lottieView != null) {
lottieView.playAnimation();
}
} catch (Exception e) {
// 忽略错误
}
}
}
private void search(Site site, String keyword) {
try {
mViewModel.searchContent(site, keyword, false);
@@ -235,6 +260,7 @@ public class CollectActivity extends BaseActivity implements CustomScroller.Call
mBinding.result.setVisibility(View.GONE);
mBinding.site.setVisibility(View.VISIBLE);
mBinding.agent.setVisibility(View.VISIBLE);
mBinding.emptyLayout.getRoot().setVisibility(View.GONE); // 隐藏空状态动画
if (mExecutor != null) mExecutor.shutdownNow();
}
@@ -17,6 +17,7 @@ import com.fongmi.android.tv.ui.adapter.HistoryAdapter;
import com.fongmi.android.tv.ui.base.BaseActivity;
import com.fongmi.android.tv.ui.dialog.SyncDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.airbnb.lottie.LottieAnimationView;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -59,6 +60,25 @@ public class HistoryActivity extends BaseActivity implements HistoryAdapter.OnCl
private void getHistory() {
mAdapter.addAll(History.get());
mBinding.delete.setVisibility(mAdapter.getItemCount() > 0 ? View.VISIBLE : View.GONE);
updateEmptyState();
}
private void updateEmptyState() {
boolean isEmpty = mAdapter.getItemCount() == 0;
mBinding.emptyLayout.getRoot().setVisibility(isEmpty ? View.VISIBLE : View.GONE);
mBinding.recycler.setVisibility(isEmpty ? View.GONE : View.VISIBLE);
// 控制Lottie动画播放
if (isEmpty) {
try {
LottieAnimationView lottieView = mBinding.emptyLayout.getRoot().findViewById(R.id.lottieAnimation);
if (lottieView != null) {
lottieView.playAnimation();
}
} catch (Exception e) {
// 忽略错误
}
}
}
private void onSync(View view) {
@@ -67,7 +87,10 @@ public class HistoryActivity extends BaseActivity implements HistoryAdapter.OnCl
private void onDelete(View view) {
if (mAdapter.isDelete()) {
new MaterialAlertDialogBuilder(this).setTitle(R.string.dialog_delete_record).setMessage(R.string.dialog_delete_history).setNegativeButton(R.string.dialog_negative, null).setPositiveButton(R.string.dialog_positive, (dialog, which) -> mAdapter.clear()).show();
new MaterialAlertDialogBuilder(this).setTitle(R.string.dialog_delete_record).setMessage(R.string.dialog_delete_history).setNegativeButton(R.string.dialog_negative, null).setPositiveButton(R.string.dialog_positive, (dialog, which) -> {
mAdapter.clear();
updateEmptyState();
}).show();
} else if (mAdapter.getItemCount() > 0) {
mAdapter.setDelete(true);
} else {
@@ -91,6 +114,7 @@ public class HistoryActivity extends BaseActivity implements HistoryAdapter.OnCl
if (mAdapter.getItemCount() > 0) return;
mBinding.delete.setVisibility(View.GONE);
mAdapter.setDelete(false);
updateEmptyState();
}
@Override
@@ -16,6 +16,7 @@ import androidx.viewbinding.ViewBinding;
import com.fongmi.android.tv.App;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.Setting;
import com.fongmi.android.tv.Updater;
import com.fongmi.android.tv.api.config.LiveConfig;
import com.fongmi.android.tv.api.config.VodConfig;
@@ -62,6 +63,18 @@ public class HomeActivity extends BaseActivity implements NavigationBarView.OnIt
@Override
protected void initView(Bundle savedInstanceState) {
// 检查隐私协议
if (!Setting.isPrivacyAgreed()) {
Intent intent = new Intent(this, PrivacyAgreementActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
return;
}
// 确保通知渠道已创建用户已同意协议的情况
com.fongmi.android.tv.utils.Notify.createChannel();
orientation = getResources().getConfiguration().orientation;
Updater.create().release().start(this);
initFragment(savedInstanceState);
@@ -21,6 +21,7 @@ import com.fongmi.android.tv.ui.base.BaseActivity;
import com.fongmi.android.tv.ui.dialog.SyncDialog;
import com.fongmi.android.tv.utils.Notify;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.airbnb.lottie.LottieAnimationView;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -63,6 +64,25 @@ public class KeepActivity extends BaseActivity implements KeepAdapter.OnClickLis
private void getKeep() {
mAdapter.addAll(Keep.getVod());
mBinding.delete.setVisibility(mAdapter.getItemCount() > 0 ? View.VISIBLE : View.GONE);
updateEmptyState();
}
private void updateEmptyState() {
boolean isEmpty = mAdapter.getItemCount() == 0;
mBinding.emptyLayout.getRoot().setVisibility(isEmpty ? View.VISIBLE : View.GONE);
mBinding.recycler.setVisibility(isEmpty ? View.GONE : View.VISIBLE);
// 控制Lottie动画播放
if (isEmpty) {
try {
LottieAnimationView lottieView = mBinding.emptyLayout.getRoot().findViewById(R.id.lottieAnimation);
if (lottieView != null) {
lottieView.playAnimation();
}
} catch (Exception e) {
// 忽略错误
}
}
}
private void onSync(View view) {
@@ -71,7 +91,10 @@ public class KeepActivity extends BaseActivity implements KeepAdapter.OnClickLis
private void onDelete(View view) {
if (mAdapter.isDelete()) {
new MaterialAlertDialogBuilder(this).setTitle(R.string.dialog_delete_record).setMessage(R.string.dialog_delete_keep).setNegativeButton(R.string.dialog_negative, null).setPositiveButton(R.string.dialog_positive, (dialog, which) -> mAdapter.clear()).show();
new MaterialAlertDialogBuilder(this).setTitle(R.string.dialog_delete_record).setMessage(R.string.dialog_delete_keep).setNegativeButton(R.string.dialog_negative, null).setPositiveButton(R.string.dialog_positive, (dialog, which) -> {
mAdapter.clear();
updateEmptyState();
}).show();
} else if (mAdapter.getItemCount() > 0) {
mAdapter.setDelete(true);
} else {
@@ -114,6 +137,7 @@ public class KeepActivity extends BaseActivity implements KeepAdapter.OnClickLis
if (mAdapter.getItemCount() > 0) return;
mBinding.delete.setVisibility(View.GONE);
mAdapter.setDelete(false);
updateEmptyState();
}
@Override
@@ -0,0 +1,92 @@
package com.fongmi.android.tv.ui.activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import androidx.viewbinding.ViewBinding;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.Setting;
import com.fongmi.android.tv.databinding.ActivityPrivacyAgreementBinding;
import com.fongmi.android.tv.ui.base.BaseActivity;
public class PrivacyAgreementActivity extends BaseActivity {
private ActivityPrivacyAgreementBinding mBinding;
@Override
protected ViewBinding getBinding() {
return mBinding = ActivityPrivacyAgreementBinding.inflate(getLayoutInflater());
}
@Override
protected void initView(Bundle savedInstanceState) {
// 隐私协议页面初始化完成
}
@Override
protected void initEvent() {
if (mBinding != null) {
if (mBinding.agreeButton != null) {
mBinding.agreeButton.setOnClickListener(this::onAgree);
}
if (mBinding.disagreeButton != null) {
mBinding.disagreeButton.setOnClickListener(this::onDisagree);
}
}
}
private void onAgree(View view) {
// 用户同意协议
Setting.setPrivacyAgreed(true);
// 创建通知渠道此时才请求通知权限
com.fongmi.android.tv.utils.Notify.createChannel();
// 跳转到主界面清除任务栈避免用户通过任务管理器回到协议页面
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}
private void onDisagree(View view) {
// 用户不同意协议退出应用
try {
// 清除隐私协议状态可选确保下次启动重新询问
Setting.setPrivacyAgreed(false);
// 优雅地退出应用
finishAffinity();
// 延迟退出 Activity 完成销毁
new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(() -> {
System.exit(0);
}, 100);
} catch (Exception e) {
e.printStackTrace();
// 备选退出方案
System.exit(0);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 禁用返回键用户必须做出选择
if (keyCode == KeyEvent.KEYCODE_BACK) {
onDisagree(null);
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onDestroy() {
// 清理 binding 引用
mBinding = null;
super.onDestroy();
}
}
@@ -59,28 +59,6 @@ public class SettingPlayerActivity extends BaseActivity implements UaCallback, B
mBinding.renderText.setText((render = ResUtil.getStringArray(R.array.select_render))[Setting.getRender()]);
mBinding.captionText.setText((caption = ResUtil.getStringArray(R.array.select_caption))[Setting.isCaption() ? 1 : 0]);
mBinding.backgroundText.setText((background = ResUtil.getStringArray(R.array.select_background))[Setting.getBackground()]);
// 设置开关的颜色为黄色
int accentColor = getResources().getColor(R.color.accent);
android.content.res.ColorStateList colorStateList = new android.content.res.ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_checked},
new int[]{android.R.attr.state_checked}
},
new int[]{
0x66FFFFFF, // 未选中时的颜色
accentColor // 选中时的颜色
}
);
mBinding.tunnelSwitch.setThumbTintList(android.content.res.ColorStateList.valueOf(android.graphics.Color.WHITE));
mBinding.tunnelSwitch.setTrackTintList(colorStateList);
mBinding.audioDecodeSwitch.setThumbTintList(android.content.res.ColorStateList.valueOf(android.graphics.Color.WHITE));
mBinding.audioDecodeSwitch.setTrackTintList(colorStateList);
mBinding.aacSwitch.setThumbTintList(android.content.res.ColorStateList.valueOf(android.graphics.Color.WHITE));
mBinding.aacSwitch.setTrackTintList(colorStateList);
mBinding.danmakuLoadSwitch.setThumbTintList(android.content.res.ColorStateList.valueOf(android.graphics.Color.WHITE));
mBinding.danmakuLoadSwitch.setTrackTintList(colorStateList);
}
@Override
@@ -30,6 +30,7 @@ import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.concurrent.TimeUnit;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -162,6 +163,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
private Runnable mTimeUpdateRunnable;
private BroadcastReceiver mBatteryReceiver;
private int mBatteryLevel = -1;
private boolean mIsCharging = false;
public static void push(FragmentActivity activity, String text) {
if (FileChooser.isValid(activity, Uri.parse(text))) file(activity, FileChooser.getPathFromUri(activity, Uri.parse(text)));
@@ -327,8 +329,12 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
if (level != -1 && scale != -1) {
mBatteryLevel = (int) ((level / (float) scale) * 100);
mIsCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL);
updateTimeBattery();
}
}
@@ -345,18 +351,41 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
}
private void updateTimeBattery() {
TextView timeBattery = mBinding.getRoot().findViewById(R.id.time_battery);
if (timeBattery == null) return;
TextView timeBattery = findViewById(R.id.time_battery);
TextView batteryText = findViewById(R.id.battery_icon);
android.widget.ImageView chargingIndicator = findViewById(R.id.charging_indicator);
// 只在屏模式下显示
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
String time = DateFormat.getTimeFormat(this).format(System.currentTimeMillis());
String battery = mBatteryLevel >= 0 ? mBatteryLevel + "%" : "";
String text = time + (battery.isEmpty() ? "" : " | " + battery);
timeBattery.setText(text);
timeBattery.setVisibility(View.VISIBLE);
// 只在屏模式下显示
if (isFullscreen()) {
// 更新时间
if (timeBattery != null) {
String time = DateFormat.getTimeFormat(this).format(System.currentTimeMillis());
timeBattery.setText(time);
timeBattery.setVisibility(View.VISIBLE);
}
// 更新充电图标
if (chargingIndicator != null) {
chargingIndicator.setVisibility(mIsCharging && mBatteryLevel >= 0 ? View.VISIBLE : View.GONE);
}
// 更新电池百分比文字
if (batteryText != null && mBatteryLevel >= 0) {
batteryText.setText(mBatteryLevel + "%");
batteryText.setVisibility(View.VISIBLE);
} else if (batteryText != null) {
batteryText.setVisibility(View.GONE);
}
} else {
timeBattery.setVisibility(View.GONE);
if (timeBattery != null) {
timeBattery.setVisibility(View.GONE);
}
if (batteryText != null) {
batteryText.setVisibility(View.GONE);
}
if (chargingIndicator != null) {
chargingIndicator.setVisibility(View.GONE);
}
}
}
@@ -1465,6 +1494,55 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
this.rotate = rotate;
if (fullscreen && rotate) noPadding(mBinding.control.getRoot());
if (fullscreen && !rotate) setPadding(mBinding.control.getRoot());
// 检测屏幕方向变化并处理
onOrientationChanged();
}
// 添加屏幕方向变化处理方法
private void onOrientationChanged() {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 切换到横屏模式
onLandscapeMode();
} else {
// 切换到竖屏模式
onPortraitMode();
}
}
private void onLandscapeMode() {
// 横屏模式下的特殊处理
// 调整进度条的敏感度
if (mPlayers != null) {
long duration = mPlayers.getDuration();
if (duration > TimeUnit.MINUTES.toMillis(30)) {
mBinding.control.seek.setKeyTimeIncrement(TimeUnit.MINUTES.toMillis(1));
} else if (duration > TimeUnit.MINUTES.toMillis(10)) {
mBinding.control.seek.setKeyTimeIncrement(TimeUnit.SECONDS.toMillis(30));
} else if (duration > 0) {
mBinding.control.seek.setKeyTimeIncrement(TimeUnit.SECONDS.toMillis(15));
}
}
// 确保进度条状态正确
if (mPlayers != null) {
long position = mPlayers.getPosition();
long duration = mPlayers.getDuration();
if (position > 0 && duration > 0) {
mBinding.control.seek.setPosition(position);
mBinding.control.seek.setDuration(duration);
}
}
}
private void onPortraitMode() {
// 竖屏模式下的处理
// 恢复进度条的默认敏感度
if (mPlayers != null) {
long duration = mPlayers.getDuration();
if (duration > 0) {
mBinding.control.seek.setKeyTimeIncrement(duration);
}
}
}
public boolean isStop() {
@@ -1566,10 +1644,40 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
@Override
public void onSeekEnd(long time) {
mBinding.widget.seek.setVisibility(View.GONE);
mPlayers.seek(time);
showProgress();
onPlay();
handleLandscapeSeek(time);
}
// 添加新的方法处理横屏模式下的特殊逻辑
private void handleLandscapeSeek(long time) {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 横屏模式下的特殊处理
mBinding.widget.seek.setVisibility(View.GONE);
mPlayers.pause();
mPlayers.seek(time);
showProgress();
App.post(() -> {
long actualPosition = mPlayers.getPosition();
if (Math.abs(actualPosition - time) > 500) {
mPlayers.seek(time);
}
onPlay();
hideProgress();
}, 150); // 横屏模式下延迟更长确保跳转完成
} else {
// 竖屏模式使用原有逻辑
mBinding.widget.seek.setVisibility(View.GONE);
mPlayers.pause();
mPlayers.seek(time);
showProgress();
App.post(() -> {
long actualPosition = mPlayers.getPosition();
if (Math.abs(actualPosition - time) > 500) {
mPlayers.seek(time);
}
onPlay();
hideProgress();
}, 100); // 竖屏模式下延迟较短
}
}
@Override
@@ -8,6 +8,7 @@ import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.WindowManager;
import android.content.res.Configuration;
import androidx.annotation.NonNull;
@@ -109,6 +110,17 @@ public class CustomKeyDownVod extends GestureDetector.SimpleOnGestureListener im
if (isEdge(e1) || changeScale || lock || e1.getPointerCount() > 1) return true;
float deltaX = e2.getX() - e1.getX();
float deltaY = e1.getY() - e2.getY();
// 在横屏模式下调整触摸事件的处理逻辑
if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 横屏模式下增加对水平滑动的敏感度
if (Math.abs(deltaX) > Math.abs(deltaY) * 0.5f) {
if (touch) checkFunc(distanceX, distanceY, e2);
if (changeTime) listener.onSeek(time = (long) (deltaX * 50));
return true;
}
}
if (touch) checkFunc(distanceX, distanceY, e2);
if (changeTime) listener.onSeek(time = (long) (deltaX * 50));
if (changeBright) setBright(deltaY);
@@ -145,9 +157,32 @@ public class CustomKeyDownVod extends GestureDetector.SimpleOnGestureListener im
private void checkFunc(float distanceX, float distanceY, MotionEvent e2) {
int four = ResUtil.getScreenWidth(activity) / 4;
if (e2.getX() > four && e2.getX() < four * 3) center = true;
else if (Math.abs(distanceX) < Math.abs(distanceY)) checkSide(e2);
if (Math.abs(distanceX) >= Math.abs(distanceY)) changeTime = true;
// 在横屏模式下调整中心区域的判断
if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 横屏模式下扩大中心区域更容易触发进度条调整
int centerStart = ResUtil.getScreenWidth(activity) / 3;
int centerEnd = ResUtil.getScreenWidth(activity) * 2 / 3;
if (e2.getX() > centerStart && e2.getX() < centerEnd) {
center = true;
} else if (Math.abs(distanceX) < Math.abs(distanceY)) {
checkSide(e2);
}
// 横屏模式下降低触发进度条调整的阈值
if (Math.abs(distanceX) >= Math.abs(distanceY) * 0.7f) {
changeTime = true;
}
} else {
// 竖屏模式保持原有逻辑
if (e2.getX() > four && e2.getX() < four * 3) {
center = true;
} else if (Math.abs(distanceX) < Math.abs(distanceY)) {
checkSide(e2);
}
if (Math.abs(distanceX) >= Math.abs(distanceY)) {
changeTime = true;
}
}
touch = false;
}
@@ -0,0 +1,137 @@
package com.fongmi.android.tv.ui.custom;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.appcompat.widget.AppCompatCheckBox;
public class CustomSwitch extends AppCompatCheckBox {
private Paint trackPaint;
private Paint thumbPaint;
private RectF trackRect;
private RectF thumbRect;
private float thumbPosition = 0f; // 0 = 左边, 1 = 右边
private int currentTrackColor;
private int currentThumbColor;
private static final int TRACK_COLOR_OFF = 0xFF555555; // 灰色
private static final int TRACK_COLOR_ON = 0xFFFFEB3B; // 黄色
private static final int THUMB_COLOR_OFF = 0xFFFFFFFF; // 白色
private static final int THUMB_COLOR_ON = 0xFF000000; // 黑色
private ValueAnimator animator;
public CustomSwitch(Context context) {
super(context);
init();
}
public CustomSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 隐藏默认的checkbox样式
setButtonDrawable(null);
trackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
thumbPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
trackRect = new RectF();
thumbRect = new RectF();
currentTrackColor = TRACK_COLOR_OFF;
currentThumbColor = THUMB_COLOR_OFF;
// 监听状态变化
setOnCheckedChangeListener((buttonView, isChecked) -> animateSwitch(isChecked));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 固定尺寸50dp × 30dp
int width = (int) (50 * getResources().getDisplayMetrics().density);
int height = (int) (30 * getResources().getDisplayMetrics().density);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
int width = getWidth();
int height = getHeight();
float radius = height / 2f;
// 绘制轨道
trackRect.set(0, 0, width, height);
trackPaint.setColor(currentTrackColor);
canvas.drawRoundRect(trackRect, radius, radius, trackPaint);
// 计算小圆位置
float thumbSize = height - 8 * getResources().getDisplayMetrics().density; // 22dp
float padding = 4 * getResources().getDisplayMetrics().density;
float thumbLeft = padding + thumbPosition * (width - thumbSize - 2 * padding);
float thumbTop = padding;
// 绘制小圆
thumbRect.set(thumbLeft, thumbTop, thumbLeft + thumbSize, thumbTop + thumbSize);
thumbPaint.setColor(currentThumbColor);
canvas.drawOval(thumbRect, thumbPaint);
}
private void animateSwitch(boolean isChecked) {
if (animator != null && animator.isRunning()) {
animator.cancel();
}
float targetPosition = isChecked ? 1f : 0f;
int targetTrackColor = isChecked ? TRACK_COLOR_ON : TRACK_COLOR_OFF;
int targetThumbColor = isChecked ? THUMB_COLOR_ON : THUMB_COLOR_OFF;
animator = ValueAnimator.ofFloat(thumbPosition, targetPosition);
animator.setDuration(250); // 250ms动画时长
final ArgbEvaluator colorEvaluator = new ArgbEvaluator();
animator.addUpdateListener(animation -> {
thumbPosition = (float) animation.getAnimatedValue();
// 颜色渐变
currentTrackColor = (int) colorEvaluator.evaluate(
thumbPosition, TRACK_COLOR_OFF, TRACK_COLOR_ON
);
currentThumbColor = (int) colorEvaluator.evaluate(
thumbPosition, THUMB_COLOR_OFF, THUMB_COLOR_ON
);
invalidate();
});
animator.start();
}
@Override
public void setChecked(boolean checked) {
super.setChecked(checked);
// 初始化时不播放动画
if (!isAttachedToWindow()) {
thumbPosition = checked ? 1f : 0f;
currentTrackColor = checked ? TRACK_COLOR_ON : TRACK_COLOR_OFF;
currentThumbColor = checked ? THUMB_COLOR_ON : THUMB_COLOR_OFF;
}
}
}
@@ -39,7 +39,7 @@ public class AboutDialog extends BaseDialog {
private void openGitHub() {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://github.com/Tosencen/XMBOX/releases/tag/v3.0.3"));
intent.setData(Uri.parse("https://github.com/Tosencen/XMBOX/releases/latest"));
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
@@ -21,6 +21,7 @@ import com.fongmi.android.tv.api.config.WallConfig;
import com.fongmi.android.tv.bean.Config;
import com.fongmi.android.tv.databinding.DialogConfigBinding;
import com.fongmi.android.tv.impl.ConfigCallback;
import com.fongmi.android.tv.impl.Callback;
import com.fongmi.android.tv.ui.custom.CustomTextListener;
import com.fongmi.android.tv.utils.FileChooser;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@@ -156,7 +157,63 @@ public class ConfigDialog {
}
// 只有URL不为空时才设置配置
// 保存原始URL以便在添加失败时恢复
String originalUrl = ori;
callback.setConfig(Config.find(url, type));
// 添加一个延迟检查如果配置没有成功加载则恢复原始URL
new android.os.Handler().postDelayed(() -> {
// 检查配置是否成功加载
Config currentConfig = getConfig();
if (currentConfig == null || !currentConfig.getUrl().equals(url)) {
// 配置加载失败恢复原始URL
if (!TextUtils.isEmpty(originalUrl)) {
// 如果有原始URL恢复原始URL
callback.setConfig(Config.find(originalUrl, type));
} else {
// 如果没有原始URL设置为空
switch (type) {
case 0:
VodConfig.get().clear().config(Config.vod()).load(new Callback() {
@Override
public void success() {}
@Override
public void success(String result) {}
@Override
public void error(String msg) {}
});
break;
case 1:
LiveConfig.get().clear().config(Config.live()).load(new Callback() {
@Override
public void success() {}
@Override
public void success(String result) {}
@Override
public void error(String msg) {}
});
break;
case 2:
WallConfig.get().clear().config(Config.wall()).load(new Callback() {
@Override
public void success() {}
@Override
public void success(String result) {}
@Override
public void error(String msg) {}
});
break;
}
}
}
}, 2000); // 2秒后检查
dialog.dismiss();
}
@@ -104,21 +104,6 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
setSourceHintText(mBinding.wallUrl, WallConfig.getDesc(), R.string.source_hint_wall);
mBinding.versionText.setText(getString(R.string.setting_version) + " " + BuildConfig.VERSION_NAME);
// 设置开关的颜色为黄色
int accentColor = getResources().getColor(R.color.accent);
android.content.res.ColorStateList colorStateList = new android.content.res.ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_checked},
new int[]{android.R.attr.state_checked}
},
new int[]{
0x66FFFFFF, // 未选中时的颜色
accentColor // 选中时的颜色
}
);
mBinding.incognitoSwitch.setThumbTintList(android.content.res.ColorStateList.valueOf(android.graphics.Color.WHITE));
mBinding.incognitoSwitch.setTrackTintList(colorStateList);
setOtherText();
setCacheText();
String[] quotes = getResources().getStringArray(R.array.motivational_quotes);
@@ -45,6 +45,7 @@ import com.fongmi.android.tv.ui.activity.CollectActivity;
import com.fongmi.android.tv.ui.activity.HistoryActivity;
import com.fongmi.android.tv.ui.activity.KeepActivity;
import com.fongmi.android.tv.ui.activity.VideoActivity;
import com.airbnb.lottie.LottieAnimationView;
import com.fongmi.android.tv.ui.adapter.TypeAdapter;
import com.fongmi.android.tv.ui.base.BaseFragment;
import com.fongmi.android.tv.ui.dialog.ConfigDialog;
@@ -265,6 +266,15 @@ public class VodFragment extends BaseFragment implements SiteCallback, FilterCal
}
// 空源状态下隐藏所有悬浮按钮
hideFabButtons();
// 启动Lottie动画
try {
LottieAnimationView lottieView = mBinding.emptySourceHint.findViewById(R.id.lottieAnimation);
if (lottieView != null) {
lottieView.playAnimation();
}
} catch (Exception e) {
// 忽略错误
}
}
}
}
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 开启状态:黄色底 + 黑色圆在右边 -->
<item android:state_checked="true">
<layer-list>
<!-- 黄色轨道 -->
<item>
<shape android:shape="rectangle">
<solid android:color="#FFEB3B" />
<corners android:radius="15dp" />
</shape>
</item>
<!-- 黑色小圆(右边,精确定位 22dp) -->
<item android:top="4dp" android:bottom="4dp" android:right="4dp" android:left="24dp">
<shape android:shape="oval">
<solid android:color="#000000" />
</shape>
</item>
</layer-list>
</item>
<!-- 关闭状态:灰色底 + 白色圆在左边 -->
<item>
<layer-list>
<!-- 灰色轨道 -->
<item>
<shape android:shape="rectangle">
<solid android:color="#555555" />
<corners android:radius="15dp" />
</shape>
</item>
<!-- 白色小圆(左边,精确定位 22dp) -->
<item android:top="4dp" android:bottom="4dp" android:left="4dp" android:right="24dp">
<shape android:shape="oval">
<solid android:color="#FFFFFF" />
</shape>
</item>
</layer-list>
</item>
</selector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,595Q553,595 604.5,543.5Q656,492 656,419Q656,346 604.5,294.5Q553,243 480,243Q407,243 355.5,294.5Q304,346 304,419Q304,492 355.5,543.5Q407,595 480,595ZM480,420ZM480,760Q587,760 693,724.5Q799,689 896,619Q902,615 905,608.5Q908,602 908,595Q908,588 905,582Q902,576 896,572Q758,474 658,447.5Q558,421 480,421Q402,421 302,447.5Q202,474 64,572Q58,576 55,582Q52,588 52,595Q52,602 55,608.5Q58,615 64,619Q161,689 267,724.5Q373,760 480,760Z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M312,720Q261,720 214.5,702Q168,684 131,649Q83,604 61.5,542.5Q40,481 40,415Q40,337 78,288.5Q116,240 189,240Q203,240 215.5,242.5Q228,245 241,250L480,339L719,250Q732,245 744.5,242.5Q757,240 771,240Q844,240 882,288.5Q920,337 920,415Q920,481 898.5,542.5Q877,604 829,649Q792,684 745.5,702Q699,720 648,720Q582,720 536,690Q490,660 490,660L470,660Q470,660 424,690Q378,720 312,720ZM312,640Q349,640 381,622.5Q413,605 440,580L520,580Q547,605 579,622.5Q611,640 648,640Q684,640 717.5,627.5Q751,615 777,589Q811,555 825.5,509Q840,463 840,415Q840,374 823,346.5Q806,319 769,320Q766,320 747,324L480,424L213,324Q208,322 202.5,321Q197,320 191,320Q154,320 137,347Q120,374 120,415Q120,464 134.5,510Q149,556 184,590Q210,615 243,627.5Q276,640 312,640ZM361,580Q398,580 419,563.5Q440,547 440,518Q440,469 375.5,424.5Q311,380 239,380Q202,380 181,396.5Q160,413 160,442Q160,491 224.5,535.5Q289,580 361,580ZM355,520Q317,520 272.5,495Q228,470 220,444Q225,442 231.5,440.5Q238,439 245,439Q283,439 327.5,464.5Q372,490 380,516Q375,518 368.5,519Q362,520 355,520ZM599,581Q671,581 735.5,536Q800,491 800,442Q800,413 779.5,396Q759,379 721,379Q649,379 584.5,424Q520,469 520,518Q520,547 541,564Q562,581 599,581ZM605,520Q598,520 592,519Q586,518 581,516Q589,490 633.5,465Q678,440 716,440Q723,440 729,441Q735,442 740,444Q732,470 687.5,495Q643,520 605,520ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,840Q363,840 281.5,758.5Q200,677 200,560Q200,483 225.5,405Q251,327 291.5,263.5Q332,200 382,160Q432,120 480,120Q529,120 578.5,160Q628,200 668.5,263.5Q709,327 734.5,405Q760,483 760,560Q760,677 678.5,758.5Q597,840 480,840ZM480,760Q563,760 621.5,701.5Q680,643 680,560Q680,503 660.5,440Q641,377 611.5,323.5Q582,270 547,235Q512,200 480,200Q449,200 413.5,235Q378,270 348.5,323.5Q319,377 299.5,440Q280,503 280,560Q280,643 338.5,701.5Q397,760 480,760ZM520,720Q537,720 548.5,708.5Q560,697 560,680Q560,663 548.5,651.5Q537,640 520,640Q470,640 435,605Q400,570 400,520Q400,503 388.5,491.5Q377,480 360,480Q343,480 331.5,491.5Q320,503 320,520Q320,603 378.5,661.5Q437,720 520,720ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M123,520Q122,510 121.5,500Q121,490 121,480Q121,405 149,339.5Q177,274 226,225.5Q275,177 340,148.5Q405,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,490 839.5,500Q839,510 838,520L757,520Q759,510 759.5,500Q760,490 760,480Q760,470 759.5,460Q759,450 757,440L639,440Q640,450 640,460Q640,470 640,480Q640,490 640,500Q640,510 639,520L560,520Q560,512 560,503.5Q560,495 560,487Q560,475 559.5,463Q559,451 558,440L403,440Q402,451 401.5,463Q401,475 401,487Q401,495 401,503.5Q401,512 401,520L322,520Q321,510 321,500Q321,490 321,480Q321,470 321,460Q321,450 322,440L204,440Q202,450 201.5,460Q201,470 201,480Q201,490 201.5,500Q202,510 204,520L123,520ZM228,360L331,360Q339,317 351,282.5Q363,248 377,220Q329,238 290,274.5Q251,311 228,360ZM414,360L546,360Q536,317 521,276Q506,235 480,200Q454,235 438.5,276Q423,317 414,360ZM630,360L733,360Q710,311 670.5,274.5Q631,238 583,220Q597,250 609.5,283.5Q622,317 630,360ZM440,840L440,800Q440,750 405,715Q370,680 320,680L80,680L80,600L320,600Q368,600 409.5,621Q451,642 480,680Q509,642 550.5,621Q592,600 640,600L880,600L880,680L640,680Q590,680 555,715Q520,750 520,800L520,840L440,840Z"/>
</vector>
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M390,880L322,760L190,760L100,600L168,480L100,360L190,200L322,200L390,80L570,80L638,200L770,200L860,360L792,480L860,600L770,760L638,760L570,880L390,880ZM638,440L724,440L768,360L724,280L638,280L593,360L638,440ZM438,560L522,560L567,480L522,400L438,400L393,480L438,560ZM438,320L522,320L568,239L523,160L437,160L392,239L438,320ZM237,440L322,440L367,360L322,280L237,280L192,360L237,440ZM237,680L322,680L367,600L322,520L236,520L192,600L237,680ZM437,800L523,800L568,721L522,640L438,640L392,721L437,800ZM638,680L723,680L768,600L723,520L638,520L593,600L638,680Z"/>
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S14.76,7 12,7L12,7zM17,15c0.75,-1.06 1.19,-2.36 1.19,-3.77c0,-3.61 -2.92,-6.54 -6.54,-6.54c-0.84,0 -1.65,0.16 -2.39,0.45L12,7.78L12,7.78c2.33,0 4.22,1.89 4.22,4.22c0,1.04 -0.38,1.99 -1,2.73V15zM2,4.27l2.28,2.28l0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5c1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22L21,20.73L3.27,3L2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65c0,1.66 1.34,3 3,3c0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53c-2.76,0 -5,-2.24 -5,-5C7,11.21 7.2,10.47 7.53,9.8z"/>
</vector>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF0000"
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,880Q346,880 253,787Q160,694 160,560L160,360Q160,238 256,159Q352,80 480,80Q608,80 704,159Q800,238 800,360L800,880L480,880ZM480,800L560,800Q541,775 530.5,744.5Q520,714 520,680L520,638Q510,639 500,639.5Q490,640 480,640Q413,640 350.5,616.5Q288,593 240,545L240,560Q240,660 310,730Q380,800 480,800ZM600,680Q600,730 635,765Q670,800 720,800L720,545Q694,571 664,589.5Q634,608 600,620L600,680ZM440,400Q440,334 395,289Q350,244 286,241Q264,265 252,295Q240,325 240,360Q240,449 312.5,504.5Q385,560 480,560Q575,560 647.5,504.5Q720,449 720,360Q720,325 708,294.5Q696,264 674,240Q610,242 565,288Q520,334 520,400L440,400ZM340,400Q323,400 311.5,388.5Q300,377 300,360Q300,343 311.5,331.5Q323,320 340,320Q357,320 368.5,331.5Q380,343 380,360Q380,377 368.5,388.5Q357,400 340,400ZM620,400Q603,400 591.5,388.5Q580,377 580,360Q580,343 591.5,331.5Q603,320 620,320Q637,320 648.5,331.5Q660,343 660,360Q660,377 648.5,388.5Q637,400 620,400ZM370,182Q404,196 432,219Q460,242 480,271Q500,242 527.5,219Q555,196 589,182Q564,171 536.5,165.5Q509,160 480,160Q451,160 423.5,165.5Q396,171 370,182ZM800,800L800,800L720,800Q670,800 635,800Q600,800 600,800L600,800Q580,800 560,800Q540,800 520,800L520,800Q520,800 578.5,800Q637,800 720,800L800,800ZM480,800Q380,800 310,730Q240,660 240,560L240,560Q240,660 310,730Q380,800 480,800Q540,800 550,800Q560,800 560,800L560,800Q560,800 560,800Q560,800 560,800L480,800ZM600,680L600,680Q600,730 635,765Q670,800 720,800L720,800Q670,800 635,765Q600,730 600,680ZM480,271Q480,271 480,271Q480,271 480,271Q480,271 480,271Q480,271 480,271Q480,271 480,271Q480,271 480,271Q480,271 480,271Q480,271 480,271Z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M508,760L732,760Q725,786 708,802Q691,818 664,822L228,875Q195,880 168.5,859.5Q142,839 138,806L85,369Q81,336 101,310Q121,284 154,280L200,274L200,354L164,359Q164,359 164,359Q164,359 164,359L218,796Q218,796 218,796Q218,796 218,796L508,760ZM360,680Q327,680 303.5,656.5Q280,633 280,600L280,160Q280,127 303.5,103.5Q327,80 360,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,600Q880,633 856.5,656.5Q833,680 800,680L360,680ZM360,600L800,600Q800,600 800,600Q800,600 800,600L800,160Q800,160 800,160Q800,160 800,160L360,160Q360,160 360,160Q360,160 360,160L360,600Q360,600 360,600Q360,600 360,600ZM580,380Q580,380 580,380Q580,380 580,380L580,380Q580,380 580,380Q580,380 580,380L580,380Q580,380 580,380Q580,380 580,380L580,380Q580,380 580,380Q580,380 580,380ZM218,796L218,796L218,796L218,796L218,796Q218,796 218,796Q218,796 218,796ZM581,560Q649,560 696.5,513Q744,466 749,400Q681,400 632.5,447Q584,494 581,560ZM581,560Q578,494 529.5,447Q481,400 413,400Q418,466 465.5,513Q513,560 581,560ZM581,440Q598,440 609.5,428.5Q621,417 621,400L621,390L631,394Q646,400 661.5,397Q677,394 685,380Q694,365 691,348Q688,331 671,324L661,320L671,316Q688,309 690.5,291.5Q693,274 685,260Q676,245 661,242.5Q646,240 631,246L621,250L621,240Q621,223 609.5,211.5Q598,200 581,200Q564,200 552.5,211.5Q541,223 541,240L541,250L531,246Q516,240 501,242.5Q486,245 477,260Q469,274 471.5,291.5Q474,309 491,316L501,320L491,324Q474,331 471,348Q468,365 477,380Q485,394 500.5,397Q516,400 531,394L541,390L541,400Q541,417 552.5,428.5Q564,440 581,440ZM581,360Q564,360 552.5,348.5Q541,337 541,320Q541,303 552.5,291.5Q564,280 581,280Q598,280 609.5,291.5Q621,303 621,320Q621,337 609.5,348.5Q598,360 581,360Z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M342,800L618,800Q618,800 618,800Q618,800 618,800L658,640L302,640L342,800Q342,800 342,800Q342,800 342,800ZM342,880Q314,880 293,863Q272,846 265,819L220,640L740,640L695,819Q688,846 667,863Q646,880 618,880L342,880ZM200,560L760,560Q760,560 760,560Q760,560 760,560L760,480L200,480L200,560Q200,560 200,560Q200,560 200,560ZM480,320Q480,220 550,150Q620,80 720,80Q720,170 663,236Q606,302 520,316L520,400L840,400L840,560Q840,593 816.5,616.5Q793,640 760,640L200,640Q167,640 143.5,616.5Q120,593 120,560L120,400L440,400L440,316Q354,302 297,236Q240,170 240,80Q340,80 410,150Q480,220 480,320Z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M280,680L280,280L680,280L680,680L280,680ZM360,600L600,600L600,360L360,360L360,600ZM200,760L200,840Q167,840 143.5,816.5Q120,793 120,760L200,760ZM120,680L120,600L200,600L200,680L120,680ZM120,520L120,440L200,440L200,520L120,520ZM120,360L120,280L200,280L200,360L120,360ZM200,200L120,200Q120,167 143.5,143.5Q167,120 200,120L200,200ZM280,840L280,760L360,760L360,840L280,840ZM280,200L280,120L360,120L360,200L280,200ZM440,840L440,760L520,760L520,840L440,840ZM440,200L440,120L520,120L520,200L440,200ZM600,840L600,760L680,760L680,840L600,840ZM600,200L600,120L680,120L680,200L600,200ZM760,840L760,760L840,760Q840,793 816.5,816.5Q793,840 760,840ZM760,680L760,600L840,600L840,680L760,680ZM760,520L760,440L840,440L840,520L760,520ZM760,360L760,280L840,280L840,360L760,360ZM760,200L760,120Q793,120 816.5,143.5Q840,167 840,200L760,200Z"/>
</vector>
@@ -56,7 +56,30 @@
android:paddingEnd="8dp"
android:textColor="@color/white"
android:textSize="16sp"
tools:text="21:30 | 85%" />
tools:text="21:30" />
<ImageView
android:id="@+id/charging_indicator"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:layout_marginEnd="4dp"
android:src="@drawable/ic_charging_bolt"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/battery_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingEnd="8dp"
android:textColor="@color/white"
android:textSize="14sp"
android:visibility="gone"
tools:text="85%"
tools:visibility="visible" />
<ImageView
android:id="@+id/cast"
@@ -154,5 +154,15 @@
app:spanCount="2"
tools:listitem="@layout/adapter_vod_rect" />
<!-- 搜索结果空状态Lottie动画 -->
<include
android:id="@+id/emptyLayout"
layout="@layout/view_empty_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="-40dp"
android:visibility="gone" />
</RelativeLayout>
</LinearLayout>
@@ -65,5 +65,15 @@
android:paddingEnd="8dp"
android:paddingBottom="8dp" />
<!-- 空状态Lottie动画 -->
<include
android:id="@+id/emptyLayout"
layout="@layout/view_empty_lottie"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="-40dp"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
@@ -65,5 +65,14 @@
android:paddingEnd="8dp"
android:paddingBottom="8dp" />
<include
android:id="@+id/emptyLayout"
layout="@layout/view_empty_keep"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="-40dp"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
@@ -222,11 +222,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/tunnelSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
@@ -249,11 +248,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/audioDecodeSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
@@ -276,11 +274,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/aacSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
@@ -303,11 +300,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/danmakuLoadSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
@@ -4,11 +4,14 @@
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_marginStart="6dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="6dp"
android:layout_marginBottom="8dp"
android:background="@drawable/shape_item"
android:gravity="center"
android:padding="8dp"
android:singleLine="true"
android:textColor="@color/text"
android:textSize="14sp"
android:textSize="12sp"
tools:text="泥巴" />
+21 -16
View File
@@ -78,7 +78,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text="(长按输入框可改源名)"
android:text="(单击视频输入框可加源)"
android:textColor="@color/white"
android:textSize="12sp"
android:alpha="0.5" />
@@ -129,7 +129,8 @@
android:background="@drawable/shape_item"
android:padding="16dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_setting_home" />
android:src="@drawable/potted_plant_24px"
android:tint="@color/white" />
<ImageView
android:id="@+id/vodHistory"
@@ -138,7 +139,8 @@
android:background="@drawable/shape_item"
android:padding="16dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_setting_history" />
android:src="@drawable/ic_m3_list_alt"
android:tint="@color/white" />
</LinearLayout>
@@ -188,7 +190,8 @@
android:background="@drawable/shape_item"
android:padding="16dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_setting_home" />
android:src="@drawable/potted_plant_24px"
android:tint="@color/white" />
<ImageView
android:id="@+id/liveHistory"
@@ -197,16 +200,19 @@
android:background="@drawable/shape_item"
android:padding="16dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_setting_history" />
android:src="@drawable/ic_m3_list_alt"
android:tint="@color/white" />
</LinearLayout>
<!-- 壁纸功能已隐藏 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center_vertical"
android:orientation="horizontal">
android:orientation="horizontal"
android:visibility="gone">
<LinearLayout
android:id="@+id/wall"
@@ -292,7 +298,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_setting_player"
android:src="@drawable/hive_24px"
android:tint="@color/white" />
<TextView
@@ -318,7 +324,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_setting_incognito"
android:src="@drawable/domino_mask_24px"
android:tint="@color/white" />
<TextView
@@ -329,11 +335,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/incognitoSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
@@ -350,7 +355,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_setting_size"
android:src="@drawable/select_all_24px"
android:tint="@color/white" />
<TextView
@@ -405,7 +410,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_setting_doh"
android:src="@drawable/globe_book_24px"
android:tint="@color/white" />
<TextView
@@ -475,7 +480,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_folder"
android:src="@drawable/owl_24px"
android:tint="@color/white" />
<TextView
@@ -529,7 +534,7 @@
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_file"
android:src="@drawable/photo_prints_24px"
android:tint="@color/white" />
<TextView
@@ -610,7 +615,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_control_info"
android:src="@drawable/egg_24px"
android:tint="@color/white" />
<TextView
@@ -354,11 +354,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/tunnelSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
@@ -387,11 +386,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/audioDecodeSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
@@ -420,11 +418,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/aacSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
@@ -453,11 +450,10 @@
android:textColor="@color/white"
android:textSize="16sp" />
<androidx.appcompat.widget.SwitchCompat
<com.fongmi.android.tv.ui.custom.CustomSwitch
android:id="@+id/danmakuLoadSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/M3SwitchStyle" />
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
+12 -9
View File
@@ -112,11 +112,13 @@
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginBottom="16dp"
android:src="@drawable/ic_logo" />
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottieAnimation"
android:layout_width="180dp"
android:layout_height="180dp"
app:lottie_fileName="lottie_empty_1.json"
app:lottie_loop="true"
app:lottie_autoPlay="true" />
<TextView
android:layout_width="wrap_content"
@@ -124,16 +126,17 @@
android:gravity="center"
android:text="@string/source_hint"
android:textColor="@color/white"
android:textSize="16sp" />
android:textSize="14sp" />
<Button
android:id="@+id/add_source_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="200dp"
android:layout_height="48dp"
android:layout_marginTop="16dp"
android:backgroundTint="@color/primary"
android:text="@string/add_source"
android:textColor="@color/black" />
android:textColor="@color/black"
android:textSize="16sp" />
</LinearLayout>
<ImageView
+23 -1
View File
@@ -56,8 +56,30 @@
android:paddingEnd="8dp"
android:textColor="@color/white"
android:textSize="16sp"
tools:text="21:30" />
<ImageView
android:id="@+id/charging_indicator"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:layout_marginEnd="2dp"
android:src="@drawable/ic_charging_bolt"
android:visibility="gone"
tools:text="21:30 | 85%" />
tools:visibility="visible" />
<TextView
android:id="@+id/battery_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingEnd="8dp"
android:textColor="@color/white"
android:textSize="16sp"
android:visibility="gone"
tools:text="85%"
tools:visibility="visible" />
<ImageView
android:id="@+id/cast"
+6 -8
View File
@@ -78,14 +78,12 @@
<item name="android:background">@null</item>
</style>
<!-- M3 Switch Style -->
<style name="M3SwitchStyle" parent="Widget.AppCompat.CompoundButton.Switch">
<item name="android:thumb">@drawable/m3_switch_thumb</item>
<item name="track">@drawable/m3_switch_track</item>
<item name="android:switchMinWidth">52dp</item>
<item name="android:switchPadding">8dp</item>
<item name="android:colorControlActivated">#FFEB3B</item>
<item name="android:colorControlHighlight">@android:color/transparent</item>
<!-- M3 Switch Style (Material Design) - 使用CustomSwitch完全自定义 -->
<style name="M3SwitchStyle" parent="Widget.AppCompat.CompoundButton.CheckBox">
<item name="android:button">@null</item>
<item name="android:background">@drawable/custom_switch_bg</item>
<item name="android:minHeight">30dp</item>
<item name="android:minWidth">50dp</item>
</style>
<!-- 自定义数据源按钮样式 -->
+6 -6
View File
@@ -1,7 +1,7 @@
plugins {
id 'com.android.application' version '8.8.2' apply false
id 'com.android.library' version '8.8.2' apply false
id 'com.chaquo.python' version '15.0.1' apply false
id 'com.android.application' version '8.12.0' apply false
id 'com.android.library' version '8.12.0' apply false
id 'com.chaquo.python' version '16.1.0' apply false
}
tasks.register('clean', Delete) {
@@ -9,7 +9,7 @@ tasks.register('clean', Delete) {
}
project.ext {
gsonVersion = '2.11.0'
media3Version = '1.6.1'
okhttpVersion = '5.0.0-alpha.14'
gsonVersion = '2.13.1'
media3Version = '1.8.0'
okhttpVersion = '5.1.0'
}
+7 -5
View File
@@ -5,21 +5,23 @@ plugins {
android {
namespace 'com.github.catvod.crawler'
compileSdk 35
compileSdk {
version = release(36)
}
defaultConfig {
minSdk 21
minSdk 24
targetSdk 28
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
api 'androidx.annotation:annotation:1.6.0'
api 'androidx.annotation:annotation:1.9.1'
api 'androidx.preference:preference:1.2.1'
api 'com.google.code.gson:gson:' + gsonVersion
api 'com.google.net.cronet:cronet-okhttp:0.1.0'
@@ -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/tosencen/XMBOX/raw/main";
public static final String CN_API_URL = "https://gitee.com/api/v5/repos/tosencen/XMBOX/releases/latest";
// 国内镜像地址 - 使用Gitee作为镜像
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()) {
@@ -0,0 +1,35 @@
package com.github.catvod.utils;
import android.util.Log;
public class Logger {
private static final String TAG = "XMBOX";
public static void d(String msg) {
Log.d(TAG, msg);
}
public static void e(String msg) {
Log.e(TAG, msg);
}
public static void e(String msg, Throwable tr) {
Log.e(TAG, msg, tr);
}
public static void i(String msg) {
Log.i(TAG, msg);
}
public static void v(String msg) {
Log.v(TAG, msg);
}
public static void w(String msg) {
Log.w(TAG, msg);
}
public static void w(String msg, Throwable tr) {
Log.w(TAG, msg, tr);
}
}
@@ -1,5 +1,6 @@
package com.github.catvod.utils;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.preference.PreferenceManager;
+51
View File
@@ -0,0 +1,51 @@
plugins {
id 'com.android.library'
id 'com.chaquo.python'
}
android {
namespace 'com.fongmi.chaquo'
compileSdk {
version = release(36)
}
defaultConfig {
minSdk 24
targetSdk 28
python {
version "3.8"
pip {
install("-r", "requirements.txt")
}
}
}
flavorDimensions = ["abi"]
productFlavors {
arm64_v8a {
dimension "abi"
ndk { abiFilters "arm64-v8a" }
}
armeabi_v7a {
dimension "abi"
ndk { abiFilters "armeabi-v7a" }
}
}
sourceSets {
main {
python.srcDirs = ["src/main/python"]
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
implementation project(':catvod')
}
+8
View File
@@ -0,0 +1,8 @@
lxml
ujson
pyquery
requests
jsonpath
cachetools
pycryptodome
beautifulsoup4
+3
View File
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
+18
View File
@@ -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 上检查并发布"
+3 -3
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 主题颜色 -->
<color name="primary">#FF0057B8</color>
<color name="primaryDark">#FF003C7E</color>
<color name="accent">#FF0057B8</color>
<color name="primary">#FFEB3B</color>
<color name="primaryDark">#FDD835</color>
<color name="accent">#FFEB3B</color>
<!-- 基本颜色 -->
<color name="white">#FFFFFFFF</color>
+1 -1
View File
@@ -1,6 +1,6 @@
#Wed Mar 29 12:54:35 CST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
+8 -10
View File
@@ -5,26 +5,24 @@ plugins {
android {
namespace 'com.fongmi.android.tv.quickjs'
compileSdk 35
compileSdk {
version = release(36)
}
defaultConfig {
minSdk 21
minSdk 24
targetSdk 28
}
lint {
disable 'UnsafeOptInUsageError'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
implementation project(':catvod')
implementation 'wang.harlon.quickjs:wrapper-java:3.2.0'
implementation 'wang.harlon.quickjs:wrapper-android:3.2.0'
implementation 'wang.harlon.quickjs:wrapper-java:3.2.3'
implementation 'wang.harlon.quickjs:wrapper-android:3.2.3'
implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.4'
}
Binary file not shown.
Binary file not shown.