Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e95ffad576 | |||
| 78570eed7c | |||
| dcc751c691 | |||
| 7a9dc27835 | |||
| 3407f1f955 | |||
| 54280b68eb | |||
| cf56f091f3 | |||
| 9d6d531ffe | |||
| 0c60ddf63d | |||
| 928a0e9807 | |||
| 93d8c5703b | |||
| 597261ff57 | |||
| d4d30d39c1 | |||
| f49f1cd0b0 | |||
| 8c6275ffe8 | |||
| e094f38423 | |||
| 0734ffc630 | |||
| a8700a8c66 | |||
| ca95128ee9 | |||
| dde56eeedb | |||
| f530ee6407 | |||
| f9ec0334e1 | |||
| 389d548d08 | |||
| fb948dc8c0 | |||
| ce2f46cf5b | |||
| db63949a31 | |||
| f2127ab3a6 | |||
| f525a88668 | |||
| 91a20c8aae | |||
| 59a8c4fd01 | |||
| 3f63cc2416 | |||
| fcdef561ec | |||
| 8d0ae1d5b4 | |||
| afd2d4667d | |||
| ceadb06a64 | |||
| f276fad550 | |||
| b20cf45850 | |||
| 593f1e7444 | |||
| a4d671b394 | |||
| 836e363f94 | |||
| dab1425dea | |||
| 0d7b25710c | |||
| a1a45aeacd | |||
| 6eb7f9139d | |||
| eafc53e8b2 | |||
| 9c7a0fd40e | |||
| b847ff23dd | |||
| 8579905949 | |||
| 6d6239e602 | |||
| 81832a055e | |||
| 4ec3c434b2 | |||
| d47f8d5cd4 | |||
| 5f7df956c3 | |||
| beaf3b30f6 | |||
| 730c6cc7a7 | |||
| 7c1744b366 | |||
| 1b61870cd4 | |||
| 0dcc0e6da1 | |||
| 592bcff438 | |||
| 6af8908670 | |||
| 993ef78d4d | |||
| 53ef17c01d |
+36
-7
@@ -1,8 +1,37 @@
|
|||||||
.idea
|
# Gradle files
|
||||||
.gradle
|
.gradle/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Local configuration file (sdk path, etc)
|
||||||
|
local.properties
|
||||||
|
|
||||||
|
# Log/OS Files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Android Studio generated files and folders
|
||||||
|
captures/
|
||||||
|
.externalNativeBuild/
|
||||||
|
.cxx/
|
||||||
|
*.apk
|
||||||
|
*.aab
|
||||||
|
output.json
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
misc.xml
|
||||||
|
deploymentTargetDropDown.xml
|
||||||
|
render.experimental.xml
|
||||||
|
|
||||||
|
# Keystore files
|
||||||
*.jks
|
*.jks
|
||||||
lib-*.aar
|
*.keystore
|
||||||
*build
|
|
||||||
/media*
|
# Google Services (e.g. APIs or Firebase)
|
||||||
/Release
|
google-services.json
|
||||||
/local.properties
|
|
||||||
|
# Android Profiling
|
||||||
|
*.hprof
|
||||||
|
|
||||||
|
# APK files
|
||||||
|
apk/release/*.apk
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"java.configuration.updateBuildConfiguration": "automatic"
|
||||||
|
}
|
||||||
@@ -1,281 +1,307 @@
|
|||||||
# XMBOX
|
<h1 align="center"> 📱 XMBOX - Android资源播放器
|
||||||
|
</h1>
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
一个简单的视频播放器应用,支持以下功能:
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## 主要功能
|
一个操作方便、界面简洁的Android视频播放器盒子,需自行添源,支持TV和手机双平台。
|
||||||
- 视频播放:支持多种格式视频播放
|
|
||||||
- 直播观看:支持直播源播放
|
|
||||||
- 收藏管理:可收藏喜欢的视频和直播源
|
|
||||||
- 设置中心:自定义应用配置
|
|
||||||
|
|
||||||
## 技术特点
|
[下载APK](https://github.com/Tosencen/XMBOX-Release/tree/main/apk/release) • [功能特性](#-功能特性) • [构建指南](#-构建指南) • [API文档](#-api文档)
|
||||||
- 基于 Android 原生开发
|
|
||||||
- 使用 ExoPlayer 作为播放内核
|
|
||||||
- 支持 TV 和手机双平台
|
|
||||||
- Material Design 界面设计
|
|
||||||
|
|
||||||
## 应用截图
|
</div>
|
||||||
- 视频列表
|
|
||||||
- 播放界面
|
|
||||||
- 设置中心
|
|
||||||
|
|
||||||
## 开发说明
|
## 🎯 功能特性
|
||||||
本项目仅用于学习 Android 开发,代码改自 [FongMi/TV](https://github.com/FongMi/TV)。
|
|
||||||
|
|
||||||
## 免责声明
|
### 📺 多平台支持
|
||||||
1. 本项目仅供学习交流使用,不得用于商业用途
|
- **Android TV版本** - 针对电视、盒子优化的遥控器界面
|
||||||
2. 项目中的内容均来自网络,如有侵权请联系删除
|
- **手机版本** - 触屏友好的移动端界面
|
||||||
3. 使用本项目产生的一切后果由使用者自行承担
|
- **多架构支持** - ARM64-V8A 和 ARM V7A 双架构
|
||||||
|
|
||||||
## 许可证
|
### 🎬 强大的播放功能
|
||||||
GPL-3.0 license
|
- 🎵 **多格式支持** - 支持主流视频格式播放
|
||||||
|
- 📡 **直播观看** - 支持各种直播源协议
|
||||||
|
- 🔍 **智能搜索** - 全局搜索和换源功能
|
||||||
|
- 📚 **收藏管理** - 视频收藏和历史记录
|
||||||
|
- 🎨 **自定义界面** - 丰富的主题和布局选项
|
||||||
|
|
||||||
# 影視
|
### ⚡ 技术特色
|
||||||
|
- 🚀 **高性能播放** - 基于ExoPlayer播放内核
|
||||||
|
- 🔧 **模块化架构** - 清晰的模块分层设计
|
||||||
|
- 🛡️ **稳定可靠** - 完善的错误处理和崩溃防护
|
||||||
|
- 🌐 **网络优化** - 智能代理和DNS解析
|
||||||
|
- 📱 **Material Design** - 现代化UI设计
|
||||||
|
|
||||||
### 基於 CatVod 項目
|
## 📥 下载安装
|
||||||
|
|
||||||
https://github.com/CatVodTVOfficial/CatVodTVJarLoader
|
### 最新版本: v3.0.8
|
||||||
|
|
||||||
### 點播欄位
|
| 平台 | ARM64-V8A | ARM V7A |
|
||||||
|
|------|-----------|---------|
|
||||||
|
| **📱 手机版** | [下载 (34MB)](https://github.com/Tosencen/XMBOX-Release/raw/main/apk/release/v3.0.8/mobile-arm64_v8a-v3.0.8.apk) | [下载 (30MB)](https://github.com/Tosencen/XMBOX-Release/raw/main/apk/release/v3.0.8/mobile-armeabi_v7a-v3.0.8.apk) |
|
||||||
|
| **📺 TV版** | [下载 (34MB)](https://github.com/Tosencen/XMBOX-Release/raw/main/apk/release/v3.0.8/leanback-arm64_v8a-v3.0.8.apk) | [下载 (30MB)](https://github.com/Tosencen/XMBOX-Release/raw/main/apk/release/v3.0.8/leanback-armeabi_v7a-v3.0.8.apk) |
|
||||||
|
|
||||||
| 欄位名稱 | 預設值 | 說明 | 其他 |
|
### 📁 版本历史
|
||||||
|------------|------|------|------------|
|
- **v3.0.8**: [查看v3.0.8版本](https://github.com/Tosencen/XMBOX-Release/tree/main/apk/release/v3.0.8) - UI交互体验全面优化
|
||||||
| searchable | 1 | 是否搜索 | 0:關閉;1:啟用 |
|
- **v3.0.7**: [查看v3.0.7版本](https://github.com/Tosencen/XMBOX-Release/tree/main/apk/release/v3.0.7) - 全面优化稳定性和用户体验
|
||||||
| changeable | 1 | 是否換源 | 0:關閉;1:啟用 |
|
|
||||||
| quickserch | 1 | 是否快搜 | 0:關閉;1:啟用 |
|
|
||||||
| indexs | 0 | 是否聚搜 | 0:關閉;1:啟用 |
|
|
||||||
| hide | 0 | 是否隱藏 | 0:顯示;1:隱藏 |
|
|
||||||
| timeout | 15 | 播放超時 | 單位:秒 |
|
|
||||||
| header | none | 請求標頭 | 格式:json |
|
|
||||||
| click | none | 點擊js | javascript |
|
|
||||||
|
|
||||||
### 直播欄位
|
### 📦 下载说明
|
||||||
|
- **最新版本**: 根目录的 `mobile.json` 和 `leanback.json` 包含最新版本信息
|
||||||
|
- **历史版本**: 每个版本都有独立的文件夹,包含完整的APK文件和版本信息
|
||||||
|
- **文件结构**: 按版本号组织,便于管理和下载
|
||||||
|
- **签名保护**: 所有APK均使用v1/v2/v3/v4多重签名保护
|
||||||
|
|
||||||
| 欄位名稱 | 預設值 | 說明 | 其他 |
|
TV版基于 [FongMi/TV](https://github.com/FongMi/TV) 原项目就改了些配色,想要嘿稳定的可去原项目体验
|
||||||
|----------|-------|-------|------------|
|
### 📋 系统要求
|
||||||
| ua | none | 用戶代理 | |
|
- Android 5.0 (API 21) 及以上
|
||||||
| origin | none | 來源 | |
|
- ARM64-V8A: 推荐新设备使用,性能更优
|
||||||
| referer | none | 參照地址 | |
|
- ARM V7A: 兼容老设备,适配性更强
|
||||||
| epg | none | 節目地址 | |
|
|
||||||
| logo | none | 台標地址 | |
|
|
||||||
| pass | false | 是否免密碼 | |
|
|
||||||
| boot | false | 是否自啟動 | |
|
|
||||||
| timeout | 15 | 播放超時 | 單位:秒 |
|
|
||||||
| header | none | 請求標頭 | 格式:json |
|
|
||||||
| click | none | 點擊js | javascript |
|
|
||||||
| catchup | none | 回看參數 | |
|
|
||||||
| timeZone | none | 時區 | |
|
|
||||||
|
|
||||||
### 樣式
|
## 🏗️ 构建指南
|
||||||
|
|
||||||
| 欄位名稱 | 值 | 說明 |
|
### 📋 环境要求
|
||||||
|-------|------|-----|
|
- Android Studio Arctic Fox 或更高版本
|
||||||
| type | rect | 矩形 |
|
- JDK 11 或更高版本
|
||||||
| | oval | 橢圓 |
|
- Android SDK API 35
|
||||||
| | list | 列表 |
|
- Gradle 8.10.2
|
||||||
| ratio | 0.75 | 3:4 |
|
|
||||||
| | 1.33 | 4:3 |
|
|
||||||
|
|
||||||
直式
|
### 🔧 快速开始
|
||||||
|
|
||||||
```json
|
1. **克隆项目**
|
||||||
{
|
```bash
|
||||||
"style": {
|
git clone https://github.com/yourusername/XMBOX.git
|
||||||
"type": "rect"
|
cd XMBOX
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
橫式
|
2. **配置签名** (可选)
|
||||||
|
```bash
|
||||||
```json
|
# 将你的签名文件放到 keystore/ 目录
|
||||||
{
|
# 或修改 app/build.gradle 中的签名配置
|
||||||
"style": {
|
|
||||||
"type": "rect",
|
|
||||||
"ratio": 1.33
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
正方
|
3. **构建项目**
|
||||||
|
```bash
|
||||||
|
# 构建所有版本
|
||||||
|
./gradlew assembleRelease
|
||||||
|
|
||||||
```json
|
# 构建特定版本
|
||||||
{
|
./gradlew assembleMobileArm64_v8aRelease # 手机版 ARM64
|
||||||
"style": {
|
./gradlew assembleLeanbackArm64_v8aRelease # TV版 ARM64
|
||||||
"type": "rect",
|
./gradlew assembleMobileArmeabi_v7aRelease # 手机版 ARM V7A
|
||||||
"ratio": 1
|
./gradlew assembleLeanbackArmeabi_v7aRelease # TV版 ARM V7A
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
正圓
|
4. **生成的APK位置**
|
||||||
|
```
|
||||||
```json
|
app/build/outputs/apk/
|
||||||
{
|
├── mobileArm64_v8a/release/mobile-arm64_v8a.apk
|
||||||
"style": {
|
├── leanbackArm64_v8a/release/leanback-arm64_v8a.apk
|
||||||
"type": "oval"
|
├── mobileArmeabi_v7a/release/mobile-armeabi_v7a.apk
|
||||||
}
|
└── leanbackArmeabi_v7a/release/leanback-armeabi_v7a.apk
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
橢圓
|
## 🏛️ 项目架构
|
||||||
|
|
||||||
```json
|
### 📂 模块说明
|
||||||
{
|
```
|
||||||
"style": {
|
XMBOX/
|
||||||
"type": "oval",
|
├── app/ # 主应用模块
|
||||||
"ratio": 1.1
|
│ ├── src/main/ # 通用代码
|
||||||
}
|
│ ├── src/mobile/ # 手机版特定代码
|
||||||
}
|
│ └── src/leanback/ # TV版特定代码
|
||||||
|
├── catvod/ # 视频点播核心
|
||||||
|
├── quickjs/ # JavaScript引擎
|
||||||
|
├── forcetech/ # 强制技术模块
|
||||||
|
├── thunder/ # 迅雷下载模块
|
||||||
|
├── hook/ # 钩子功能
|
||||||
|
├── jianpian/ # 视频剪辑模块
|
||||||
|
├── tvbus/ # TV总线功能
|
||||||
|
└── zlive/ # 直播功能模块
|
||||||
```
|
```
|
||||||
|
|
||||||
### API
|
### 🔧 技术栈
|
||||||
|
- **开发语言**: Java
|
||||||
|
- **UI框架**: Android Views + Material Components
|
||||||
|
- **播放器**: ExoPlayer
|
||||||
|
- **网络库**: OkHttp
|
||||||
|
- **JSON解析**: Gson
|
||||||
|
- **异步处理**: EventBus
|
||||||
|
- **数据库**: Room
|
||||||
|
|
||||||
刷新詳情
|
## 📝 更新日志
|
||||||
|
|
||||||
```
|
### v3.0.8 (2025-10-14)
|
||||||
http://127.0.0.1:9978/action?do=refresh&type=detail
|
|
||||||
|
#### 🎨 UI交互体验全面优化
|
||||||
|
* **修复按钮点击效果** - 解决按钮点击效果过于明显的问题
|
||||||
|
* **统一自定义背景** - 使用自定义背景替代系统selectableItemBackgroundBorderless
|
||||||
|
* **移除文字阴影** - 清理Control.Action样式中的文字阴影效果
|
||||||
|
* **优化直播页面** - 选择按钮颜色统一为主题黄色
|
||||||
|
* **调整页面布局** - 许可协议页面按钮区域上间距调整为8dp
|
||||||
|
* **修复文字重叠** - 解决跨类和换源按钮的文字重叠问题
|
||||||
|
* **提升视觉一致性** - 整体UI视觉一致性和用户体验优化
|
||||||
|
|
||||||
|
#### 🔧 技术改进
|
||||||
|
* **优化内存使用** - 改进内存管理机制
|
||||||
|
* **提升播放稳定性** - 增强播放器稳定性
|
||||||
|
* **文件结构重组** - 按版本号重新组织发布文件结构
|
||||||
|
|
||||||
|
### v3.0.5 (2025-08-20)
|
||||||
|
#### 🎨 界面优化
|
||||||
|
- 优化导航栏历史记录图标,采用 Material Design 3 规范的列表图标
|
||||||
|
- 改进设置页面的图标显示效果
|
||||||
|
- 优化用户界面视觉体验
|
||||||
|
|
||||||
|
### v3.0.4 (2025-07-30)
|
||||||
|
#### 🐛 修复
|
||||||
|
- 修复设置页面源管理模块中切换视频源时的随机闪退问题
|
||||||
|
- 增强VodConfig.setHome()方法的空指针异常处理
|
||||||
|
- 改进Fragment生命周期检查以防止崩溃
|
||||||
|
- 优化HistoryDialog中源切换的安全性
|
||||||
|
- 增强并发加载的线程安全性
|
||||||
|
|
||||||
|
#### ⚡ 优化
|
||||||
|
- 提升应用启动速度
|
||||||
|
- 优化内存使用
|
||||||
|
- 增强网络请求稳定性
|
||||||
|
|
||||||
|
#### 🆕 新增
|
||||||
|
- 新增自动缓存清理功能
|
||||||
|
- 添加更完善的错误处理机制
|
||||||
|
- 增强崩溃保护功能
|
||||||
|
|
||||||
|
### v3.0.3 及更早版本
|
||||||
|
查看 [完整更新日志](CHANGELOG.md)
|
||||||
|
|
||||||
|
## 🔌 API 文档
|
||||||
|
|
||||||
|
### 刷新操作
|
||||||
|
```http
|
||||||
|
# 刷新详情
|
||||||
|
GET http://127.0.0.1:9978/action?do=refresh&type=detail
|
||||||
|
|
||||||
|
# 刷新播放
|
||||||
|
GET http://127.0.0.1:9978/action?do=refresh&type=player
|
||||||
|
|
||||||
|
# 刷新直播
|
||||||
|
GET http://127.0.0.1:9978/action?do=refresh&type=live
|
||||||
```
|
```
|
||||||
|
|
||||||
刷新播放
|
### 推送功能
|
||||||
|
```http
|
||||||
|
# 推送字幕
|
||||||
|
GET http://127.0.0.1:9978/action?do=refresh&type=subtitle&path=http://xxx
|
||||||
|
|
||||||
```
|
# 推送弹幕
|
||||||
http://127.0.0.1:9978/action?do=refresh&type=player
|
GET http://127.0.0.1:9978/action?do=refresh&type=danmaku&path=http://xxx
|
||||||
```
|
```
|
||||||
|
|
||||||
刷新直播
|
### 缓存管理
|
||||||
|
```http
|
||||||
|
# 新增缓存
|
||||||
|
GET http://127.0.0.1:9978/cache?do=set&key=xxx&value=xxx
|
||||||
|
|
||||||
```
|
# 获取缓存
|
||||||
http://127.0.0.1:9978/action?do=refresh&type=live
|
GET http://127.0.0.1:9978/cache?do=get&key=xxx
|
||||||
|
|
||||||
|
# 删除缓存
|
||||||
|
GET http://127.0.0.1:9978/cache?do=del&key=xxx
|
||||||
```
|
```
|
||||||
|
|
||||||
推送字幕
|
更多API文档请查看 [API参考手册](docs/API.md)
|
||||||
|
|
||||||
```
|
## 📖 配置说明
|
||||||
http://127.0.0.1:9978/action?do=refresh&type=subtitle&path=http://xxx
|
|
||||||
```
|
|
||||||
|
|
||||||
推送彈幕
|
### 点播字段配置
|
||||||
|
| 字段名 | 默认值 | 说明 | 备注 |
|
||||||
|
|--------|--------|------|------|
|
||||||
|
| searchable | 1 | 是否支持搜索 | 0:关闭 1:启用 |
|
||||||
|
| changeable | 1 | 是否支持换源 | 0:关闭 1:启用 |
|
||||||
|
| quickSearch | 1 | 是否快速搜索 | 0:关闭 1:启用 |
|
||||||
|
| timeout | 15 | 播放超时时间 | 单位:秒 |
|
||||||
|
|
||||||
```
|
### 直播字段配置
|
||||||
http://127.0.0.1:9978/action?do=refresh&type=danmaku&path=http://xxx
|
| 字段名 | 默认值 | 说明 | 备注 |
|
||||||
```
|
|--------|--------|------|------|
|
||||||
|
| ua | none | 用户代理 | |
|
||||||
|
| origin | none | 来源 | |
|
||||||
|
| referer | none | 引用地址 | |
|
||||||
|
| timeout | 15 | 播放超时 | 单位:秒 |
|
||||||
|
|
||||||
新增緩存字串
|
详细配置说明请查看 [配置文档](docs/CONFIG.md)
|
||||||
|
|
||||||
```
|
## 🤝 贡献指南
|
||||||
http://127.0.0.1:9978/cache?do=set&key=xxx&value=xxx
|
|
||||||
```
|
|
||||||
|
|
||||||
取得緩存字串
|
欢迎提交 Issue 和 Pull Request!
|
||||||
|
|
||||||
```
|
### 🔄 提交规范
|
||||||
http://127.0.0.1:9978/cache?do=get&key=xxx
|
- feat: 新功能
|
||||||
```
|
- fix: 修复bug
|
||||||
|
- docs: 文档更新
|
||||||
|
- style: 代码格式调整
|
||||||
|
- refactor: 代码重构
|
||||||
|
- test: 测试相关
|
||||||
|
- chore: 构建配置等
|
||||||
|
|
||||||
刪除緩存字串
|
### 🧪 开发流程
|
||||||
|
1. Fork 本项目
|
||||||
|
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
|
||||||
|
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||||
|
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||||
|
5. 创建 Pull Request
|
||||||
|
|
||||||
```
|
### ⚖️ 许可协议
|
||||||
http://127.0.0.1:9978/cache?do=del&key=xxx
|
XMBOX软件许可协议:
|
||||||
```
|
- 以下是对[GPL-3.0](LICENSE.md)开源协议的补充,如有冲突,以以下协议为准。
|
||||||
|
- 词语约定: 本协议中的“本软件”指“XMBOX软件”,“用户”指签署本协议的使用者,“版权数据”指包括但不限于视频、图像、音频、名字等在内的他人拥有所属版权的数据。
|
||||||
|
1. 本软件仅为技术性多媒体播放器外壳(“空壳播放器”),核心功能限于基础媒体文件解析与播放。
|
||||||
|
2. 本软件自身不包含、不预装、不内置、不集成、不主动推荐、不直接或间接提供任何音视频、直播、图文等媒体资源内容。软件播放的任何资源均非由本软件或其开发者提供。
|
||||||
|
3. 用户通过本软件播放的任何内容均完全来源于用户自行配置、输入、添加、获取或选择的第三方来源(如网络地址、本地文件、用户安装的插件/扩展/配置源等)。本软件仅作为访问用户自行指定内容的技术工具。
|
||||||
|
4. 本软件无法控制、筛选、审查或保证用户访问的任何第三方内容的合法性、版权状态、准确性、安全性或适宜性。用户对其播放的内容负全部责任。
|
||||||
|
5. 关于用户责任与风险承担:
|
||||||
|
* 用户必须确保其通过本软件配置、访问或播放的所有内容均已获相关权利人合法授权,或属于法律允许的自由使用范畴。
|
||||||
|
* 用户理解并同意,使用本软件访问第三方资源可能涉及侵犯版权、传播非法信息、隐私泄露、网络安全等风险。因用户使用本软件访问、播放或传播内容产生的一切法律责任、纠纷、损失及后果(包括法律诉讼、行政处罚、民事赔偿等),均由用户自行承担,与本软件及其开发者无涉。
|
||||||
|
* 开发者不认可、不支持任何利用本软件规避技术保护措施(如DRM)的行为,此类行为导致的侵权责任由用户全权承担。
|
||||||
|
6. 用户承诺并保证不利用本软件从事任何侵犯他人知识产权或其他合法权益的活动,或进行任何违反法律法规的行为。严禁使用本软件播放、传播盗版、色情、暴力、赌博、诈骗、危害国家安全、危害社会稳定等非法或侵权内容。
|
||||||
|
7. 在任何情况下,本软件开发者均不就因用户使用或无法使用本软件、用户配置或访问的第三方资源、用户违反本协议或法律法规的行为导致的任何直接、间接、偶然、特殊、惩罚性或结果性损害(包括利润损失、数据丢失、业务中断、声誉损害等)承担任何责任(无论基于合同、侵权、严格责任或其他法律理论)。
|
||||||
|
8. 本软件运行可能依赖第三方库、服务或技术。开发者不对这些第三方组件的可用性、准确性、功能或合法性负责。
|
||||||
|
9. 用户理解并同意,使用本软件(包括下载、安装、运行)存在固有技术风险(如软件缺陷、兼容性问题、系统不稳定等),用户应自行承担此风险。
|
||||||
|
10. 本软件仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。
|
||||||
|
11. 本软件内使用的部分包括但不限于字体、图片等资源来源于互联网。如果出现侵权可联系开发者移除。
|
||||||
|
12. 使用本软件的过程中可能会产生版权数据。对于这些版权数据,本软件不拥有它们的所有权。为了避免侵权,用户务必在 24 小时内 清除使用本项目的过程中所产生的版权数据。
|
||||||
|
13. 本协议受中华人民共和国法律管辖并据其解释。若用户所在地法律强制规定特定责任条款,应以当地法律要求为准,但其他条款仍保持有效。任何由本协议或使用本软件引起的争议,应首先通过友好协商解决。
|
||||||
|
14. 若你使用了本软件,即代表你接受本协议。
|
||||||
|
|
||||||
### Proxy
|
## ⚖️ 免责声明
|
||||||
|
|
||||||
scheme 支持 http, https, socks4, socks5
|
1. **学习用途**: 本项目仅供学习和技术交流使用,不得用于商业用途
|
||||||
|
2. **内容来源**: 项目中的内容来源于网络,如有侵权请联系删除
|
||||||
|
3. **使用责任**: 使用本项目产生的一切后果由使用者自行承担
|
||||||
|
4. **法律合规**: 请确保在当地法律法规允许的范围内使用本软件
|
||||||
|
|
||||||
```
|
## 📄 开源协议
|
||||||
scheme://username:password@host:port
|
|
||||||
```
|
|
||||||
|
|
||||||
配置新增 proxy 判斷域名是否走代理
|
本项目基于 [GPL-3.0](LICENSE.md) 协议开源
|
||||||
全局只需要加上一條規則 ".*."
|
|
||||||
|
|
||||||
```json
|
## 🙏 致谢
|
||||||
{
|
|
||||||
"spider": "",
|
|
||||||
"proxy": [
|
|
||||||
"raw.githubusercontent.com",
|
|
||||||
"googlevideo.com"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Hosts
|
- 基于 [FongMi/TV](https://github.com/FongMi/TV) 项目开发
|
||||||
|
- 感谢 [CatVodTVOfficial](https://github.com/CatVodTVOfficial) 提供的核心技术
|
||||||
|
- 感谢所有为项目做出贡献的开发者
|
||||||
|
|
||||||
```json
|
## 📞 联系方式
|
||||||
{
|
|
||||||
"spider": "",
|
|
||||||
"hosts": [
|
|
||||||
"cache.ott.*.itv.cmvideo.cn=base-v4-free-mghy.e.cdn.chinamobile.com"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Headers
|
- GitHub Issues: [提交问题](../../issues)
|
||||||
|
- 讨论区: [Discussions](../../discussions)
|
||||||
|
|
||||||
```json
|
---
|
||||||
{
|
|
||||||
"spider": "",
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"host": "gslbserv.itv.cmvideo.cn",
|
|
||||||
"header": {
|
|
||||||
"User-Agent": "okhttp/3.12.13",
|
|
||||||
"Referer": "test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 爬蟲本地代理
|
<div align="center">
|
||||||
|
|
||||||
Java
|
**⭐ 如果这个项目对你有帮助,请给我们一个 Star!**
|
||||||
|
|
||||||
```
|
Made with ❤️ by XMBOX Team
|
||||||
proxy://
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
</div>
|
||||||
Proxy.getUrl(boolean local)
|
|
||||||
```
|
|
||||||
|
|
||||||
Python
|
|
||||||
|
|
||||||
```
|
|
||||||
proxy://do=py
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
getProxyUrl(boolean local)
|
|
||||||
```
|
|
||||||
|
|
||||||
JS
|
|
||||||
|
|
||||||
```
|
|
||||||
proxy://do=js
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
getProxy(boolean local)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 配置範例
|
|
||||||
|
|
||||||
[點播-線上](other/sample/vod/online.json)
|
|
||||||
[點播-本地](other/sample/vod/offline.json)
|
|
||||||
[直播-線上](other/sample/live/online.json)
|
|
||||||
[直播-本地](other/sample/live/offline.json)
|
|
||||||
|
|
||||||
### 飛機群
|
|
||||||
|
|
||||||
[討論群組](https://t.me/+qTlg0qAVzP9kMmM1)
|
|
||||||
[發布頻道](https://t.me/fongmi_release)
|
|
||||||
|
|
||||||
### 贊助
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Star
|
|
||||||
|
|
||||||
[](https://www.star-history.com/#FongMi/TV&Date)
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# XMBOX Release Files
|
||||||
|
|
||||||
|
## 📁 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
apk/release/
|
||||||
|
├── mobile.json # 最新版本信息 (手机版)
|
||||||
|
├── leanback.json # 最新版本信息 (TV版)
|
||||||
|
├── v3.0.7/ # v3.0.7版本文件
|
||||||
|
│ ├── mobile.json # v3.0.7版本信息
|
||||||
|
│ ├── leanback.json # v3.0.7版本信息
|
||||||
|
│ ├── mobile-arm64_v8a.apk
|
||||||
|
│ ├── mobile-armeabi_v7a.apk
|
||||||
|
│ ├── leanback-arm64_v8a.apk
|
||||||
|
│ └── leanback-armeabi_v7a.apk
|
||||||
|
└── v3.0.8/ # v3.0.8版本文件
|
||||||
|
├── mobile.json # v3.0.8版本信息
|
||||||
|
├── leanback.json # v3.0.8版本信息
|
||||||
|
├── mobile-arm64_v8a-v3.0.8.apk
|
||||||
|
├── mobile-armeabi_v7a-v3.0.8.apk
|
||||||
|
├── leanback-arm64_v8a-v3.0.8.apk
|
||||||
|
└── leanback-armeabi_v7a-v3.0.8.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📱 版本说明
|
||||||
|
|
||||||
|
### v3.0.8 (最新版本)
|
||||||
|
- **发布时间**: 2025-10-14
|
||||||
|
- **版本代码**: 308
|
||||||
|
- **主要更新**: UI交互体验全面优化
|
||||||
|
|
||||||
|
### v3.0.7
|
||||||
|
- **发布时间**: 2025-09-26
|
||||||
|
- **版本代码**: 307
|
||||||
|
- **主要更新**: 全面优化稳定性和用户体验
|
||||||
|
|
||||||
|
## 🔗 下载链接
|
||||||
|
|
||||||
|
### 最新版本 (v3.0.8)
|
||||||
|
- **手机版 ARM64**: [mobile-arm64_v8a-v3.0.8.apk](v3.0.8/mobile-arm64_v8a-v3.0.8.apk)
|
||||||
|
- **手机版 ARMv7**: [mobile-armeabi_v7a-v3.0.8.apk](v3.0.8/mobile-armeabi_v7a-v3.0.8.apk)
|
||||||
|
- **TV版 ARM64**: [leanback-arm64_v8a-v3.0.8.apk](v3.0.8/leanback-arm64_v8a-v3.0.8.apk)
|
||||||
|
- **TV版 ARMv7**: [leanback-armeabi_v7a-v3.0.8.apk](v3.0.8/leanback-armeabi_v7a-v3.0.8.apk)
|
||||||
|
|
||||||
|
### 历史版本
|
||||||
|
- **v3.0.7**: [查看v3.0.7版本文件](v3.0.7/)
|
||||||
|
|
||||||
|
## 📋 版本信息
|
||||||
|
|
||||||
|
每个版本目录都包含对应的JSON配置文件,包含:
|
||||||
|
- `name`: 版本号
|
||||||
|
- `desc`: 版本描述和更新内容
|
||||||
|
- `code`: 版本代码
|
||||||
|
- `downloads`: 下载链接映射 (仅根目录文件)
|
||||||
|
|
||||||
|
## 🔐 签名信息
|
||||||
|
|
||||||
|
所有APK文件均使用多重签名保护:
|
||||||
|
- ✅ v1 (JAR签名) - 最佳兼容性
|
||||||
|
- ✅ v2 (APK签名方案v2) - 全文件签名
|
||||||
|
- ✅ v3 (APK签名方案v3) - 支持密钥轮换
|
||||||
|
- ✅ v4 (APK签名方案v4) - 增量签名
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "3.0.8",
|
||||||
|
"desc": "XMBOX TV版 v3.0.8 (Android TV/机顶盒专用)\n\n✨ UI优化:\n• 修复按钮点击效果过于明显的问题\n• 统一使用自定义背景替代系统selectableItemBackgroundBorderless\n• 移除Control.Action样式中的文字阴影效果\n• 优化直播页面选择按钮颜色为主题黄色\n• 调整许可协议页面按钮区域上间距为8dp\n• 修复跨类和换源按钮的文字重叠问题\n• 提升整体UI视觉一致性和用户体验\n\n🔧 改进优化:\n• 优化大屏界面体验\n• 提升播放稳定性\n\n📺 专为电视优化:遥控器导航 | 10-foot UI | ARM64/ARMv7",
|
||||||
|
"code": 308,
|
||||||
|
"downloads": {
|
||||||
|
"arm64_v8a": "v3.0.8/leanback-arm64_v8a-v3.0.8.apk",
|
||||||
|
"armeabi_v7a": "v3.0.8/leanback-armeabi_v7a-v3.0.8.apk"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "3.0.8",
|
||||||
|
"desc": "XMBOX 手机版 v3.0.8\n\n✨ UI优化:\n• 修复按钮点击效果过于明显的问题\n• 统一使用自定义背景替代系统selectableItemBackgroundBorderless\n• 移除Control.Action样式中的文字阴影效果\n• 优化直播页面选择按钮颜色为主题黄色\n• 调整许可协议页面按钮区域上间距为8dp\n• 修复跨类和换源按钮的文字重叠问题\n• 提升整体UI视觉一致性和用户体验\n\n🔧 改进优化:\n• 优化内存使用\n• 提升播放稳定性\n\n📱 支持架构:ARM64-v8a | ARMv7a",
|
||||||
|
"code": 308,
|
||||||
|
"downloads": {
|
||||||
|
"arm64_v8a": "v3.0.8/mobile-arm64_v8a-v3.0.8.apk",
|
||||||
|
"armeabi_v7a": "v3.0.8/mobile-armeabi_v7a-v3.0.8.apk"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "3.0.7",
|
||||||
|
"desc": "XMBOX TV版 v3.0.7 (Android TV/机顶盒专用)\n\n✨ UI优化:\n• 全新自定义开关按钮(黄色/黑色Material Design风格)\n• 优化电量百分比显示(16sp字号,2dp间距)\n• 精简设置页面,隐藏壁纸功能\n\n🔒 安全增强:\n• 启用v1/v2/v3/v4多重签名保护\n• 提升应用安全性和兼容性\n\n🔧 改进优化:\n• 修复设置页面崩溃问题\n• 优化大屏界面体验\n• 提升播放稳定性\n\n📺 专为电视优化:遥控器导航 | 10-foot UI | ARM64/ARMv7",
|
||||||
|
"code": 307
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "3.0.7",
|
||||||
|
"desc": "XMBOX 手机版 v3.0.7\n\n✨ UI优化:\n• 全新自定义开关按钮(黄色/黑色Material Design风格)\n• 优化电量百分比显示(16sp字号,2dp间距)\n• 精简设置页面,隐藏壁纸功能\n\n🔒 安全增强:\n• 启用v1/v2/v3/v4多重签名保护\n• 提升应用安全性和兼容性\n\n🔧 改进优化:\n• 修复设置页面崩溃问题\n• 优化内存使用\n• 提升播放稳定性\n\n📱 支持架构:ARM64-v8a | ARMv7a",
|
||||||
|
"code": 307
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,62 @@
|
|||||||
|
# XMBOX Release Files
|
||||||
|
|
||||||
|
## 📁 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
apk/release/
|
||||||
|
├── mobile.json # 最新版本信息 (手机版)
|
||||||
|
├── leanback.json # 最新版本信息 (TV版)
|
||||||
|
├── v3.0.7/ # v3.0.7版本文件
|
||||||
|
│ ├── mobile.json # v3.0.7版本信息
|
||||||
|
│ ├── leanback.json # v3.0.7版本信息
|
||||||
|
│ ├── mobile-arm64_v8a.apk
|
||||||
|
│ ├── mobile-armeabi_v7a.apk
|
||||||
|
│ ├── leanback-arm64_v8a.apk
|
||||||
|
│ └── leanback-armeabi_v7a.apk
|
||||||
|
└── v3.0.8/ # v3.0.8版本文件
|
||||||
|
├── mobile.json # v3.0.8版本信息
|
||||||
|
├── leanback.json # v3.0.8版本信息
|
||||||
|
├── mobile-arm64_v8a-v3.0.8.apk
|
||||||
|
├── mobile-armeabi_v7a-v3.0.8.apk
|
||||||
|
├── leanback-arm64_v8a-v3.0.8.apk
|
||||||
|
└── leanback-armeabi_v7a-v3.0.8.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📱 版本说明
|
||||||
|
|
||||||
|
### v3.0.8 (最新版本)
|
||||||
|
- **发布时间**: 2025-10-14
|
||||||
|
- **版本代码**: 308
|
||||||
|
- **主要更新**: UI交互体验全面优化
|
||||||
|
|
||||||
|
### v3.0.7
|
||||||
|
- **发布时间**: 2025-09-26
|
||||||
|
- **版本代码**: 307
|
||||||
|
- **主要更新**: 全面优化稳定性和用户体验
|
||||||
|
|
||||||
|
## 🔗 下载链接
|
||||||
|
|
||||||
|
### 最新版本 (v3.0.8)
|
||||||
|
- **手机版 ARM64**: [mobile-arm64_v8a-v3.0.8.apk](v3.0.8/mobile-arm64_v8a-v3.0.8.apk)
|
||||||
|
- **手机版 ARMv7**: [mobile-armeabi_v7a-v3.0.8.apk](v3.0.8/mobile-armeabi_v7a-v3.0.8.apk)
|
||||||
|
- **TV版 ARM64**: [leanback-arm64_v8a-v3.0.8.apk](v3.0.8/leanback-arm64_v8a-v3.0.8.apk)
|
||||||
|
- **TV版 ARMv7**: [leanback-armeabi_v7a-v3.0.8.apk](v3.0.8/leanback-armeabi_v7a-v3.0.8.apk)
|
||||||
|
|
||||||
|
### 历史版本
|
||||||
|
- **v3.0.7**: [查看v3.0.7版本文件](v3.0.7/)
|
||||||
|
|
||||||
|
## 📋 版本信息
|
||||||
|
|
||||||
|
每个版本目录都包含对应的JSON配置文件,包含:
|
||||||
|
- `name`: 版本号
|
||||||
|
- `desc`: 版本描述和更新内容
|
||||||
|
- `code`: 版本代码
|
||||||
|
- `downloads`: 下载链接映射 (仅根目录文件)
|
||||||
|
|
||||||
|
## 🔐 签名信息
|
||||||
|
|
||||||
|
所有APK文件均使用多重签名保护:
|
||||||
|
- ✅ v1 (JAR签名) - 最佳兼容性
|
||||||
|
- ✅ v2 (APK签名方案v2) - 全文件签名
|
||||||
|
- ✅ v3 (APK签名方案v3) - 支持密钥轮换
|
||||||
|
- ✅ v4 (APK签名方案v4) - 增量签名
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "3.0.9",
|
||||||
|
"desc": "XMBOX TV版 v3.0.9 (Android TV/机顶盒专用)\n\n✨ 新功能:\n• 新增直播开关控制功能,可隐藏/显示直播tab\n• 新增实时倍速显示功能,播放控制对话框显示当前倍速\n• 优化源管理模块间距动态调整\n\n🎨 UI优化:\n• 滑杆圆球大小优化至20dp直径,提升操作体验\n• 改进滑杆刻度显示,非激活轨道显示刻度\n• 增强播放进度条动态大小调整功能\n• 修复播放进度条圆球跳回问题\n• 完善直播开关逻辑和UI交互\n\n🔧 改进优化:\n• 优化大屏界面体验\n• 提升播放稳定性\n• 增强UI交互体验\n\n📺 专为电视优化:遥控器导航 | 10-foot UI | ARM64/ARMv7",
|
||||||
|
"code": 309,
|
||||||
|
"downloads": {
|
||||||
|
"arm64_v8a": "v3.0.9/leanback-arm64_v8a-v3.0.9.apk",
|
||||||
|
"armeabi_v7a": "v3.0.9/leanback-armeabi_v7a-v3.0.9.apk"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "3.0.9",
|
||||||
|
"desc": "XMBOX 手机版 v3.0.9\n\n✨ 新功能:\n• 新增直播开关控制功能,可隐藏/显示直播tab\n• 新增实时倍速显示功能,播放控制对话框显示当前倍速\n• 优化源管理模块间距动态调整\n\n🎨 UI优化:\n• 滑杆圆球大小优化至20dp直径,提升操作体验\n• 改进滑杆刻度显示,非激活轨道显示刻度\n• 增强播放进度条动态大小调整功能\n• 修复播放进度条圆球跳回问题\n• 完善直播开关逻辑和UI交互\n\n🔧 改进优化:\n• 优化内存使用\n• 提升播放稳定性\n• 增强UI交互体验\n\n📱 支持架构:ARM64-v8a | ARMv7a",
|
||||||
|
"code": 309,
|
||||||
|
"downloads": {
|
||||||
|
"arm64_v8a": "v3.0.9/mobile-arm64_v8a-v3.0.9.apk",
|
||||||
|
"armeabi_v7a": "v3.0.9/mobile-armeabi_v7a-v3.0.9.apk"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "3.0.7",
|
||||||
|
"desc": "XMBOX TV版 v3.0.7 (Android TV/机顶盒专用)\n\n✨ UI优化:\n• 全新自定义开关按钮(黄色/黑色Material Design风格)\n• 优化电量百分比显示(16sp字号,2dp间距)\n• 精简设置页面,隐藏壁纸功能\n\n🔒 安全增强:\n• 启用v1/v2/v3/v4多重签名保护\n• 提升应用安全性和兼容性\n\n🔧 改进优化:\n• 修复设置页面崩溃问题\n• 优化大屏界面体验\n• 提升播放稳定性\n\n📺 专为电视优化:遥控器导航 | 10-foot UI | ARM64/ARMv7",
|
||||||
|
"code": 307
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "3.0.7",
|
||||||
|
"desc": "XMBOX 手机版 v3.0.7\n\n✨ UI优化:\n• 全新自定义开关按钮(黄色/黑色Material Design风格)\n• 优化电量百分比显示(16sp字号,2dp间距)\n• 精简设置页面,隐藏壁纸功能\n\n🔒 安全增强:\n• 启用v1/v2/v3/v4多重签名保护\n• 提升应用安全性和兼容性\n\n🔧 改进优化:\n• 修复设置页面崩溃问题\n• 优化内存使用\n• 提升播放稳定性\n\n📱 支持架构:ARM64-v8a | ARMv7a",
|
||||||
|
"code": 307
|
||||||
|
}
|
||||||
+35
-17
@@ -5,7 +5,7 @@ plugins {
|
|||||||
android {
|
android {
|
||||||
namespace 'com.fongmi.android.tv'
|
namespace 'com.fongmi.android.tv'
|
||||||
|
|
||||||
compileSdk 35
|
compileSdk 36
|
||||||
flavorDimensions = ["mode", "abi"]
|
flavorDimensions = ["mode", "abi"]
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
@@ -14,16 +14,21 @@ android {
|
|||||||
storePassword "xmbox123"
|
storePassword "xmbox123"
|
||||||
keyAlias "xmbox"
|
keyAlias "xmbox"
|
||||||
keyPassword "xmbox123"
|
keyPassword "xmbox123"
|
||||||
|
// 同时启用v1、v2、v3、v4签名以确保最佳兼容性
|
||||||
|
enableV1Signing true
|
||||||
|
enableV2Signing true
|
||||||
|
enableV3Signing true
|
||||||
|
enableV4Signing true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.fongmi.android.tv"
|
applicationId "com.fongmi.android.tv"
|
||||||
minSdk 21
|
minSdk 24
|
||||||
//noinspection ExpiredTargetSdkVersion
|
//noinspection ExpiredTargetSdkVersion
|
||||||
targetSdk 28
|
targetSdk 28
|
||||||
versionCode 300
|
versionCode 309
|
||||||
versionName "3.0.0"
|
versionName "3.0.9"
|
||||||
javaCompileOptions {
|
javaCompileOptions {
|
||||||
annotationProcessorOptions {
|
annotationProcessorOptions {
|
||||||
arguments = ["room.schemaLocation": "$projectDir/schemas".toString(), "eventBusIndex": "com.fongmi.android.tv.event.EventIndex"]
|
arguments = ["room.schemaLocation": "$projectDir/schemas".toString(), "eventBusIndex": "com.fongmi.android.tv.event.EventIndex"]
|
||||||
@@ -67,6 +72,17 @@ android {
|
|||||||
exclude 'META-INF/beans.xml'
|
exclude 'META-INF/beans.xml'
|
||||||
exclude 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'
|
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 ->
|
android.applicationVariants.configureEach { variant ->
|
||||||
@@ -87,15 +103,15 @@ android {
|
|||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
coreLibraryDesugaringEnabled true
|
coreLibraryDesugaringEnabled true
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: "libs", include: ["*.aar"])
|
implementation fileTree(dir: "libs", include: ["*.aar"])
|
||||||
implementation project(':catvod')
|
implementation project(':catvod')
|
||||||
//implementation project(':chaquo')
|
// implementation project(':chaquo') // 移除Python支持减少8-10MB体积
|
||||||
implementation project(':quickjs')
|
implementation project(':quickjs')
|
||||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||||
implementation 'androidx.media:media:1.7.0'
|
implementation 'androidx.media:media:1.7.0'
|
||||||
@@ -114,9 +130,9 @@ dependencies {
|
|||||||
implementation 'androidx.media3:media3-exoplayer-smoothstreaming:' + media3Version
|
implementation 'androidx.media3:media3-exoplayer-smoothstreaming:' + media3Version
|
||||||
implementation 'androidx.media3:media3-extractor:' + media3Version
|
implementation 'androidx.media3:media3-extractor:' + media3Version
|
||||||
implementation 'androidx.media3:media3-ui:' + media3Version
|
implementation 'androidx.media3:media3-ui:' + media3Version
|
||||||
implementation 'androidx.room:room-runtime:2.7.1'
|
implementation 'androidx.room:room-runtime:2.6.1'
|
||||||
implementation 'cat.ereza:customactivityoncrash:2.4.0'
|
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.bassaer:materialdesigncolors:1.0.0'
|
||||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||||
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||||
@@ -124,12 +140,12 @@ dependencies {
|
|||||||
implementation 'com.github.bumptech.glide:okhttp3-integration:4.16.0'
|
implementation 'com.github.bumptech.glide:okhttp3-integration:4.16.0'
|
||||||
implementation 'com.github.jahirfiquitiva:TextDrawable:1.0.3'
|
implementation 'com.github.jahirfiquitiva:TextDrawable:1.0.3'
|
||||||
implementation 'com.github.thegrizzlylabs:sardine-android:0.9'
|
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.android.material:material:1.11.0'
|
||||||
implementation 'com.google.zxing:core:3.5.3'
|
implementation 'com.google.zxing:core:3.5.3'
|
||||||
implementation 'com.guolindev.permissionx:permissionx:1.8.0'
|
implementation 'com.guolindev.permissionx:permissionx:1.7.1'
|
||||||
implementation 'com.hierynomus:smbj:0.14.0'
|
implementation 'com.hierynomus:smbj:0.13.0'
|
||||||
implementation 'io.antmedia:rtmp-client:3.2.0'
|
implementation 'io.antmedia:rtmp-client:3.1.0'
|
||||||
implementation 'javax.servlet:javax.servlet-api:3.1.0'
|
implementation 'javax.servlet:javax.servlet-api:3.1.0'
|
||||||
implementation 'org.aomedia.avif.android:avif:1.1.1.14d8e3c4'
|
implementation 'org.aomedia.avif.android:avif:1.1.1.14d8e3c4'
|
||||||
implementation 'org.eclipse.jetty:jetty-client:8.1.21.v20160908'
|
implementation 'org.eclipse.jetty:jetty-client:8.1.21.v20160908'
|
||||||
@@ -146,8 +162,10 @@ dependencies {
|
|||||||
mobileImplementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
mobileImplementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
mobileImplementation 'com.google.android.flexbox:flexbox:3.0.0'
|
mobileImplementation 'com.google.android.flexbox:flexbox:3.0.0'
|
||||||
mobileImplementation('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false }
|
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.6.1'
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
// annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||||
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
|
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.1.4'
|
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'
|
||||||
}
|
}
|
||||||
Vendored
+5
-3
@@ -59,12 +59,14 @@
|
|||||||
-keep class fi.iki.elonen.** { *; }
|
-keep class fi.iki.elonen.** { *; }
|
||||||
|
|
||||||
# NewPipeExtractor
|
# 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.** { *; }
|
-keep class org.mozilla.javascript.** { *; }
|
||||||
-keep class org.mozilla.javascript.engine.** { *; }
|
-keep class org.mozilla.javascript.engine.** { *; }
|
||||||
-keep class javax.script.** { *; }
|
-keep class org.mozilla.classfile.ClassFileWriter
|
||||||
-keep class jdk.dynalink.** { *; }
|
-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.JavaToJSONConverters
|
||||||
-dontwarn org.mozilla.javascript.tools.**
|
-dontwarn org.mozilla.javascript.tools.**
|
||||||
-dontwarn javax.script.**
|
-dontwarn javax.script.**
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package com.fongmi.android.tv;
|
package com.fongmi.android.tv;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.fongmi.android.tv.App;
|
||||||
import com.fongmi.android.tv.databinding.DialogUpdateBinding;
|
import com.fongmi.android.tv.databinding.DialogUpdateBinding;
|
||||||
import com.fongmi.android.tv.utils.Download;
|
import com.fongmi.android.tv.utils.Download;
|
||||||
import com.fongmi.android.tv.utils.FileUtil;
|
import com.fongmi.android.tv.utils.FileUtil;
|
||||||
@@ -13,6 +16,7 @@ import com.fongmi.android.tv.utils.Notify;
|
|||||||
import com.fongmi.android.tv.utils.ResUtil;
|
import com.fongmi.android.tv.utils.ResUtil;
|
||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
import com.github.catvod.utils.Github;
|
import com.github.catvod.utils.Github;
|
||||||
|
import com.github.catvod.utils.Logger;
|
||||||
import com.github.catvod.utils.Path;
|
import com.github.catvod.utils.Path;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
@@ -27,9 +31,10 @@ public class Updater implements Download.Callback {
|
|||||||
private final Download download;
|
private final Download download;
|
||||||
private AlertDialog dialog;
|
private AlertDialog dialog;
|
||||||
private boolean dev;
|
private boolean dev;
|
||||||
|
private boolean forceCheck; // 是否为手动检查
|
||||||
|
|
||||||
private File getFile() {
|
private File getFile() {
|
||||||
return Path.cache("update.apk");
|
return Path.root("Download", "XMBOX-update.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getJson() {
|
private String getJson() {
|
||||||
@@ -37,6 +42,26 @@ public class Updater implements Download.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getApk() {
|
private String getApk() {
|
||||||
|
// 使用JSON中指定的具体下载路径
|
||||||
|
try {
|
||||||
|
String response = OkHttp.string(getJson());
|
||||||
|
JSONObject object = new JSONObject(response);
|
||||||
|
JSONObject downloads = object.optJSONObject("downloads");
|
||||||
|
if (downloads != null) {
|
||||||
|
String abi = BuildConfig.FLAVOR_abi;
|
||||||
|
String downloadPath = downloads.optString(abi);
|
||||||
|
if (!downloadPath.isEmpty()) {
|
||||||
|
// 直接构建完整URL,不通过Github.getApk()避免重复添加路径
|
||||||
|
String baseUrl = Github.useCnMirror() ?
|
||||||
|
"https://gitee.com/ochenoktochen/XMBOX-Release/raw/main" :
|
||||||
|
"https://raw.githubusercontent.com/Tosencen/XMBOX-Release/main";
|
||||||
|
return baseUrl + "/apk/" + (dev ? "dev" : "release") + "/" + downloadPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("Failed to get download path from JSON: " + e.getMessage());
|
||||||
|
}
|
||||||
|
// 回退到原来的方式
|
||||||
return Github.getApk(dev, BuildConfig.FLAVOR_mode + "-" + BuildConfig.FLAVOR_abi);
|
return Github.getApk(dev, BuildConfig.FLAVOR_mode + "-" + BuildConfig.FLAVOR_abi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,11 +71,13 @@ public class Updater implements Download.Callback {
|
|||||||
|
|
||||||
public Updater() {
|
public Updater() {
|
||||||
this.download = Download.create(getApk(), getFile(), this);
|
this.download = Download.create(getApk(), getFile(), this);
|
||||||
|
this.forceCheck = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Updater force() {
|
public Updater force() {
|
||||||
Notify.show(R.string.update_check);
|
Notify.show(R.string.update_check);
|
||||||
Setting.putUpdate(true);
|
Setting.putUpdate(true);
|
||||||
|
this.forceCheck = true; // 标记为手动检查
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,13 +106,82 @@ public class Updater implements Download.Callback {
|
|||||||
|
|
||||||
private void doInBackground(Activity activity) {
|
private void doInBackground(Activity activity) {
|
||||||
try {
|
try {
|
||||||
JSONObject object = new JSONObject(OkHttp.string(getJson()));
|
// 直接使用GitHub Releases API检测最新版本
|
||||||
String name = object.optString("name");
|
String releasesUrl = "https://api.github.com/repos/Tosencen/XMBOX/releases/latest";
|
||||||
String desc = object.optString("desc");
|
String response = OkHttp.string(releasesUrl);
|
||||||
int code = object.optInt("code");
|
|
||||||
if (need(code, name)) App.post(() -> show(activity, name, desc));
|
// 检查响应是否包含错误信息
|
||||||
|
if (response.contains("rate limit exceeded")) {
|
||||||
|
if (forceCheck) {
|
||||||
|
App.post(() -> Notify.show("检查更新失败:API请求过于频繁,请稍后重试"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.contains("Not Found") || response.contains("404")) {
|
||||||
|
if (forceCheck) {
|
||||||
|
App.post(() -> Notify.show("检查更新失败:更新服务暂时不可用"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject release = new JSONObject(response);
|
||||||
|
String tagName = release.optString("tag_name");
|
||||||
|
String body = release.optString("body");
|
||||||
|
|
||||||
|
// 提取版本号(去掉v前缀)
|
||||||
|
String version = tagName.startsWith("v") ? tagName.substring(1) : tagName;
|
||||||
|
|
||||||
|
if (needUpdate(version)) {
|
||||||
|
App.post(() -> show(activity, version, body));
|
||||||
|
} else {
|
||||||
|
if (forceCheck) {
|
||||||
|
App.post(() -> Notify.show("已是最新版本 " + version));
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean needUpdate(String remoteVersion) {
|
||||||
|
if (!Setting.getUpdate()) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 简单的版本号比较,假设版本格式为 x.y.z
|
||||||
|
String[] remoteParts = remoteVersion.split("\\.");
|
||||||
|
String[] localParts = BuildConfig.VERSION_NAME.split("\\.");
|
||||||
|
|
||||||
|
// 确保两个版本号都有足够的段
|
||||||
|
int maxLength = Math.max(remoteParts.length, localParts.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < maxLength; i++) {
|
||||||
|
int remotePart = i < remoteParts.length ? Integer.parseInt(remoteParts[i]) : 0;
|
||||||
|
int localPart = i < localParts.length ? Integer.parseInt(localParts[i]) : 0;
|
||||||
|
|
||||||
|
if (remotePart > localPart) {
|
||||||
|
return true;
|
||||||
|
} else if (remotePart < localPart) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false; // 版本相同
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("Updater: Version comparison error: " + e.getMessage());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +205,31 @@ public class Updater implements Download.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void confirm(View view) {
|
private void confirm(View view) {
|
||||||
binding.confirm.setEnabled(false);
|
// 跳转到GitHub Releases页面而不是直接下载
|
||||||
download.start();
|
try {
|
||||||
|
String url = "https://github.com/Tosencen/XMBOX/releases/tag/v3.0.8";
|
||||||
|
Logger.d("Updater: Attempting to open URL: " + url);
|
||||||
|
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(url));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|
||||||
|
// 检查是否有应用可以处理这个Intent
|
||||||
|
if (intent.resolveActivity(App.get().getPackageManager()) != null) {
|
||||||
|
App.get().startActivity(intent);
|
||||||
|
Logger.d("Updater: Successfully started browser intent");
|
||||||
|
dismiss();
|
||||||
|
} else {
|
||||||
|
Logger.e("Updater: No app can handle the URL");
|
||||||
|
Notify.show("没有找到可以打开链接的应用,请手动访问GitHub下载");
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("Updater: Failed to open GitHub releases page: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
Notify.show("无法打开更新页面,请手动访问GitHub下载");
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismiss() {
|
private void dismiss() {
|
||||||
|
|||||||
+21
-2
@@ -1,5 +1,9 @@
|
|||||||
package com.fongmi.android.tv.ui.activity;
|
package com.fongmi.android.tv.ui.activity;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.widget.Toast;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.viewbinding.ViewBinding;
|
import androidx.viewbinding.ViewBinding;
|
||||||
|
|
||||||
@@ -14,22 +18,37 @@ import cat.ereza.customactivityoncrash.CustomActivityOnCrash;
|
|||||||
public class CrashActivity extends BaseActivity {
|
public class CrashActivity extends BaseActivity {
|
||||||
|
|
||||||
private ActivityCrashBinding mBinding;
|
private ActivityCrashBinding mBinding;
|
||||||
|
private String errorDetails;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ViewBinding getBinding() {
|
protected ViewBinding getBinding() {
|
||||||
return mBinding = ActivityCrashBinding.inflate(getLayoutInflater());
|
return mBinding = ActivityCrashBinding.inflate(getLayoutInflater());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initView() {
|
||||||
|
errorDetails = CustomActivityOnCrash.getAllErrorDetailsFromIntent(this, getIntent());
|
||||||
|
mBinding.error.setText(errorDetails);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initEvent() {
|
protected void initEvent() {
|
||||||
mBinding.copy.setOnClickListener(v -> showError());
|
mBinding.copy.setOnClickListener(v -> copyErrorToClipboard());
|
||||||
mBinding.restart.setOnClickListener(v -> CustomActivityOnCrash.restartApplication(this, Objects.requireNonNull(CustomActivityOnCrash.getConfigFromIntent(getIntent()))));
|
mBinding.restart.setOnClickListener(v -> CustomActivityOnCrash.restartApplication(this, Objects.requireNonNull(CustomActivityOnCrash.getConfigFromIntent(getIntent()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copyErrorToClipboard() {
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip = ClipData.newPlainText(getString(R.string.crash_details_title), errorDetails);
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||||
|
showError();
|
||||||
|
}
|
||||||
|
|
||||||
private void showError() {
|
private void showError() {
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.crash_details_title)
|
.setTitle(R.string.crash_details_title)
|
||||||
.setMessage(CustomActivityOnCrash.getAllErrorDetailsFromIntent(this, getIntent()))
|
.setMessage(errorDetails)
|
||||||
.setPositiveButton(R.string.crash_details_close, null)
|
.setPositiveButton(R.string.crash_details_close, null)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
@@ -47,6 +47,7 @@ import com.fongmi.android.tv.ui.base.BaseActivity;
|
|||||||
import com.fongmi.android.tv.ui.custom.CustomRowPresenter;
|
import com.fongmi.android.tv.ui.custom.CustomRowPresenter;
|
||||||
import com.fongmi.android.tv.ui.custom.CustomSelector;
|
import com.fongmi.android.tv.ui.custom.CustomSelector;
|
||||||
import com.fongmi.android.tv.ui.custom.CustomTitleView;
|
import com.fongmi.android.tv.ui.custom.CustomTitleView;
|
||||||
|
import com.fongmi.android.tv.ui.dialog.LastWatchToast;
|
||||||
import com.fongmi.android.tv.ui.dialog.SiteDialog;
|
import com.fongmi.android.tv.ui.dialog.SiteDialog;
|
||||||
import com.fongmi.android.tv.ui.presenter.FuncPresenter;
|
import com.fongmi.android.tv.ui.presenter.FuncPresenter;
|
||||||
import com.fongmi.android.tv.ui.presenter.HeaderPresenter;
|
import com.fongmi.android.tv.ui.presenter.HeaderPresenter;
|
||||||
@@ -252,6 +253,16 @@ public class HomeActivity extends BaseActivity implements CustomTitleView.Listen
|
|||||||
if ((items.isEmpty() && exist) || (renew && exist)) mAdapter.removeItems(historyIndex, 1);
|
if ((items.isEmpty() && exist) || (renew && exist)) mAdapter.removeItems(historyIndex, 1);
|
||||||
if ((!items.isEmpty() && !exist) || (renew && exist)) mAdapter.add(historyIndex, new ListRow(mHistoryAdapter));
|
if ((!items.isEmpty() && !exist) || (renew && exist)) mAdapter.add(historyIndex, new ListRow(mHistoryAdapter));
|
||||||
mHistoryAdapter.setItems(items, null);
|
mHistoryAdapter.setItems(items, null);
|
||||||
|
|
||||||
|
// 显示上次播放弹窗
|
||||||
|
checkLastWatchDialog(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkLastWatchDialog(List<History> items) {
|
||||||
|
if (!items.isEmpty() && App.isAppJustLaunched()) {
|
||||||
|
App.setAppLaunched();
|
||||||
|
LastWatchToast.create(this, items.get(0)).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setHistoryDelete(boolean delete) {
|
private void setHistoryDelete(boolean delete) {
|
||||||
|
|||||||
@@ -99,8 +99,17 @@ public class SettingActivity extends BaseActivity implements ConfigCallback, Sit
|
|||||||
mBinding.dohText.setText(getDohList()[getDohIndex()]);
|
mBinding.dohText.setText(getDohList()[getDohIndex()]);
|
||||||
mBinding.proxyText.setText(getProxy(Setting.getProxy()));
|
mBinding.proxyText.setText(getProxy(Setting.getProxy()));
|
||||||
mBinding.incognitoText.setText(getSwitch(Setting.isIncognito()));
|
mBinding.incognitoText.setText(getSwitch(Setting.isIncognito()));
|
||||||
|
mBinding.liveTabVisibleText.setText(getSwitch(Setting.isLiveTabVisible()));
|
||||||
mBinding.sizeText.setText((size = ResUtil.getStringArray(R.array.select_size))[Setting.getSize()]);
|
mBinding.sizeText.setText((size = ResUtil.getStringArray(R.array.select_size))[Setting.getSize()]);
|
||||||
mBinding.qualityText.setText((quality = ResUtil.getStringArray(R.array.select_quality))[Setting.getQuality()]);
|
mBinding.qualityText.setText((quality = ResUtil.getStringArray(R.array.select_quality))[Setting.getQuality()]);
|
||||||
|
setLiveSettingsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLiveSettingsVisibility() {
|
||||||
|
boolean isLiveTabVisible = !Setting.isLiveTabVisible(); // 注意:这里取反,因为开关是"隐藏直播"
|
||||||
|
mBinding.live.setVisibility(isLiveTabVisible ? View.VISIBLE : View.GONE);
|
||||||
|
mBinding.liveHome.setVisibility(isLiveTabVisible ? View.VISIBLE : View.GONE);
|
||||||
|
mBinding.liveHistory.setVisibility(isLiveTabVisible ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCacheText() {
|
private void setCacheText() {
|
||||||
@@ -134,6 +143,7 @@ public class SettingActivity extends BaseActivity implements ConfigCallback, Sit
|
|||||||
mBinding.wallDefault.setOnClickListener(this::setWallDefault);
|
mBinding.wallDefault.setOnClickListener(this::setWallDefault);
|
||||||
mBinding.wallRefresh.setOnClickListener(this::setWallRefresh);
|
mBinding.wallRefresh.setOnClickListener(this::setWallRefresh);
|
||||||
mBinding.incognito.setOnClickListener(this::setIncognito);
|
mBinding.incognito.setOnClickListener(this::setIncognito);
|
||||||
|
mBinding.liveTabVisible.setOnClickListener(this::setLiveTabVisible);
|
||||||
mBinding.quality.setOnClickListener(this::setQuality);
|
mBinding.quality.setOnClickListener(this::setQuality);
|
||||||
mBinding.size.setOnClickListener(this::setSize);
|
mBinding.size.setOnClickListener(this::setSize);
|
||||||
mBinding.doh.setOnClickListener(this::setDoh);
|
mBinding.doh.setOnClickListener(this::setDoh);
|
||||||
@@ -304,6 +314,15 @@ public class SettingActivity extends BaseActivity implements ConfigCallback, Sit
|
|||||||
mBinding.incognitoText.setText(getSwitch(Setting.isIncognito()));
|
mBinding.incognitoText.setText(getSwitch(Setting.isIncognito()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setLiveTabVisible(View view) {
|
||||||
|
Setting.putLiveTabVisible(!Setting.isLiveTabVisible());
|
||||||
|
mBinding.liveTabVisibleText.setText(getSwitch(Setting.isLiveTabVisible()));
|
||||||
|
// 发送刷新事件,通知主界面更新导航栏
|
||||||
|
RefreshEvent.config();
|
||||||
|
// 更新直播设置项的可见性
|
||||||
|
setLiveSettingsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
private void setQuality(View view) {
|
private void setQuality(View view) {
|
||||||
int index = Setting.getQuality();
|
int index = Setting.getQuality();
|
||||||
Setting.putQuality(index = index == quality.length - 1 ? 0 : ++index);
|
Setting.putQuality(index = index == quality.length - 1 ? 0 : ++index);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||||
import androidx.leanback.widget.ItemBridgeAdapter;
|
import androidx.leanback.widget.ItemBridgeAdapter;
|
||||||
@@ -427,7 +428,10 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
|
|||||||
private void setDetail(Result result) {
|
private void setDetail(Result result) {
|
||||||
if (result.getList().isEmpty()) setEmpty(result.hasMsg());
|
if (result.getList().isEmpty()) setEmpty(result.hasMsg());
|
||||||
else setDetail(result.getList().get(0));
|
else setDetail(result.getList().get(0));
|
||||||
Notify.show(result.getMsg());
|
// 只在有错误或重要消息时显示提示
|
||||||
|
if (result.hasMsg() && result.getList().isEmpty()) {
|
||||||
|
Notify.show(result.getMsg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setEmpty(boolean finish) {
|
private void setEmpty(boolean finish) {
|
||||||
@@ -481,7 +485,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
|
|||||||
private void setText(TextView view, int resId, String text) {
|
private void setText(TextView view, int resId, String text) {
|
||||||
view.setText(getSpan(resId, text), TextView.BufferType.SPANNABLE);
|
view.setText(getSpan(resId, text), TextView.BufferType.SPANNABLE);
|
||||||
view.setVisibility(text.isEmpty() ? View.GONE : View.VISIBLE);
|
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);
|
CustomMovement.bind(view);
|
||||||
view.setTag(text);
|
view.setTag(text);
|
||||||
}
|
}
|
||||||
@@ -1224,7 +1228,10 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
|
|||||||
private void nextSite() {
|
private void nextSite() {
|
||||||
if (mQuickAdapter.size() == 0) return;
|
if (mQuickAdapter.size() == 0) return;
|
||||||
Vod item = (Vod) mQuickAdapter.get(0);
|
Vod item = (Vod) mQuickAdapter.get(0);
|
||||||
Notify.show(getString(R.string.play_switch_site, item.getSiteName()));
|
// 只在真正需要切换时显示提示(即当前站点已经失败的情况下)
|
||||||
|
if (mBroken.contains(getId())) {
|
||||||
|
Notify.show(getString(R.string.play_switch_site, item.getSiteName()));
|
||||||
|
}
|
||||||
mQuickAdapter.removeItems(0, 1);
|
mQuickAdapter.removeItems(0, 1);
|
||||||
mBroken.add(getId());
|
mBroken.add(getId());
|
||||||
setInitAuto(false);
|
setInitAuto(false);
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import android.view.LayoutInflater;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
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.databinding.DialogDescBinding;
|
||||||
import com.fongmi.android.tv.ui.custom.CustomMovement;
|
import com.fongmi.android.tv.ui.custom.CustomMovement;
|
||||||
import com.github.bassaer.library.MDColor;
|
import com.github.bassaer.library.MDColor;
|
||||||
@@ -21,13 +23,13 @@ public class DescDialog {
|
|||||||
DialogDescBinding binding = DialogDescBinding.inflate(LayoutInflater.from(activity));
|
DialogDescBinding binding = DialogDescBinding.inflate(LayoutInflater.from(activity));
|
||||||
AlertDialog dialog = new MaterialAlertDialogBuilder(activity).setView(binding.getRoot()).create();
|
AlertDialog dialog = new MaterialAlertDialogBuilder(activity).setView(binding.getRoot()).create();
|
||||||
dialog.getWindow().setDimAmount(0);
|
dialog.getWindow().setDimAmount(0);
|
||||||
initView(binding.text, desc);
|
initView(binding.text, desc, activity);
|
||||||
dialog.show();
|
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.setText(desc, TextView.BufferType.SPANNABLE);
|
||||||
view.setLinkTextColor(MDColor.BLUE_500);
|
view.setLinkTextColor(ContextCompat.getColor(activity, R.color.primary));
|
||||||
CustomMovement.bind(view);
|
CustomMovement.bind(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public class SiteDialog implements SiteAdapter.OnClickListener {
|
|||||||
setType(type);
|
setType(type);
|
||||||
initView();
|
initView();
|
||||||
initEvent();
|
initEvent();
|
||||||
|
setDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean list() {
|
private boolean list() {
|
||||||
@@ -94,7 +95,13 @@ public class SiteDialog implements SiteAdapter.OnClickListener {
|
|||||||
if (decoration != null) binding.recycler.removeItemDecoration(decoration);
|
if (decoration != null) binding.recycler.removeItemDecoration(decoration);
|
||||||
binding.recycler.addItemDecoration(decoration = new SpaceItemDecoration(getCount(), 16));
|
binding.recycler.addItemDecoration(decoration = new SpaceItemDecoration(getCount(), 16));
|
||||||
binding.recycler.setLayoutManager(new GridLayoutManager(dialog.getContext(), getCount()));
|
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() {
|
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>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<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/primary" 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_selected="true" />
|
||||||
<item android:color="@color/white" />
|
<item android:color="@color/white" />
|
||||||
</selector>
|
</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>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:color="@color/white" android:state_focused="true" android:state_selected="true" />
|
<item android:color="@color/white" android:state_focused="true" android:state_selected="true" />
|
||||||
<item android:color="@color/green_400" android:state_selected="true" />
|
<item android:color="@color/primary" android:state_selected="true" />
|
||||||
<item android:color="@color/white" />
|
<item android:color="@color/white" />
|
||||||
</selector>
|
</selector>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/white_90" />
|
<solid android:color="@color/black_90" />
|
||||||
|
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="12dp"
|
android:topLeftRadius="12dp"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/white_90" />
|
<solid android:color="@color/black_80" />
|
||||||
|
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="8dp"
|
android:topLeftRadius="8dp"
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/green_400" />
|
<solid android:color="@color/primary" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<solid android:color="@color/black_20" />
|
<solid android:color="@color/black_20" />
|
||||||
|
|
||||||
<corners android:radius="4dp" />
|
<corners android:radius="12dp" />
|
||||||
|
|
||||||
<padding
|
<padding
|
||||||
android:bottom="8dp"
|
android:bottom="8dp"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/grey_600" />
|
<solid android:color="@color/primary" />
|
||||||
|
|
||||||
<corners android:radius="4dp" />
|
<corners android:radius="4dp" />
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/grey_800" />
|
<solid android:color="@color/primary" />
|
||||||
|
|
||||||
<corners android:radius="4dp" />
|
<corners android:radius="4dp" />
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/grey_600" />
|
<solid android:color="@color/black_60" />
|
||||||
|
|
||||||
<corners android:radius="4dp" />
|
<corners android:radius="4dp" />
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/black_40" />
|
<solid android:color="@color/black_90" />
|
||||||
|
|
||||||
<corners android:radius="8dp" />
|
<corners android:radius="8dp" />
|
||||||
|
|
||||||
|
|||||||
@@ -265,6 +265,36 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/liveTabVisible"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/selector_item"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:nextFocusDown="@id/quality"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/setting_live_tab_visible"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/liveTabVisibleText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18sp"
|
||||||
|
tools:text="On" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
tools:text="https://fongmi.github.io/cat.json" />
|
tools:text="https://fongmi.github.io/cat.json" />
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="愛奇異彈幕" />
|
tools:text="愛奇異彈幕" />
|
||||||
@@ -9,6 +9,6 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
tools:text="Google" />
|
tools:text="Google" />
|
||||||
@@ -12,6 +12,6 @@
|
|||||||
android:nextFocusUp="@id/flag"
|
android:nextFocusUp="@id/flag"
|
||||||
android:nextFocusDown="@id/array"
|
android:nextFocusDown="@id/array"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/episode_text"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
tools:text="20" />
|
tools:text="20" />
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
tools:text="https://fongmi.github.io/live.json" />
|
tools:text="https://fongmi.github.io/live.json" />
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,6 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="解析" />
|
tools:text="解析" />
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/green_a_400"
|
android:textColor="@color/primary"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="泥巴"
|
tools:text="泥巴"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
tools:text="tv-2024-12-26.bk.gz" />
|
tools:text="tv-2024-12-26.bk.gz" />
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
|
android:duplicateParentState="true"
|
||||||
tools:text="泥巴" />
|
tools:text="泥巴" />
|
||||||
|
|
||||||
<com.google.android.material.checkbox.MaterialCheckBox
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="中文、哥斯拉.srt" />
|
tools:text="中文、哥斯拉.srt" />
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="48dp">
|
android:padding="48dp">
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
<com.google.android.material.slider.Slider
|
||||||
@@ -12,9 +13,12 @@
|
|||||||
android:stepSize="1"
|
android:stepSize="1"
|
||||||
android:valueFrom="1"
|
android:valueFrom="1"
|
||||||
android:valueTo="10"
|
android:valueTo="10"
|
||||||
app:thumbColor="@color/yellow_500"
|
app:thumbColor="@color/primary"
|
||||||
app:tickVisible="false"
|
app:thumbRadius="10dp"
|
||||||
app:trackColorActive="@color/yellow_500"
|
app:tickVisible="true"
|
||||||
app:trackColorInactive="@color/yellow_50" />
|
app:tickColor="@color/black_50"
|
||||||
|
app:trackColorActive="@color/primary"
|
||||||
|
app:trackColorInactive="@color/white_20"
|
||||||
|
app:trackHeight="4dp" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
android:maxLines="3"
|
android:maxLines="3"
|
||||||
android:paddingStart="4dp"
|
android:paddingStart="4dp"
|
||||||
android:paddingEnd="4dp"
|
android:paddingEnd="4dp"
|
||||||
android:textColor="@color/grey_700"
|
android:textColor="@color/white"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
tools:text="@string/push_info" />
|
tools:text="@string/push_info" />
|
||||||
|
|
||||||
@@ -41,11 +42,13 @@
|
|||||||
android:layout_alignStart="@+id/info"
|
android:layout_alignStart="@+id/info"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:hint="@string/dialog_config_hint"
|
android:hint="@string/dialog_config_hint"
|
||||||
|
android:textColorHint="@color/white_50"
|
||||||
android:imeOptions="actionDone"
|
android:imeOptions="actionDone"
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:nextFocusDown="@id/positive"
|
android:nextFocusDown="@id/positive"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
android:textSize="18sp" />
|
android:textSize="18sp" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -68,7 +71,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/setting_choose"
|
android:text="@string/setting_choose"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -83,7 +86,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/dialog_positive"
|
android:text="@string/dialog_positive"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -97,7 +100,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/dialog_negative"
|
android:text="@string/dialog_negative"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/danmaku_select"
|
android:text="@string/danmaku_select"
|
||||||
android:textColor="@color/grey_900"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:fillViewport="true">
|
android:fillViewport="true">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
android:letterSpacing="0.05"
|
android:letterSpacing="0.05"
|
||||||
android:lineSpacingExtra="8dp"
|
android:lineSpacingExtra="8dp"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:textColor="@color/grey_800"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
android:id="@+id/recycler"
|
android:id="@+id/recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:maxHeight="296dp"
|
app:maxHeight="296dp"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
android:id="@+id/recycler"
|
android:id="@+id/recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:maxHeight="352dp" />
|
app:maxHeight="352dp" />
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
android:id="@+id/recycler"
|
android:id="@+id/recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:maxHeight="296dp"
|
app:maxHeight="296dp"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@@ -35,11 +36,13 @@
|
|||||||
android:layout_alignStart="@+id/info"
|
android:layout_alignStart="@+id/info"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:hint="socks5://127.0.0.1:9978"
|
android:hint="socks5://127.0.0.1:9978"
|
||||||
|
android:textColorHint="@color/white_50"
|
||||||
android:imeOptions="actionDone"
|
android:imeOptions="actionDone"
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:nextFocusDown="@id/positive"
|
android:nextFocusDown="@id/positive"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
android:textSize="18sp" />
|
android:textSize="18sp" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -62,7 +65,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/dialog_positive"
|
android:text="@string/dialog_positive"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -76,7 +79,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/dialog_negative"
|
android:text="@string/dialog_negative"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
android:id="@+id/recycler"
|
android:id="@+id/recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:maxHeight="352dp" />
|
app:maxHeight="352dp" />
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="48dp">
|
android:padding="48dp">
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
<com.google.android.material.slider.Slider
|
||||||
@@ -12,9 +13,12 @@
|
|||||||
android:stepSize="0.5"
|
android:stepSize="0.5"
|
||||||
android:valueFrom="2"
|
android:valueFrom="2"
|
||||||
android:valueTo="5"
|
android:valueTo="5"
|
||||||
app:thumbColor="@color/yellow_500"
|
app:thumbColor="@color/primary"
|
||||||
app:tickVisible="false"
|
app:thumbRadius="10dp"
|
||||||
app:trackColorActive="@color/yellow_500"
|
app:tickVisible="true"
|
||||||
app:trackColorInactive="@color/yellow_50" />
|
app:tickColor="@color/black_50"
|
||||||
|
app:trackColorActive="@color/primary"
|
||||||
|
app:trackColorInactive="@color/white_20"
|
||||||
|
app:trackHeight="4dp" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -18,7 +19,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textColor="@color/grey_900"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
tools:text="選擇字幕" />
|
tools:text="選擇字幕" />
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@@ -35,11 +36,13 @@
|
|||||||
android:layout_alignStart="@+id/info"
|
android:layout_alignStart="@+id/info"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:hint="@string/player_ua"
|
android:hint="@string/player_ua"
|
||||||
|
android:textColorHint="@color/white_50"
|
||||||
android:imeOptions="actionDone"
|
android:imeOptions="actionDone"
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:nextFocusDown="@id/positive"
|
android:nextFocusDown="@id/positive"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
android:textSize="18sp" />
|
android:textSize="18sp" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -62,7 +65,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/dialog_positive"
|
android:text="@string/dialog_positive"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -76,7 +79,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/dialog_negative"
|
android:text="@string/dialog_negative"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black_90"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="24dp">
|
android:padding="24dp">
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:letterSpacing="0.02"
|
android:letterSpacing="0.02"
|
||||||
android:textColor="@color/grey_900"
|
android:textColor="@color/white"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
tools:text="@string/update_version" />
|
tools:text="@string/update_version" />
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:letterSpacing="0.02"
|
android:letterSpacing="0.02"
|
||||||
android:lineSpacingExtra="8dp"
|
android:lineSpacingExtra="8dp"
|
||||||
android:textColor="@color/grey_900"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
tools:text="1. 新增 ffmpeg 音頻軟解\n2. 詳情頁新增分詞快搜\n3. 修復搜尋閃退問題\n4. 設定支援渲染切換" />
|
tools:text="1. 新增 ffmpeg 音頻軟解\n2. 詳情頁新增分詞快搜\n3. 修復搜尋閃退問題\n4. 設定支援渲染切換" />
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/update_confirm"
|
android:text="@string/update_confirm"
|
||||||
android:textColor="@color/white" />
|
android:textColor="@color/button_text" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/cancel"
|
android:id="@+id/cancel"
|
||||||
@@ -68,7 +69,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/dialog_negative"
|
android:text="@string/dialog_negative"
|
||||||
android:textColor="@color/white" />
|
android:textColor="@color/button_text" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusLeft="@id/video"
|
android:nextFocusLeft="@id/video"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="刷新" />
|
tools:text="刷新" />
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:text="@string/play_exo"
|
android:text="@string/play_exo"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="硬解" />
|
tools:text="硬解" />
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="1.00" />
|
tools:text="1.00" />
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="預設" />
|
tools:text="預設" />
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:tag="3"
|
android:tag="3"
|
||||||
android:text="@string/play_track_text"
|
android:text="@string/play_track_text"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:tag="1"
|
android:tag="1"
|
||||||
android:text="@string/play_track_audio"
|
android:text="@string/play_track_audio"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:tag="2"
|
android:tag="2"
|
||||||
android:text="@string/play_track_video"
|
android:text="@string/play_track_video"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusLeft="@id/change"
|
android:nextFocusLeft="@id/change"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="首頁" />
|
tools:text="首頁" />
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:text="@string/play"
|
android:text="@string/play"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:text="@string/play_exo"
|
android:text="@string/play_exo"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
android:background="@drawable/selector_text"
|
android:background="@drawable/selector_text"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="硬解" />
|
tools:text="硬解" />
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
android:background="@drawable/selector_text"
|
android:background="@drawable/selector_text"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:text="1.00"
|
tools:text="1.00"
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
android:background="@drawable/selector_text"
|
android:background="@drawable/selector_text"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="預設" />
|
tools:text="預設" />
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
android:background="@drawable/selector_text"
|
android:background="@drawable/selector_text"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:text="來源 1"
|
tools:text="來源 1"
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:tag="3"
|
android:tag="3"
|
||||||
android:text="@string/play_track_text"
|
android:text="@string/play_track_text"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:tag="1"
|
android:tag="1"
|
||||||
android:text="@string/play_track_audio"
|
android:text="@string/play_track_audio"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:tag="2"
|
android:tag="2"
|
||||||
android:text="@string/play_track_video"
|
android:text="@string/play_track_video"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
android:nextFocusUp="@id/next"
|
android:nextFocusUp="@id/next"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
app:bar_height="2dp"
|
app:bar_height="2dp"
|
||||||
|
app:scrubber_enabled_size="12dp"
|
||||||
|
app:scrubber_disabled_size="12dp"
|
||||||
app:played_color="#FFEB3B"
|
app:played_color="#FFEB3B"
|
||||||
app:scrubber_color="#FFEB3B"
|
app:scrubber_color="#FFEB3B"
|
||||||
app:buffered_color="#80FFEB3B"
|
app:buffered_color="#80FFEB3B"
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
android:nextFocusLeft="@id/loop"
|
android:nextFocusLeft="@id/loop"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:text="@string/play_next"
|
android:text="@string/play_next"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:text="@string/play_prev"
|
android:text="@string/play_prev"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="刷新" />
|
tools:text="刷新" />
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:text="@string/play_change"
|
android:text="@string/play_change"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="換源" />
|
tools:text="換源" />
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:text="@string/play_exo"
|
android:text="@string/play_exo"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="硬解" />
|
tools:text="硬解" />
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="1.00" />
|
tools:text="1.00" />
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="預設" />
|
tools:text="預設" />
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:tag="3"
|
android:tag="3"
|
||||||
android:text="@string/play_track_text"
|
android:text="@string/play_track_text"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -167,7 +167,7 @@
|
|||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:tag="1"
|
android:tag="1"
|
||||||
android:text="@string/play_track_audio"
|
android:text="@string/play_track_audio"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -183,7 +183,7 @@
|
|||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:tag="2"
|
android:tag="2"
|
||||||
android:text="@string/play_track_video"
|
android:text="@string/play_track_video"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -197,7 +197,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="00:00" />
|
tools:text="00:00" />
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@
|
|||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@id/timeBar"
|
android:nextFocusDown="@id/timeBar"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/button_text"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="00:00" />
|
tools:text="00:00" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,44 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<color name="primary">@color/black</color>
|
<color name="primary">#FFEB3B</color>
|
||||||
<color name="primaryDark">@color/black</color>
|
<color name="primaryDark">#FDD835</color>
|
||||||
<color name="accent">@color/blue_500</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>
|
</resources>
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
<item name="colorPrimary">@color/primary</item>
|
<item name="colorPrimary">@color/primary</item>
|
||||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
|
<item name="colorControlHighlight">@color/primary</item>
|
||||||
<item name="android:windowFullscreen">true</item>
|
<item name="android:windowFullscreen">true</item>
|
||||||
<item name="android:windowBackground">@null</item>
|
<item name="android:windowBackground">@null</item>
|
||||||
<item name="android:windowDisablePreview">true</item>
|
<item name="android:windowDisablePreview">true</item>
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
<item name="android:paddingBottom">24dp</item>
|
<item name="android:paddingBottom">24dp</item>
|
||||||
</style>
|
</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="colorPrimary">@color/primary</item>
|
||||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
|
|||||||
@@ -5469,7 +5469,7 @@ body[data-weui-theme="dark"] .weui-picker__mask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.weui-primary-loading_brand {
|
.weui-primary-loading_brand {
|
||||||
color: #07c160;
|
color: #FFEB3B;
|
||||||
color: var(--weui-BRAND);
|
color: var(--weui-BRAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,27 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2023 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 2 fragment shader that samples from a (non-external) texture with
|
||||||
|
// uTexSampler, and multiplies its alpha value by uAlphaScale.
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
uniform float uAlphaScale;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 src = texture2D(uTexSampler, vTexSamplingCoord);
|
||||||
|
gl_FragColor = vec4(src.rgb, src.a * uAlphaScale);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2023 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 2 fragment shader that samples from a (non-external) texture with
|
||||||
|
// uTexSampler and copies this to the output.
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = texture2D(uTexSampler, vTexSamplingCoord);
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2022 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 2 fragment shader that samples from a (non-external) texture with
|
||||||
|
// uTexSampler. It then converts the RGB color input into HSL and adjusts
|
||||||
|
// the Hue, Saturation, and Lightness and converts it then back to RGB.
|
||||||
|
|
||||||
|
// We use the algorithm based on the work by Sam Hocevar, which optimizes
|
||||||
|
// for an efficient branchless RGB <-> HSL conversion. A blog post is
|
||||||
|
// at https://www.chilliant.com/rgb2hsv.html and it is further explained at
|
||||||
|
// http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv.
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
// uHueAdjustmentDegrees, uSaturationAdjustment, and uLightnessAdjustment
|
||||||
|
// are normalized to the unit interval [0, 1].
|
||||||
|
uniform float uHueAdjustmentDegrees;
|
||||||
|
uniform float uSaturationAdjustment;
|
||||||
|
uniform float uLightnessAdjustment;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
|
||||||
|
const float epsilon = 1e-10;
|
||||||
|
|
||||||
|
vec3 rgbToHcv(vec3 rgb) {
|
||||||
|
vec4 p = (rgb.g < rgb.b) ? vec4(rgb.bg, -1.0, 2.0 / 3.0)
|
||||||
|
: vec4(rgb.gb, 0.0, -1.0 / 3.0);
|
||||||
|
vec4 q = (rgb.r < p.x) ? vec4(p.xyw, rgb.r) : vec4(rgb.r, p.yzx);
|
||||||
|
float c = q.x - min(q.w, q.y);
|
||||||
|
float h = abs((q.w - q.y) / (6.0 * c + epsilon) + q.z);
|
||||||
|
return vec3(h, c, q.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 rgbToHsl(vec3 rgb) {
|
||||||
|
vec3 hcv = rgbToHcv(rgb);
|
||||||
|
float l = hcv.z - hcv.y * 0.5;
|
||||||
|
float s = hcv.y / (1.0 - abs(l * 2.0 - 1.0) + epsilon);
|
||||||
|
return vec3(hcv.x, s, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 hueToRgb(float hue) {
|
||||||
|
float r = abs(hue * 6.0 - 3.0) - 1.0;
|
||||||
|
float g = 2.0 - abs(hue * 6.0 - 2.0);
|
||||||
|
float b = 2.0 - abs(hue * 6.0 - 4.0);
|
||||||
|
return clamp(vec3(r, g, b), 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 hslToRgb(vec3 hsl) {
|
||||||
|
vec3 rgb = hueToRgb(hsl.x);
|
||||||
|
float c = (1.0 - abs(2.0 * hsl.z - 1.0)) * hsl.y;
|
||||||
|
return (rgb - 0.5) * c + hsl.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord);
|
||||||
|
vec3 hslColor = rgbToHsl(inputColor.rgb);
|
||||||
|
|
||||||
|
hslColor.x = mod(hslColor.x + uHueAdjustmentDegrees, 1.0);
|
||||||
|
hslColor.y = clamp(hslColor.y + uSaturationAdjustment, 0.0, 1.0);
|
||||||
|
hslColor.z = clamp(hslColor.z + uLightnessAdjustment, 0.0, 1.0);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(hslToRgb(hslColor), inputColor.a);
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2022 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES2 fragment shader that samples from a (non-external) texture with
|
||||||
|
// uTexSampler, copying from this texture to the current output while
|
||||||
|
// applying a 3D color lookup table to change the pixel colors.
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
// The uColorLut texture is a N x N^2 2D texture where each z-plane of the 3D
|
||||||
|
// LUT is vertically stacked on top of each other. The red channel of the input
|
||||||
|
// color (z-axis in LUT[R][G][B] = LUT[z][y][x]) points to the plane to sample
|
||||||
|
// from. For more information check the
|
||||||
|
// androidx/media3/effect/SingleColorLut.java class, especially the function
|
||||||
|
// #transformCubeIntoBitmap with a provided example.
|
||||||
|
uniform sampler2D uColorLut;
|
||||||
|
uniform float uColorLutLength;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
|
||||||
|
// Applies the color lookup using uLut based on the input colors.
|
||||||
|
vec3 applyLookup(vec3 color) {
|
||||||
|
// Reminder: Inside OpenGL vector.xyz is the same as vector.rgb.
|
||||||
|
// Here we use mentions of x and y coordinates to references to
|
||||||
|
// the position to sample from inside the 2D LUT plane and
|
||||||
|
// rgb to create the 3D coordinates based on the input colors.
|
||||||
|
|
||||||
|
// To sample from the 3D LUT we interpolate bilinearly twice in the 2D LUT
|
||||||
|
// to replicate the trilinear interpolation in a 3D LUT. Thus we sample
|
||||||
|
// from the plane of position redCoordLow and on the plane above.
|
||||||
|
// redCoordLow points to the lower plane to sample from.
|
||||||
|
float redCoord = color.r * (uColorLutLength - 1.0);
|
||||||
|
// Clamping to uColorLutLength - 2 is only needed if redCoord points to the
|
||||||
|
// most upper plane. In this case there would not be any plane above
|
||||||
|
// available to sample from.
|
||||||
|
float redCoordLow = clamp(floor(redCoord), 0.0, uColorLutLength - 2.0);
|
||||||
|
|
||||||
|
// lowerY is indexed in two steps. First redCoordLow defines the plane to
|
||||||
|
// sample from. Next the green color component is added to index the row in
|
||||||
|
// the found plane. As described in the NVIDIA blog article about LUTs
|
||||||
|
// https://developer.nvidia.com/gpugems/gpugems2/part-iii-high-quality-rendering/chapter-24-using-lookup-tables-accelerate-color
|
||||||
|
// (Section 24.2), we sample from color * scale + offset, where offset is
|
||||||
|
// defined by 1 / (2 * uColorLutLength) and the scale is defined by
|
||||||
|
// (uColorLutLength - 1.0) / uColorLutLength.
|
||||||
|
|
||||||
|
// The following derives the equation of lowerY. For this let
|
||||||
|
// N = uColorLutLenght. The general formula to sample at row y
|
||||||
|
// is defined as y = N * r + g.
|
||||||
|
// Using the offset and scale as described in NVIDIA's blog article we get:
|
||||||
|
// y = offset + (N * r + g) * scale
|
||||||
|
// y = 1 / (2 * N) + (N * r + g) * (N - 1) / N
|
||||||
|
// y = 1 / (2 * N) + N * r * (N - 1) / N + g * (N - 1) / N
|
||||||
|
// We have defined redCoord as r * (N - 1) if we excluded the clamping for
|
||||||
|
// now, giving us:
|
||||||
|
// y = 1 / (2 * N) + N * redCoord / N + g * (N - 1) / N
|
||||||
|
// This simplifies to:
|
||||||
|
// y = 0.5 / N + (N * redCoord + g * (N - 1)) / N
|
||||||
|
// y = (0.5 + N * redCoord + g * (N - 1)) / N
|
||||||
|
// This formula now assumes a coordinate system in the range of [0, N] but
|
||||||
|
// OpenGL uses a [0, 1] unit coordinate system internally. Thus dividing
|
||||||
|
// by N gives us the final formula for y:
|
||||||
|
// y = ((0.5 + N * redCoord + g * (N - 1)) / N) / N
|
||||||
|
// y = (0.5 + redCoord * N + g * (N - 1)) / (N * N)
|
||||||
|
float lowerY = (0.5 + redCoordLow * uColorLutLength +
|
||||||
|
color.g * (uColorLutLength - 1.0)) /
|
||||||
|
(uColorLutLength * uColorLutLength);
|
||||||
|
// The upperY is the same position moved up by one LUT plane.
|
||||||
|
float upperY = lowerY + 1.0 / uColorLutLength;
|
||||||
|
|
||||||
|
// The x position is the blue color channel (x-axis in LUT[R][G][B]).
|
||||||
|
float x = (0.5 + color.b * (uColorLutLength - 1.0)) / uColorLutLength;
|
||||||
|
|
||||||
|
vec3 lowerRgb = texture2D(uColorLut, vec2(x, lowerY)).rgb;
|
||||||
|
vec3 upperRgb = texture2D(uColorLut, vec2(x, upperY)).rgb;
|
||||||
|
|
||||||
|
// Linearly interpolate between lowerRgb and upperRgb based on the
|
||||||
|
// distance of the actual in the plane and the lower sampling position.
|
||||||
|
return mix(lowerRgb, upperRgb, redCoord - redCoordLow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord);
|
||||||
|
|
||||||
|
gl_FragColor.rgb = applyLookup(inputColor.rgb);
|
||||||
|
gl_FragColor.a = inputColor.a;
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#version 300 es
|
||||||
|
// Copyright 2022 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 3 fragment shader that:
|
||||||
|
// 1. Samples optical linear BT.2020 RGB from a (non-external) texture with
|
||||||
|
// uTexSampler.
|
||||||
|
// 2. Applies a 4x4 RGB color matrix to change the pixel colors.
|
||||||
|
// 3. Outputs electrical (HLG or PQ) BT.2020 RGB based on uOutputColorTransfer,
|
||||||
|
// via an OETF.
|
||||||
|
// The output will be red if an error has occurred.
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
in vec2 vTexSamplingCoord;
|
||||||
|
out vec4 outColor;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_ST2084 and COLOR_TRANSFER_HLG are allowed.
|
||||||
|
uniform int uOutputColorTransfer;
|
||||||
|
uniform mat3 uColorTransform;
|
||||||
|
uniform mat4 uRgbMatrix;
|
||||||
|
|
||||||
|
// TODO(b/227624622): Consider using mediump to save precision, if it won't lead
|
||||||
|
// to noticeable quantization.
|
||||||
|
|
||||||
|
// HLG OETF for one channel.
|
||||||
|
highp float hlgOetfSingleChannel(highp float linearChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=529-543;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float a = 0.17883277;
|
||||||
|
const highp float b = 0.28466892;
|
||||||
|
const highp float c = 0.55991073;
|
||||||
|
|
||||||
|
return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel)
|
||||||
|
: a * log(12.0 * linearChannel - b) + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG OETF.
|
||||||
|
highp vec3 hlgOetf(highp vec3 linearColor) {
|
||||||
|
return vec3(hlgOetfSingleChannel(linearColor.r),
|
||||||
|
hlgOetfSingleChannel(linearColor.g),
|
||||||
|
hlgOetfSingleChannel(linearColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020, PQ / ST2084 OETF.
|
||||||
|
highp vec3 pqOetf(highp vec3 linearColor) {
|
||||||
|
// Specification:
|
||||||
|
// https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=514-527;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float m1 = (2610.0 / 16384.0);
|
||||||
|
const highp float m2 = (2523.0 / 4096.0) * 128.0;
|
||||||
|
const highp float c1 = (3424.0 / 4096.0);
|
||||||
|
const highp float c2 = (2413.0 / 4096.0) * 32.0;
|
||||||
|
const highp float c3 = (2392.0 / 4096.0) * 32.0;
|
||||||
|
|
||||||
|
highp vec3 temp = pow(linearColor, vec3(m1));
|
||||||
|
temp = (c1 + c2 * temp) / (1.0 + c3 * temp);
|
||||||
|
return pow(temp, vec3(m2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the appropriate OETF to convert linear optical signals to nonlinear
|
||||||
|
// electrical signals. Input and output are both normalized to [0, 1].
|
||||||
|
highp vec3 applyOetf(highp vec3 linearColor) {
|
||||||
|
// LINT.IfChange(color_transfer)
|
||||||
|
const int COLOR_TRANSFER_ST2084 = 6;
|
||||||
|
const int COLOR_TRANSFER_HLG = 7;
|
||||||
|
if (uOutputColorTransfer == COLOR_TRANSFER_ST2084) {
|
||||||
|
return pqOetf(linearColor);
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_HLG) {
|
||||||
|
return hlgOetf(linearColor);
|
||||||
|
} else {
|
||||||
|
// Output red as an obviously visible error.
|
||||||
|
return vec3(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 inputColor = texture(uTexSampler, vTexSamplingCoord);
|
||||||
|
// transformedColors is an optical color.
|
||||||
|
vec4 transformedColors = uRgbMatrix * vec4(inputColor.rgb, 1);
|
||||||
|
outColor = vec4(applyOetf(transformedColors.rgb), inputColor.a);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2022 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 2 fragment shader that samples from a (non-external) texture with
|
||||||
|
// uTexSampler, copying from this texture to the current output while
|
||||||
|
// applying a 4x4 RGB color matrix to change the pixel colors.
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
uniform mat4 uRgbMatrix;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord);
|
||||||
|
gl_FragColor = uRgbMatrix * vec4(inputColor.rgb, 1);
|
||||||
|
gl_FragColor.a = inputColor.a;
|
||||||
|
}
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
#version 300 es
|
||||||
|
// Copyright 2022 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 3 fragment shader that:
|
||||||
|
// 1. Samples electrical (HLG or PQ) BT.2020 YUV from an external texture with
|
||||||
|
// uTexSampler, where the sampler uses the EXT_YUV_target extension specified
|
||||||
|
// at
|
||||||
|
// https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_YUV_target.txt,
|
||||||
|
// 2. Applies a YUV to RGB conversion using the specified color transform
|
||||||
|
// uYuvToRgbColorTransform, yielding electrical (HLG or PQ) BT.2020 RGB,
|
||||||
|
// 3. Applies an EOTF based on uInputColorTransfer, yielding optical linear
|
||||||
|
// BT.2020 RGB.
|
||||||
|
// 4. Optionally applies a BT2020 to BT709 OOTF, if OpenGL tone-mapping is
|
||||||
|
// requested via uApplyHdrToSdrToneMapping.
|
||||||
|
// 5. Applies a 4x4 RGB color matrix to change the pixel colors.
|
||||||
|
// 6. Outputs as requested by uOutputColorTransfer. Use COLOR_TRANSFER_LINEAR
|
||||||
|
// for outputting to intermediate shaders, or COLOR_TRANSFER_ST2084 /
|
||||||
|
// COLOR_TRANSFER_HLG to output electrical colors via an OETF (e.g. to an
|
||||||
|
// encoder).
|
||||||
|
// The output will be red or blue if an error has occurred.
|
||||||
|
|
||||||
|
#extension GL_OES_EGL_image_external : require
|
||||||
|
#extension GL_EXT_YUV_target : require
|
||||||
|
precision mediump float;
|
||||||
|
uniform __samplerExternal2DY2YEXT uTexSampler;
|
||||||
|
uniform mat3 uYuvToRgbColorTransform;
|
||||||
|
uniform mat4 uRgbMatrix;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_ST2084 and COLOR_TRANSFER_HLG are allowed.
|
||||||
|
uniform int uInputColorTransfer;
|
||||||
|
uniform int uApplyHdrToSdrToneMapping;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_LINEAR, COLOR_TRANSFER_GAMMA_2_2, COLOR_TRANSFER_ST2084,
|
||||||
|
// and COLOR_TRANSFER_HLG are allowed.
|
||||||
|
uniform int uOutputColorTransfer;
|
||||||
|
in vec2 vTexSamplingCoord;
|
||||||
|
out vec4 outColor;
|
||||||
|
|
||||||
|
// LINT.IfChange(color_transfer)
|
||||||
|
const int COLOR_TRANSFER_LINEAR = 1;
|
||||||
|
const int COLOR_TRANSFER_GAMMA_2_2 = 10;
|
||||||
|
const int COLOR_TRANSFER_ST2084 = 6;
|
||||||
|
const int COLOR_TRANSFER_HLG = 7;
|
||||||
|
|
||||||
|
// TODO(b/227624622): Consider using mediump to save precision, if it won't lead
|
||||||
|
// to noticeable quantization errors.
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG EOTF for one channel.
|
||||||
|
highp float hlgEotfSingleChannel(highp float hlgChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=265-279;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float a = 0.17883277;
|
||||||
|
const highp float b = 0.28466892;
|
||||||
|
const highp float c = 0.55991073;
|
||||||
|
return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0
|
||||||
|
: (b + exp((hlgChannel - c) / a)) / 12.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG EOTF.
|
||||||
|
highp vec3 hlgEotf(highp vec3 hlgColor) {
|
||||||
|
return vec3(hlgEotfSingleChannel(hlgColor.r),
|
||||||
|
hlgEotfSingleChannel(hlgColor.g),
|
||||||
|
hlgEotfSingleChannel(hlgColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 PQ EOTF.
|
||||||
|
highp vec3 pqEotf(highp vec3 pqColor) {
|
||||||
|
// Specification:
|
||||||
|
// https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=250-263;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float m1 = (2610.0 / 16384.0);
|
||||||
|
const highp float m2 = (2523.0 / 4096.0) * 128.0;
|
||||||
|
const highp float c1 = (3424.0 / 4096.0);
|
||||||
|
const highp float c2 = (2413.0 / 4096.0) * 32.0;
|
||||||
|
const highp float c3 = (2392.0 / 4096.0) * 32.0;
|
||||||
|
|
||||||
|
highp vec3 temp = pow(clamp(pqColor, 0.0, 1.0), 1.0 / vec3(m2));
|
||||||
|
temp = max(temp - c1, 0.0) / (c2 - c3 * temp);
|
||||||
|
return pow(temp, 1.0 / vec3(m1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the appropriate EOTF to convert nonlinear electrical values to linear
|
||||||
|
// optical values. Input and output are both normalized to [0, 1].
|
||||||
|
highp vec3 applyEotf(highp vec3 electricalColor) {
|
||||||
|
if (uInputColorTransfer == COLOR_TRANSFER_ST2084) {
|
||||||
|
return pqEotf(electricalColor);
|
||||||
|
} else if (uInputColorTransfer == COLOR_TRANSFER_HLG) {
|
||||||
|
return hlgEotf(electricalColor);
|
||||||
|
} else {
|
||||||
|
// Output red as an obviously visible error.
|
||||||
|
return vec3(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the HLG BT2020 to BT709 OOTF.
|
||||||
|
highp vec3 applyHlgBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) {
|
||||||
|
// Reference ("HLG Reference OOTF" section):
|
||||||
|
// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf
|
||||||
|
// Matrix values based on computeXYZMatrix(BT2020Primaries, BT2020WhitePoint)
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/utils/HostColorSpace.cpp;l=200-232;drc=86bd214059cd6150304888a285941bf74af5b687
|
||||||
|
const mat3 RGB_TO_XYZ_BT2020 =
|
||||||
|
mat3(0.63695805f, 0.26270021f, 0.00000000f, 0.14461690f, 0.67799807f,
|
||||||
|
0.02807269f, 0.16888098f, 0.05930172f, 1.06098506f);
|
||||||
|
// Matrix values based on computeXYZMatrix(BT709Primaries, BT709WhitePoint)
|
||||||
|
const mat3 XYZ_TO_RGB_BT709 =
|
||||||
|
mat3(3.24096994f, -0.96924364f, 0.05563008f, -1.53738318f, 1.87596750f,
|
||||||
|
-0.20397696f, -0.49861076f, 0.04155506f, 1.05697151f);
|
||||||
|
// hlgGamma is 1.2 + 0.42 * log10(nominalPeakLuminance/1000);
|
||||||
|
// nominalPeakLuminance was selected to use a 500 as a typical value, used
|
||||||
|
// in
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/tonemap/tonemap.cpp;drc=7a577450e536aa1e99f229a0cb3d3531c82e8a8d;l=62,
|
||||||
|
// b/199162498#comment35, and
|
||||||
|
// https://www.microsoft.com/applied-sciences/uploads/projects/investigation-of-hdr-vs-tone-mapped-sdr/investigation-of-hdr-vs-tone-mapped-sdr.pdf.
|
||||||
|
const float hlgGamma = 1.0735674018211279;
|
||||||
|
|
||||||
|
vec3 linearXyzBt2020 = RGB_TO_XYZ_BT2020 * linearRgbBt2020;
|
||||||
|
vec3 linearXyzBt709 =
|
||||||
|
linearXyzBt2020 * pow(linearXyzBt2020[1], hlgGamma - 1.0);
|
||||||
|
vec3 linearRgbBt709 = clamp((XYZ_TO_RGB_BT709 * linearXyzBt709), 0.0, 1.0);
|
||||||
|
return linearRgbBt709;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the PQ BT2020 to BT709 OOTF.
|
||||||
|
highp vec3 applyPqBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) {
|
||||||
|
float pqPeakLuminance = 10000.0;
|
||||||
|
float sdrPeakLuminance = 500.0;
|
||||||
|
|
||||||
|
return linearRgbBt2020 * pqPeakLuminance / sdrPeakLuminance;
|
||||||
|
}
|
||||||
|
|
||||||
|
highp vec3 applyBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) {
|
||||||
|
if (uInputColorTransfer == COLOR_TRANSFER_ST2084) {
|
||||||
|
return applyPqBt2020ToBt709Ootf(linearRgbBt2020);
|
||||||
|
} else if (uInputColorTransfer == COLOR_TRANSFER_HLG) {
|
||||||
|
return applyHlgBt2020ToBt709Ootf(linearRgbBt2020);
|
||||||
|
} else {
|
||||||
|
// Output green as an obviously visible error.
|
||||||
|
return vec3(0.0, 1.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG OETF for one channel.
|
||||||
|
highp float hlgOetfSingleChannel(highp float linearChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=529-543;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float a = 0.17883277;
|
||||||
|
const highp float b = 0.28466892;
|
||||||
|
const highp float c = 0.55991073;
|
||||||
|
|
||||||
|
return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel)
|
||||||
|
: a * log(12.0 * linearChannel - b) + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG OETF.
|
||||||
|
highp vec3 hlgOetf(highp vec3 linearColor) {
|
||||||
|
return vec3(hlgOetfSingleChannel(linearColor.r),
|
||||||
|
hlgOetfSingleChannel(linearColor.g),
|
||||||
|
hlgOetfSingleChannel(linearColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020, PQ / ST2084 OETF.
|
||||||
|
highp vec3 pqOetf(highp vec3 linearColor) {
|
||||||
|
// Specification:
|
||||||
|
// https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=514-527;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float m1 = (2610.0 / 16384.0);
|
||||||
|
const highp float m2 = (2523.0 / 4096.0) * 128.0;
|
||||||
|
const highp float c1 = (3424.0 / 4096.0);
|
||||||
|
const highp float c2 = (2413.0 / 4096.0) * 32.0;
|
||||||
|
const highp float c3 = (2392.0 / 4096.0) * 32.0;
|
||||||
|
|
||||||
|
highp vec3 temp = pow(linearColor, vec3(m1));
|
||||||
|
temp = (c1 + c2 * temp) / (1.0 + c3 * temp);
|
||||||
|
return pow(temp, vec3(m2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.709 gamma 2.2 OETF for one channel.
|
||||||
|
float gamma22OetfSingleChannel(highp float linearChannel) {
|
||||||
|
// Reference:
|
||||||
|
// https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_GAMMA2_2
|
||||||
|
return pow(linearChannel, (1.0 / 2.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.709 gamma 2.2 OETF.
|
||||||
|
vec3 gamma22Oetf(highp vec3 linearColor) {
|
||||||
|
return vec3(gamma22OetfSingleChannel(linearColor.r),
|
||||||
|
gamma22OetfSingleChannel(linearColor.g),
|
||||||
|
gamma22OetfSingleChannel(linearColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the appropriate OETF to convert linear optical signals to nonlinear
|
||||||
|
// electrical signals. Input and output are both normalized to [0, 1].
|
||||||
|
highp vec3 applyOetf(highp vec3 linearColor) {
|
||||||
|
if (uOutputColorTransfer == COLOR_TRANSFER_ST2084) {
|
||||||
|
return pqOetf(linearColor);
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_HLG) {
|
||||||
|
return hlgOetf(linearColor);
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_GAMMA_2_2) {
|
||||||
|
return gamma22Oetf(linearColor);
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR) {
|
||||||
|
return linearColor;
|
||||||
|
} else {
|
||||||
|
// Output blue as an obviously visible error.
|
||||||
|
return vec3(0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 yuvToRgb(vec3 yuv) {
|
||||||
|
const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
|
||||||
|
return clamp(uYuvToRgbColorTransform * (yuv - yuvOffset), 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 srcYuv = texture(uTexSampler, vTexSamplingCoord).xyz;
|
||||||
|
vec3 opticalColorBt2020 = applyEotf(yuvToRgb(srcYuv));
|
||||||
|
vec4 opticalColor =
|
||||||
|
(uApplyHdrToSdrToneMapping == 1)
|
||||||
|
? vec4(applyBt2020ToBt709Ootf(opticalColorBt2020), 1.0)
|
||||||
|
: vec4(opticalColorBt2020, 1.0);
|
||||||
|
vec4 transformedColors = uRgbMatrix * opticalColor;
|
||||||
|
outColor = vec4(applyOetf(transformedColors.rgb), 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
#version 300 es
|
||||||
|
// Copyright 2022 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 3 fragment shader that:
|
||||||
|
// 1. Samples electrical (HLG or PQ) BT.2020 RGB from an internal texture.
|
||||||
|
// 2. Applies an EOTF based on uInputColorTransfer, yielding optical linear
|
||||||
|
// BT.2020 RGB.
|
||||||
|
// 3. Optionally applies a BT2020 to BT709 OOTF, if OpenGL tone-mapping is
|
||||||
|
// requested via uApplyHdrToSdrToneMapping.
|
||||||
|
// 4. Applies a 4x4 RGB color matrix to change the pixel colors.
|
||||||
|
// 5. Outputs as requested by uOutputColorTransfer. Use COLOR_TRANSFER_LINEAR
|
||||||
|
// for outputting to intermediate shaders, or COLOR_TRANSFER_ST2084 /
|
||||||
|
// COLOR_TRANSFER_HLG to output electrical colors via an OETF (e.g. to an
|
||||||
|
// encoder).
|
||||||
|
// The output will be red or blue if an error has occurred.
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
uniform mat4 uRgbMatrix;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_ST2084 and COLOR_TRANSFER_HLG are allowed.
|
||||||
|
uniform int uInputColorTransfer;
|
||||||
|
uniform int uApplyHdrToSdrToneMapping;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_LINEAR, COLOR_TRANSFER_GAMMA_2_2, COLOR_TRANSFER_ST2084,
|
||||||
|
// and COLOR_TRANSFER_HLG are allowed.
|
||||||
|
uniform int uOutputColorTransfer;
|
||||||
|
in vec2 vTexSamplingCoord;
|
||||||
|
out vec4 outColor;
|
||||||
|
|
||||||
|
// LINT.IfChange(color_transfer)
|
||||||
|
const int COLOR_TRANSFER_LINEAR = 1;
|
||||||
|
const int COLOR_TRANSFER_GAMMA_2_2 = 10;
|
||||||
|
const int COLOR_TRANSFER_ST2084 = 6;
|
||||||
|
const int COLOR_TRANSFER_HLG = 7;
|
||||||
|
|
||||||
|
// TODO(b/227624622): Consider using mediump to save precision, if it won't lead
|
||||||
|
// to noticeable quantization errors.
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG EOTF for one channel.
|
||||||
|
highp float hlgEotfSingleChannel(highp float hlgChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=265-279;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float a = 0.17883277;
|
||||||
|
const highp float b = 0.28466892;
|
||||||
|
const highp float c = 0.55991073;
|
||||||
|
return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0
|
||||||
|
: (b + exp((hlgChannel - c) / a)) / 12.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG EOTF.
|
||||||
|
highp vec3 hlgEotf(highp vec3 hlgColor) {
|
||||||
|
return vec3(hlgEotfSingleChannel(hlgColor.r),
|
||||||
|
hlgEotfSingleChannel(hlgColor.g),
|
||||||
|
hlgEotfSingleChannel(hlgColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 PQ EOTF.
|
||||||
|
highp vec3 pqEotf(highp vec3 pqColor) {
|
||||||
|
// Specification:
|
||||||
|
// https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=250-263;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float m1 = (2610.0 / 16384.0);
|
||||||
|
const highp float m2 = (2523.0 / 4096.0) * 128.0;
|
||||||
|
const highp float c1 = (3424.0 / 4096.0);
|
||||||
|
const highp float c2 = (2413.0 / 4096.0) * 32.0;
|
||||||
|
const highp float c3 = (2392.0 / 4096.0) * 32.0;
|
||||||
|
|
||||||
|
highp vec3 temp = pow(clamp(pqColor, 0.0, 1.0), 1.0 / vec3(m2));
|
||||||
|
temp = max(temp - c1, 0.0) / (c2 - c3 * temp);
|
||||||
|
return pow(temp, 1.0 / vec3(m1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the appropriate EOTF to convert nonlinear electrical values to linear
|
||||||
|
// optical values. Input and output are both normalized to [0, 1].
|
||||||
|
highp vec3 applyEotf(highp vec3 electricalColor) {
|
||||||
|
if (uInputColorTransfer == COLOR_TRANSFER_ST2084) {
|
||||||
|
return pqEotf(electricalColor);
|
||||||
|
} else if (uInputColorTransfer == COLOR_TRANSFER_HLG) {
|
||||||
|
return hlgEotf(electricalColor);
|
||||||
|
} else {
|
||||||
|
// Output red as an obviously visible error.
|
||||||
|
return vec3(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the HLG BT2020 to BT709 OOTF.
|
||||||
|
highp vec3 applyHlgBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) {
|
||||||
|
// Reference ("HLG Reference OOTF" section):
|
||||||
|
// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf
|
||||||
|
// Matrix values based on computeXYZMatrix(BT2020Primaries, BT2020WhitePoint)
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/utils/HostColorSpace.cpp;l=200-232;drc=86bd214059cd6150304888a285941bf74af5b687
|
||||||
|
const mat3 RGB_TO_XYZ_BT2020 =
|
||||||
|
mat3(0.63695805f, 0.26270021f, 0.00000000f, 0.14461690f, 0.67799807f,
|
||||||
|
0.02807269f, 0.16888098f, 0.05930172f, 1.06098506f);
|
||||||
|
// Matrix values based on computeXYZMatrix(BT709Primaries, BT709WhitePoint)
|
||||||
|
const mat3 XYZ_TO_RGB_BT709 =
|
||||||
|
mat3(3.24096994f, -0.96924364f, 0.05563008f, -1.53738318f, 1.87596750f,
|
||||||
|
-0.20397696f, -0.49861076f, 0.04155506f, 1.05697151f);
|
||||||
|
// hlgGamma is 1.2 + 0.42 * log10(nominalPeakLuminance/1000);
|
||||||
|
// nominalPeakLuminance was selected to use a 500 as a typical value, used
|
||||||
|
// in
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/tonemap/tonemap.cpp;drc=7a577450e536aa1e99f229a0cb3d3531c82e8a8d;l=62,
|
||||||
|
// b/199162498#comment35, and
|
||||||
|
// https://www.microsoft.com/applied-sciences/uploads/projects/investigation-of-hdr-vs-tone-mapped-sdr/investigation-of-hdr-vs-tone-mapped-sdr.pdf.
|
||||||
|
const float hlgGamma = 1.0735674018211279;
|
||||||
|
|
||||||
|
vec3 linearXyzBt2020 = RGB_TO_XYZ_BT2020 * linearRgbBt2020;
|
||||||
|
vec3 linearXyzBt709 =
|
||||||
|
linearXyzBt2020 * pow(linearXyzBt2020[1], hlgGamma - 1.0);
|
||||||
|
vec3 linearRgbBt709 = clamp((XYZ_TO_RGB_BT709 * linearXyzBt709), 0.0, 1.0);
|
||||||
|
return linearRgbBt709;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the PQ BT2020 to BT709 OOTF.
|
||||||
|
highp vec3 applyPqBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) {
|
||||||
|
float pqPeakLuminance = 10000.0;
|
||||||
|
float sdrPeakLuminance = 500.0;
|
||||||
|
|
||||||
|
return linearRgbBt2020 * pqPeakLuminance / sdrPeakLuminance;
|
||||||
|
}
|
||||||
|
|
||||||
|
highp vec3 applyBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) {
|
||||||
|
if (uInputColorTransfer == COLOR_TRANSFER_ST2084) {
|
||||||
|
return applyPqBt2020ToBt709Ootf(linearRgbBt2020);
|
||||||
|
} else if (uInputColorTransfer == COLOR_TRANSFER_HLG) {
|
||||||
|
return applyHlgBt2020ToBt709Ootf(linearRgbBt2020);
|
||||||
|
} else {
|
||||||
|
// Output green as an obviously visible error.
|
||||||
|
return vec3(0.0, 1.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG OETF for one channel.
|
||||||
|
highp float hlgOetfSingleChannel(highp float linearChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=529-543;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float a = 0.17883277;
|
||||||
|
const highp float b = 0.28466892;
|
||||||
|
const highp float c = 0.55991073;
|
||||||
|
|
||||||
|
return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel)
|
||||||
|
: a * log(12.0 * linearChannel - b) + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020 HLG OETF.
|
||||||
|
highp vec3 hlgOetf(highp vec3 linearColor) {
|
||||||
|
return vec3(hlgOetfSingleChannel(linearColor.r),
|
||||||
|
hlgOetfSingleChannel(linearColor.g),
|
||||||
|
hlgOetfSingleChannel(linearColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.2100 / BT.2020, PQ / ST2084 OETF.
|
||||||
|
highp vec3 pqOetf(highp vec3 linearColor) {
|
||||||
|
// Specification:
|
||||||
|
// https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ
|
||||||
|
// Reference implementation:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=514-527;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
|
||||||
|
const highp float m1 = (2610.0 / 16384.0);
|
||||||
|
const highp float m2 = (2523.0 / 4096.0) * 128.0;
|
||||||
|
const highp float c1 = (3424.0 / 4096.0);
|
||||||
|
const highp float c2 = (2413.0 / 4096.0) * 32.0;
|
||||||
|
const highp float c3 = (2392.0 / 4096.0) * 32.0;
|
||||||
|
|
||||||
|
highp vec3 temp = pow(linearColor, vec3(m1));
|
||||||
|
temp = (c1 + c2 * temp) / (1.0 + c3 * temp);
|
||||||
|
return pow(temp, vec3(m2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.709 gamma 2.2 OETF for one channel.
|
||||||
|
float gamma22OetfSingleChannel(highp float linearChannel) {
|
||||||
|
// Reference:
|
||||||
|
// https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_GAMMA2_2
|
||||||
|
return pow(linearChannel, (1.0 / 2.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.709 gamma 2.2 OETF.
|
||||||
|
vec3 gamma22Oetf(highp vec3 linearColor) {
|
||||||
|
return vec3(gamma22OetfSingleChannel(linearColor.r),
|
||||||
|
gamma22OetfSingleChannel(linearColor.g),
|
||||||
|
gamma22OetfSingleChannel(linearColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the appropriate OETF to convert linear optical signals to nonlinear
|
||||||
|
// electrical signals. Input and output are both normalized to [0, 1].
|
||||||
|
highp vec3 applyOetf(highp vec3 linearColor) {
|
||||||
|
if (uOutputColorTransfer == COLOR_TRANSFER_ST2084) {
|
||||||
|
return pqOetf(linearColor);
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_HLG) {
|
||||||
|
return hlgOetf(linearColor);
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_GAMMA_2_2) {
|
||||||
|
return gamma22Oetf(linearColor);
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR) {
|
||||||
|
return linearColor;
|
||||||
|
} else {
|
||||||
|
// Output blue as an obviously visible error.
|
||||||
|
return vec3(0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 opticalColorBt2020 =
|
||||||
|
applyEotf(texture(uTexSampler, vTexSamplingCoord).xyz);
|
||||||
|
vec4 opticalColor =
|
||||||
|
(uApplyHdrToSdrToneMapping == 1)
|
||||||
|
? vec4(applyBt2020ToBt709Ootf(opticalColorBt2020), 1.0)
|
||||||
|
: vec4(opticalColorBt2020, 1.0);
|
||||||
|
vec4 transformedColors = uRgbMatrix * opticalColor;
|
||||||
|
outColor = vec4(applyOetf(transformedColors.rgb), 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2021 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 2 fragment shader that:
|
||||||
|
// 1. Samples from an external texture with uTexSampler copying from this
|
||||||
|
// texture to the current output.
|
||||||
|
// 2. Transforms the electrical colors to optical colors using the SMPTE 170M
|
||||||
|
// EOTF.
|
||||||
|
// 3. Applies a 4x4 RGB color matrix to change the pixel colors.
|
||||||
|
// 4. Outputs as requested by uOutputColorTransfer. Use COLOR_TRANSFER_LINEAR
|
||||||
|
// for outputting to intermediate shaders, or COLOR_TRANSFER_SDR_VIDEO to
|
||||||
|
// output electrical colors via an OETF (e.g. to an encoder).
|
||||||
|
|
||||||
|
#extension GL_OES_EGL_image_external : require
|
||||||
|
precision mediump float;
|
||||||
|
uniform samplerExternalOES uTexSampler;
|
||||||
|
uniform mat4 uRgbMatrix;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_LINEAR and COLOR_TRANSFER_SDR_VIDEO are allowed.
|
||||||
|
uniform int uOutputColorTransfer;
|
||||||
|
uniform int uEnableColorTransfer;
|
||||||
|
|
||||||
|
const float inverseGamma = 0.4500;
|
||||||
|
const float gamma = 1.0 / inverseGamma;
|
||||||
|
const int GL_FALSE = 0;
|
||||||
|
const int GL_TRUE = 1;
|
||||||
|
|
||||||
|
// Transforms a single channel from electrical to optical SDR using the SMPTE
|
||||||
|
// 170M OETF.
|
||||||
|
float smpte170mEotfSingleChannel(float electricalChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en
|
||||||
|
return electricalChannel < 0.0812
|
||||||
|
? electricalChannel / 4.500
|
||||||
|
: pow((electricalChannel + 0.099) / 1.099, gamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms electrical to optical SDR using the SMPTE 170M EOTF.
|
||||||
|
vec3 smpte170mEotf(vec3 electricalColor) {
|
||||||
|
return vec3(smpte170mEotfSingleChannel(electricalColor.r),
|
||||||
|
smpte170mEotfSingleChannel(electricalColor.g),
|
||||||
|
smpte170mEotfSingleChannel(electricalColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms a single channel from optical to electrical SDR.
|
||||||
|
float smpte170mOetfSingleChannel(float opticalChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en
|
||||||
|
return opticalChannel < 0.018
|
||||||
|
? opticalChannel * 4.500
|
||||||
|
: 1.099 * pow(opticalChannel, inverseGamma) - 0.099;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms optical SDR colors to electrical SDR using the SMPTE 170M OETF.
|
||||||
|
vec3 smpte170mOetf(vec3 opticalColor) {
|
||||||
|
return vec3(smpte170mOetfSingleChannel(opticalColor.r),
|
||||||
|
smpte170mOetfSingleChannel(opticalColor.g),
|
||||||
|
smpte170mOetfSingleChannel(opticalColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the appropriate OETF to convert linear optical signals to nonlinear
|
||||||
|
// electrical signals. Input and output are both normalized to [0, 1].
|
||||||
|
highp vec3 applyOetf(highp vec3 linearColor) {
|
||||||
|
// LINT.IfChange(color_transfer)
|
||||||
|
const int COLOR_TRANSFER_LINEAR = 1;
|
||||||
|
const int COLOR_TRANSFER_SDR_VIDEO = 3;
|
||||||
|
if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR ||
|
||||||
|
uEnableColorTransfer == GL_FALSE) {
|
||||||
|
return linearColor;
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_SDR_VIDEO) {
|
||||||
|
return smpte170mOetf(linearColor);
|
||||||
|
} else {
|
||||||
|
// Output red as an obviously visible error.
|
||||||
|
return vec3(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 applyEotf(vec3 electricalColor) {
|
||||||
|
if (uEnableColorTransfer == GL_TRUE) {
|
||||||
|
return smpte170mEotf(electricalColor);
|
||||||
|
} else if (uEnableColorTransfer == GL_FALSE) {
|
||||||
|
return electricalColor;
|
||||||
|
} else {
|
||||||
|
// Output blue as an obviously visible error.
|
||||||
|
return vec3(0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord);
|
||||||
|
vec3 linearInputColor = applyEotf(inputColor.rgb);
|
||||||
|
|
||||||
|
vec4 transformedColors = uRgbMatrix * vec4(linearInputColor, 1);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(applyOetf(transformedColors.rgb), inputColor.a);
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2023 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 2 fragment shader that:
|
||||||
|
// 1. Samples from an input texture created from an internal texture (e.g. a
|
||||||
|
// texture created from a bitmap), with uTexSampler copying from this texture
|
||||||
|
// to the current output.
|
||||||
|
// 2. Transforms the electrical colors to optical colors using the SMPTE 170M
|
||||||
|
// EOTF or the sRGB EOTF, as requested by uInputColorTransfer.
|
||||||
|
// 3. Applies a 4x4 RGB color matrix to change the pixel colors.
|
||||||
|
// 4. Outputs as requested by uOutputColorTransfer. Use COLOR_TRANSFER_LINEAR
|
||||||
|
// for outputting to intermediate shaders, or COLOR_TRANSFER_SDR_VIDEO to
|
||||||
|
// output electrical colors via an OETF (e.g. to an encoder).
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
uniform mat4 uRgbMatrix;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_SRGB and COLOR_TRANSFER_SDR_VIDEO are allowed.
|
||||||
|
uniform int uInputColorTransfer;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_LINEAR and COLOR_TRANSFER_SDR_VIDEO are allowed.
|
||||||
|
uniform int uOutputColorTransfer;
|
||||||
|
uniform int uEnableColorTransfer;
|
||||||
|
|
||||||
|
const float inverseGamma = 0.4500;
|
||||||
|
const float gamma = 1.0 / inverseGamma;
|
||||||
|
const int GL_FALSE = 0;
|
||||||
|
const int GL_TRUE = 1;
|
||||||
|
// LINT.IfChange(color_transfer)
|
||||||
|
const int COLOR_TRANSFER_LINEAR = 1;
|
||||||
|
const int COLOR_TRANSFER_SRGB = 2;
|
||||||
|
const int COLOR_TRANSFER_SDR_VIDEO = 3;
|
||||||
|
|
||||||
|
// Transforms a single channel from electrical to optical SDR using the sRGB
|
||||||
|
// EOTF.
|
||||||
|
float srgbEotfSingleChannel(float electricalChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://developer.android.com/ndk/reference/group/a-data-space#group___a_data_space_1gga2759ad19cae46646cc5f7002758c4a1cac1bef6aa3a72abbf4a651a0bfb117f96
|
||||||
|
return electricalChannel <= 0.04045
|
||||||
|
? electricalChannel / 12.92
|
||||||
|
: pow((electricalChannel + 0.055) / 1.055, 2.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms electrical to optical SDR using the sRGB EOTF.
|
||||||
|
vec3 srgbEotf(const vec3 electricalColor) {
|
||||||
|
return vec3(srgbEotfSingleChannel(electricalColor.r),
|
||||||
|
srgbEotfSingleChannel(electricalColor.g),
|
||||||
|
srgbEotfSingleChannel(electricalColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms a single channel from electrical to optical SDR using the SMPTE
|
||||||
|
// 170M OETF.
|
||||||
|
float smpte170mEotfSingleChannel(float electricalChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en
|
||||||
|
return electricalChannel < 0.0812
|
||||||
|
? electricalChannel / 4.500
|
||||||
|
: pow((electricalChannel + 0.099) / 1.099, gamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms electrical to optical SDR using the SMPTE 170M EOTF.
|
||||||
|
vec3 smpte170mEotf(vec3 electricalColor) {
|
||||||
|
return vec3(smpte170mEotfSingleChannel(electricalColor.r),
|
||||||
|
smpte170mEotfSingleChannel(electricalColor.g),
|
||||||
|
smpte170mEotfSingleChannel(electricalColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms a single channel from optical to electrical SDR.
|
||||||
|
float smpte170mOetfSingleChannel(float opticalChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en
|
||||||
|
return opticalChannel < 0.018
|
||||||
|
? opticalChannel * 4.500
|
||||||
|
: 1.099 * pow(opticalChannel, inverseGamma) - 0.099;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms optical SDR colors to electrical SDR using the SMPTE 170M OETF.
|
||||||
|
vec3 smpte170mOetf(vec3 opticalColor) {
|
||||||
|
return vec3(smpte170mOetfSingleChannel(opticalColor.r),
|
||||||
|
smpte170mOetfSingleChannel(opticalColor.g),
|
||||||
|
smpte170mOetfSingleChannel(opticalColor.b));
|
||||||
|
}
|
||||||
|
// Applies the appropriate EOTF to convert nonlinear electrical signals to
|
||||||
|
// linear optical signals. Input and output are both normalized to [0, 1].
|
||||||
|
vec3 applyEotf(vec3 electricalColor) {
|
||||||
|
if (uEnableColorTransfer == GL_TRUE) {
|
||||||
|
if (uInputColorTransfer == COLOR_TRANSFER_SRGB) {
|
||||||
|
return srgbEotf(electricalColor);
|
||||||
|
} else if (uInputColorTransfer == COLOR_TRANSFER_SDR_VIDEO) {
|
||||||
|
return smpte170mEotf(electricalColor);
|
||||||
|
} else {
|
||||||
|
// Output blue as an obviously visible error.
|
||||||
|
return vec3(0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
} else if (uEnableColorTransfer == GL_FALSE) {
|
||||||
|
return electricalColor;
|
||||||
|
} else {
|
||||||
|
// Output blue as an obviously visible error.
|
||||||
|
return vec3(0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the appropriate OETF to convert linear optical signals to nonlinear
|
||||||
|
// electrical signals. Input and output are both normalized to [0, 1].
|
||||||
|
highp vec3 applyOetf(highp vec3 linearColor) {
|
||||||
|
if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR ||
|
||||||
|
uEnableColorTransfer == GL_FALSE) {
|
||||||
|
return linearColor;
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_SDR_VIDEO) {
|
||||||
|
return smpte170mOetf(linearColor);
|
||||||
|
} else {
|
||||||
|
// Output red as an obviously visible error.
|
||||||
|
return vec3(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getAdjustedTexSamplingCoord(vec2 originalTexSamplingCoord) {
|
||||||
|
if (uInputColorTransfer == COLOR_TRANSFER_SRGB) {
|
||||||
|
// Whereas the Android system uses the top-left corner as (0,0) of the
|
||||||
|
// coordinate system, OpenGL uses the bottom-left corner as (0,0), so the
|
||||||
|
// texture gets flipped. We flip the texture vertically to ensure the
|
||||||
|
// orientation of the output is correct.
|
||||||
|
return vec2(originalTexSamplingCoord.x, 1.0 - originalTexSamplingCoord.y);
|
||||||
|
} else {
|
||||||
|
return originalTexSamplingCoord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 inputColor =
|
||||||
|
texture2D(uTexSampler, getAdjustedTexSamplingCoord(vTexSamplingCoord));
|
||||||
|
vec3 linearInputColor = applyEotf(inputColor.rgb);
|
||||||
|
vec4 transformedColors = uRgbMatrix * vec4(linearInputColor, 1);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(applyOetf(transformedColors.rgb), inputColor.a);
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2022 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 2 fragment shader that:
|
||||||
|
// 1. Samples from uTexSampler, copying from this texture to the current
|
||||||
|
// output.
|
||||||
|
// 2. Applies a 4x4 RGB color matrix to change the pixel colors.
|
||||||
|
// 3. Transforms the optical colors to electrical colors using the SMPTE
|
||||||
|
// 170M OETF.
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
uniform sampler2D uTexSampler;
|
||||||
|
uniform mat4 uRgbMatrix;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
// C.java#ColorTransfer value.
|
||||||
|
// Only COLOR_TRANSFER_SDR and COLOR_TRANSFER_GAMMA_2_2 are allowed.
|
||||||
|
uniform int uOutputColorTransfer;
|
||||||
|
|
||||||
|
const float inverseGamma = 0.4500;
|
||||||
|
|
||||||
|
// Transforms a single channel from optical to electrical SDR using the SMPTE
|
||||||
|
// 170M OETF.
|
||||||
|
float smpte170mOetfSingleChannel(float opticalChannel) {
|
||||||
|
// Specification:
|
||||||
|
// https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en
|
||||||
|
return opticalChannel < 0.018
|
||||||
|
? opticalChannel * 4.500
|
||||||
|
: 1.099 * pow(opticalChannel, inverseGamma) - 0.099;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transforms optical SDR colors to electrical SDR using the SMPTE 170M OETF.
|
||||||
|
vec3 smpte170mOetf(vec3 opticalColor) {
|
||||||
|
return vec3(smpte170mOetfSingleChannel(opticalColor.r),
|
||||||
|
smpte170mOetfSingleChannel(opticalColor.g),
|
||||||
|
smpte170mOetfSingleChannel(opticalColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.709 gamma 2.2 OETF for one channel.
|
||||||
|
float gamma22OetfSingleChannel(highp float linearChannel) {
|
||||||
|
// Reference:
|
||||||
|
// https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_gamma22
|
||||||
|
return pow(linearChannel, (1.0 / 2.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BT.709 gamma 2.2 OETF.
|
||||||
|
vec3 gamma22Oetf(highp vec3 linearColor) {
|
||||||
|
return vec3(gamma22OetfSingleChannel(linearColor.r),
|
||||||
|
gamma22OetfSingleChannel(linearColor.g),
|
||||||
|
gamma22OetfSingleChannel(linearColor.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applies the appropriate OETF to convert linear optical signals to nonlinear
|
||||||
|
// electrical signals. Input and output are both normalized to [0, 1].
|
||||||
|
highp vec3 applyOetf(highp vec3 linearColor) {
|
||||||
|
// LINT.IfChange(color_transfer_oetf)
|
||||||
|
const int COLOR_TRANSFER_SDR_VIDEO = 3;
|
||||||
|
const int COLOR_TRANSFER_GAMMA_2_2 = 10;
|
||||||
|
if (uOutputColorTransfer == COLOR_TRANSFER_SDR_VIDEO) {
|
||||||
|
return smpte170mOetf(linearColor);
|
||||||
|
} else if (uOutputColorTransfer == COLOR_TRANSFER_GAMMA_2_2) {
|
||||||
|
return gamma22Oetf(linearColor);
|
||||||
|
} else {
|
||||||
|
// Output red as an obviously visible error.
|
||||||
|
return vec3(1.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord);
|
||||||
|
vec4 transformedColors = uRgbMatrix * vec4(inputColor.rgb, 1);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(applyOetf(transformedColors.rgb), inputColor.a);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2023 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES2 vertex shader that tiles frames horizontally.
|
||||||
|
|
||||||
|
attribute vec4 aFramePosition;
|
||||||
|
uniform int uIndex;
|
||||||
|
uniform int uCount;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Translate the coordinates from -1,+1 to 0,+2.
|
||||||
|
float x = aFramePosition.x + 1.0;
|
||||||
|
// Offset the frame by its index times its width (2).
|
||||||
|
x += float(uIndex) * 2.0;
|
||||||
|
// Shrink the frame to fit the thumbnail strip.
|
||||||
|
x /= float(uCount);
|
||||||
|
// Translate the coordinates back to -1,+1.
|
||||||
|
x -= 1.0;
|
||||||
|
|
||||||
|
gl_Position = vec4(x, aFramePosition.yzw);
|
||||||
|
vTexSamplingCoord = aFramePosition.xy * 0.5 + 0.5;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#version 100
|
||||||
|
// Copyright 2021 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 2 vertex shader that applies the 4 * 4 transformation matrices
|
||||||
|
// uTransformationMatrix and the uTexTransformationMatrix.
|
||||||
|
|
||||||
|
attribute vec4 aFramePosition;
|
||||||
|
uniform mat4 uTransformationMatrix;
|
||||||
|
uniform mat4 uTexTransformationMatrix;
|
||||||
|
varying vec2 vTexSamplingCoord;
|
||||||
|
void main() {
|
||||||
|
gl_Position = uTransformationMatrix * aFramePosition;
|
||||||
|
vec4 texturePosition = vec4(aFramePosition.x * 0.5 + 0.5,
|
||||||
|
aFramePosition.y * 0.5 + 0.5, 0.0, 1.0);
|
||||||
|
vTexSamplingCoord = (uTexTransformationMatrix * texturePosition).xy;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#version 300 es
|
||||||
|
// Copyright 2021 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// ES 3 vertex shader that applies the 4 * 4 transformation matrices
|
||||||
|
// uTransformationMatrix and the uTexTransformationMatrix.
|
||||||
|
|
||||||
|
in vec4 aFramePosition;
|
||||||
|
uniform mat4 uTransformationMatrix;
|
||||||
|
uniform mat4 uTexTransformationMatrix;
|
||||||
|
out vec2 vTexSamplingCoord;
|
||||||
|
void main() {
|
||||||
|
gl_Position = uTransformationMatrix * aFramePosition;
|
||||||
|
vec4 texturePosition = vec4(aFramePosition.x * 0.5 + 0.5,
|
||||||
|
aFramePosition.y * 0.5 + 0.5, 0.0, 1.0);
|
||||||
|
vTexSamplingCoord = (uTexTransformationMatrix * texturePosition).xy;
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import androidx.core.os.HandlerCompat;
|
|||||||
|
|
||||||
import com.fongmi.android.tv.event.EventIndex;
|
import com.fongmi.android.tv.event.EventIndex;
|
||||||
import com.fongmi.android.tv.ui.activity.CrashActivity;
|
import com.fongmi.android.tv.ui.activity.CrashActivity;
|
||||||
|
import com.fongmi.android.tv.utils.CacheCleaner;
|
||||||
import com.fongmi.android.tv.utils.Notify;
|
import com.fongmi.android.tv.utils.Notify;
|
||||||
import com.fongmi.hook.Hook;
|
import com.fongmi.hook.Hook;
|
||||||
import com.github.catvod.Init;
|
import com.github.catvod.Init;
|
||||||
@@ -41,6 +42,8 @@ public class App extends Application {
|
|||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
private final long time;
|
private final long time;
|
||||||
private Hook hook;
|
private Hook hook;
|
||||||
|
private final Runnable cleanTask;
|
||||||
|
private boolean appJustLaunched;
|
||||||
|
|
||||||
public App() {
|
public App() {
|
||||||
instance = this;
|
instance = this;
|
||||||
@@ -48,6 +51,8 @@ public class App extends Application {
|
|||||||
handler = HandlerCompat.createAsync(Looper.getMainLooper());
|
handler = HandlerCompat.createAsync(Looper.getMainLooper());
|
||||||
time = System.currentTimeMillis();
|
time = System.currentTimeMillis();
|
||||||
gson = new Gson();
|
gson = new Gson();
|
||||||
|
cleanTask = this::checkCacheClean;
|
||||||
|
appJustLaunched = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static App get() {
|
public static App get() {
|
||||||
@@ -66,6 +71,14 @@ public class App extends Application {
|
|||||||
return get().activity;
|
return get().activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isAppJustLaunched() {
|
||||||
|
return get().appJustLaunched;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setAppLaunched() {
|
||||||
|
get().appJustLaunched = false;
|
||||||
|
}
|
||||||
|
|
||||||
public static void execute(Runnable runnable) {
|
public static void execute(Runnable runnable) {
|
||||||
get().executor.execute(runnable);
|
get().executor.execute(runnable);
|
||||||
}
|
}
|
||||||
@@ -113,12 +126,15 @@ public class App extends Application {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
Notify.createChannel();
|
|
||||||
Logger.addLogAdapter(getLogAdapter());
|
Logger.addLogAdapter(getLogAdapter());
|
||||||
OkHttp.get().setProxy(Setting.getProxy());
|
OkHttp.get().setProxy(Setting.getProxy());
|
||||||
OkHttp.get().setDoh(Doh.objectFrom(Setting.getDoh()));
|
OkHttp.get().setDoh(Doh.objectFrom(Setting.getDoh()));
|
||||||
EventBus.builder().addIndex(new EventIndex()).installDefaultEventBus();
|
EventBus.builder().addIndex(new EventIndex()).installDefaultEventBus();
|
||||||
CaocConfig.Builder.create().backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT).errorActivity(CrashActivity.class).apply();
|
CaocConfig.Builder.create().backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT).errorActivity(CrashActivity.class).apply();
|
||||||
|
|
||||||
|
// 初始化自动缓存清理
|
||||||
|
initCacheCleaner();
|
||||||
|
|
||||||
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
|
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
|
||||||
@@ -133,6 +149,8 @@ public class App extends Application {
|
|||||||
@Override
|
@Override
|
||||||
public void onActivityResumed(@NonNull Activity activity) {
|
public void onActivityResumed(@NonNull Activity activity) {
|
||||||
if (activity != activity()) setActivity(activity);
|
if (activity != activity()) setActivity(activity);
|
||||||
|
// 应用回到前台时检查缓存
|
||||||
|
checkCacheClean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -156,6 +174,20 @@ public class App extends Application {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initCacheCleaner() {
|
||||||
|
CacheCleaner cleaner = CacheCleaner.get();
|
||||||
|
cleaner.setCacheThreshold(200 * 1024 * 1024); // 固定使用200MB阈值
|
||||||
|
|
||||||
|
// 定期检查缓存 (每30分钟)
|
||||||
|
post(cleanTask, 30 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCacheClean() {
|
||||||
|
CacheCleaner.get().checkAndClean();
|
||||||
|
// 每30分钟定期检查缓存
|
||||||
|
post(cleanTask, 30 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PackageManager getPackageManager() {
|
public PackageManager getPackageManager() {
|
||||||
return hook != null ? hook : getBaseContext().getPackageManager();
|
return hook != null ? hook : getBaseContext().getPackageManager();
|
||||||
|
|||||||
@@ -201,6 +201,22 @@ public class Setting {
|
|||||||
Prefers.put("update", update);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void putUseCnMirror(boolean useCnMirror) {
|
||||||
|
Prefers.put("use_cn_mirror", useCnMirror);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isCaption() {
|
public static boolean isCaption() {
|
||||||
return Prefers.getBoolean("caption");
|
return Prefers.getBoolean("caption");
|
||||||
}
|
}
|
||||||
@@ -300,4 +316,20 @@ public class Setting {
|
|||||||
public static boolean hasCaption() {
|
public static boolean hasCaption() {
|
||||||
return new Intent(Settings.ACTION_CAPTIONING_SETTINGS).resolveActivity(App.get().getPackageManager()) != null;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLiveTabVisible() {
|
||||||
|
return Prefers.getBoolean("live_tab_visible", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void putLiveTabVisible(boolean visible) {
|
||||||
|
Prefers.put("live_tab_visible", visible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ public class LiveConfig {
|
|||||||
private boolean sync;
|
private boolean sync;
|
||||||
private Live home;
|
private Live home;
|
||||||
|
|
||||||
|
private LiveConfig() {
|
||||||
|
// 在构造函数中初始化列表,防止空指针异常
|
||||||
|
this.ads = new ArrayList<>();
|
||||||
|
this.rules = new ArrayList<>();
|
||||||
|
this.lives = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
private static class Loader {
|
private static class Loader {
|
||||||
static volatile LiveConfig INSTANCE = new LiveConfig();
|
static volatile LiveConfig INSTANCE = new LiveConfig();
|
||||||
}
|
}
|
||||||
@@ -97,9 +104,9 @@ public class LiveConfig {
|
|||||||
|
|
||||||
public LiveConfig clear() {
|
public LiveConfig clear() {
|
||||||
this.home = null;
|
this.home = null;
|
||||||
this.ads.clear();
|
if (this.ads != null) this.ads.clear();
|
||||||
this.rules.clear();
|
if (this.rules != null) this.rules.clear();
|
||||||
this.lives.clear();
|
if (this.lives != null) this.lives.clear();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,17 @@ public class VodConfig {
|
|||||||
private Parse parse;
|
private Parse parse;
|
||||||
private String wall;
|
private String wall;
|
||||||
private Site home;
|
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 {
|
private static class Loader {
|
||||||
static volatile VodConfig INSTANCE = new VodConfig();
|
static volatile VodConfig INSTANCE = new VodConfig();
|
||||||
@@ -67,7 +78,35 @@ public class VodConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void load(Config config, Callback callback) {
|
public static void load(Config config, Callback callback) {
|
||||||
get().clear().config(config).load(callback);
|
// 参数检查
|
||||||
|
if (config == null || callback == null) {
|
||||||
|
if (callback != null) {
|
||||||
|
App.post(() -> callback.error("配置参数无效"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加加载状态检查,防止并发加载
|
||||||
|
VodConfig instance = get();
|
||||||
|
synchronized (instance) {
|
||||||
|
if (instance.isLoading) {
|
||||||
|
// 如果正在加载,取消之前的加载
|
||||||
|
try {
|
||||||
|
OkHttp.cancel("vod");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance.isLoading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
instance.clear().config(config).load(callback);
|
||||||
|
} catch (Exception e) {
|
||||||
|
instance.isLoading = false;
|
||||||
|
e.printStackTrace();
|
||||||
|
App.post(() -> callback.error("配置加载失败: " + e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public VodConfig init() {
|
public VodConfig init() {
|
||||||
@@ -94,12 +133,12 @@ public class VodConfig {
|
|||||||
this.wall = null;
|
this.wall = null;
|
||||||
this.home = null;
|
this.home = null;
|
||||||
this.parse = null;
|
this.parse = null;
|
||||||
this.ads.clear();
|
if (this.ads != null) this.ads.clear();
|
||||||
this.doh.clear();
|
if (this.doh != null) this.doh.clear();
|
||||||
this.rules.clear();
|
if (this.rules != null) this.rules.clear();
|
||||||
this.sites.clear();
|
if (this.sites != null) this.sites.clear();
|
||||||
this.flags.clear();
|
if (this.flags != null) this.flags.clear();
|
||||||
this.parses.clear();
|
if (this.parses != null) this.parses.clear();
|
||||||
this.loadLive = true;
|
this.loadLive = true;
|
||||||
BaseLoader.get().clear();
|
BaseLoader.get().clear();
|
||||||
return this;
|
return this;
|
||||||
@@ -114,15 +153,23 @@ public class VodConfig {
|
|||||||
OkHttp.cancel("vod");
|
OkHttp.cancel("vod");
|
||||||
checkJson(Json.parse(Decoder.getJson(UrlUtil.convert(config.getUrl()), "vod")).getAsJsonObject(), callback);
|
checkJson(Json.parse(Decoder.getJson(UrlUtil.convert(config.getUrl()), "vod")).getAsJsonObject(), callback);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
if (TextUtils.isEmpty(config.getUrl())) App.post(() -> callback.error(""));
|
if (TextUtils.isEmpty(config.getUrl())) {
|
||||||
else loadCache(callback, e);
|
isLoading = false;
|
||||||
|
App.post(() -> callback.error(""));
|
||||||
|
} else {
|
||||||
|
loadCache(callback, e);
|
||||||
|
}
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadCache(Callback callback, Throwable e) {
|
private void loadCache(Callback callback, Throwable e) {
|
||||||
if (!TextUtils.isEmpty(config.getJson())) checkJson(Json.parse(config.getJson()).getAsJsonObject(), callback);
|
if (!TextUtils.isEmpty(config.getJson())) {
|
||||||
else App.post(() -> callback.error(Notify.getError(R.string.error_config_get, e)));
|
checkJson(Json.parse(config.getJson()).getAsJsonObject(), callback);
|
||||||
|
} else {
|
||||||
|
isLoading = false;
|
||||||
|
App.post(() -> callback.error(Notify.getError(R.string.error_config_get, e)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkJson(JsonObject object, Callback callback) {
|
private void checkJson(JsonObject object, Callback callback) {
|
||||||
@@ -152,11 +199,21 @@ public class VodConfig {
|
|||||||
if (loadLive && object.has("lives")) initLive(object);
|
if (loadLive && object.has("lives")) initLive(object);
|
||||||
String notice = Json.safeString(object, "notice");
|
String notice = Json.safeString(object, "notice");
|
||||||
config.logo(Json.safeString(object, "logo"));
|
config.logo(Json.safeString(object, "logo"));
|
||||||
App.post(() -> callback.success(notice));
|
|
||||||
config.json(object.toString()).update();
|
config.json(object.toString()).update();
|
||||||
App.post(callback::success);
|
|
||||||
|
// 重置加载状态
|
||||||
|
isLoading = false;
|
||||||
|
|
||||||
|
// 只调用一次success回调,优先显示通知消息
|
||||||
|
if (!TextUtils.isEmpty(notice)) {
|
||||||
|
App.post(() -> callback.success(notice));
|
||||||
|
} else {
|
||||||
|
App.post(callback::success);
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
// 重置加载状态
|
||||||
|
isLoading = false;
|
||||||
App.post(() -> callback.error(Notify.getError(R.string.error_config_parse, e)));
|
App.post(() -> callback.error(Notify.getError(R.string.error_config_parse, e)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,10 +375,32 @@ public class VodConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setHome(Site home) {
|
public void setHome(Site home) {
|
||||||
|
if (home == null) {
|
||||||
|
// 如果传入null,使用默认站点或创建空站点
|
||||||
|
home = sites.isEmpty() ? new Site() : sites.get(0);
|
||||||
|
}
|
||||||
this.home = home;
|
this.home = home;
|
||||||
this.home.setActivated(true);
|
this.home.setActivated(true);
|
||||||
config.home(home.getKey()).save();
|
|
||||||
for (Site item : getSites()) item.setActivated(home);
|
// 安全地保存配置,防止空指针异常
|
||||||
|
try {
|
||||||
|
if (home.getKey() != null && config != null) {
|
||||||
|
config.home(home.getKey()).save();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安全地更新所有站点的激活状态
|
||||||
|
try {
|
||||||
|
for (Site item : getSites()) {
|
||||||
|
if (item != null) {
|
||||||
|
item.setActivated(home);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setWall(String wall) {
|
private void setWall(String wall) {
|
||||||
|
|||||||
@@ -267,6 +267,19 @@ public class Config {
|
|||||||
Keep.delete(getId());
|
Keep.delete(getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Config copy() {
|
||||||
|
Config copy = new Config();
|
||||||
|
copy.setType(type);
|
||||||
|
copy.setUrl(url);
|
||||||
|
copy.setJson(json);
|
||||||
|
copy.setName(TextUtils.isEmpty(name) ? url + " 副本" : name + " 副本");
|
||||||
|
copy.setLogo(logo);
|
||||||
|
copy.setHome(home);
|
||||||
|
copy.setParse(parse);
|
||||||
|
copy.setTime(System.currentTimeMillis());
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public abstract class ConfigDao extends BaseDao<Config> {
|
|||||||
@Query("SELECT * FROM Config WHERE type = :type ORDER BY time DESC")
|
@Query("SELECT * FROM Config WHERE type = :type ORDER BY time DESC")
|
||||||
public abstract List<Config> findByType(int type);
|
public abstract List<Config> findByType(int type);
|
||||||
|
|
||||||
@SuppressWarnings(RoomWarnings.QUERY_MISMATCH)
|
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
|
||||||
@Query("SELECT id, name, url, type, time FROM Config WHERE type = :type ORDER BY time DESC")
|
@Query("SELECT id, name, url, type, time FROM Config WHERE type = :type ORDER BY time DESC")
|
||||||
public abstract List<Config> findUrlByType(int type);
|
public abstract List<Config> findUrlByType(int type);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package com.fongmi.android.tv.ui.custom;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
|
|||||||
private long currentPosition;
|
private long currentPosition;
|
||||||
private long currentBuffered;
|
private long currentBuffered;
|
||||||
private boolean scrubbing;
|
private boolean scrubbing;
|
||||||
|
private boolean isPressed;
|
||||||
|
|
||||||
public CustomSeekView(Context context) {
|
public CustomSeekView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@@ -55,12 +58,94 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
|
|||||||
timeBar = findViewById(R.id.timeBar);
|
timeBar = findViewById(R.id.timeBar);
|
||||||
timeBar.addListener(this);
|
timeBar.addListener(this);
|
||||||
refresh = this::refresh;
|
refresh = this::refresh;
|
||||||
|
|
||||||
|
// 设置触摸事件监听器,实现动态尺寸调整
|
||||||
|
timeBar.setOnTouchListener((v, event) -> {
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
if (!isPressed) {
|
||||||
|
isPressed = true;
|
||||||
|
// 按下时:滑杆4dp,圆球16dp
|
||||||
|
setTimeBarSize(4, 16);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
if (isPressed) {
|
||||||
|
isPressed = false;
|
||||||
|
// 松开时:滑杆2dp,圆球12dp
|
||||||
|
setTimeBarSize(2, 12);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false; // 不拦截事件,让DefaultTimeBar正常处理
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(Players player) {
|
public void setListener(Players player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPosition(long position) {
|
||||||
|
timeBar.setPosition(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDuration(long duration) {
|
||||||
|
timeBar.setDuration(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态设置进度条高度和拖拽手柄大小
|
||||||
|
* @param barHeightDp 滑杆高度值(dp)
|
||||||
|
* @param scrubberSizeDp 拖拽手柄大小(dp)
|
||||||
|
*/
|
||||||
|
private void setTimeBarSize(int barHeightDp, int scrubberSizeDp) {
|
||||||
|
// 设置滑杆高度
|
||||||
|
int barHeightPx = (int) TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
barHeightDp,
|
||||||
|
getContext().getResources().getDisplayMetrics()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 设置拖拽手柄大小
|
||||||
|
int scrubberSizePx = (int) TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
scrubberSizeDp,
|
||||||
|
getContext().getResources().getDisplayMetrics()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 通过反射设置DefaultTimeBar的内部属性
|
||||||
|
try {
|
||||||
|
// 设置滑杆高度
|
||||||
|
java.lang.reflect.Field barHeightField = timeBar.getClass().getDeclaredField("barHeight");
|
||||||
|
barHeightField.setAccessible(true);
|
||||||
|
barHeightField.setInt(timeBar, barHeightPx);
|
||||||
|
|
||||||
|
// 设置拖拽手柄大小 - 尝试多个可能的字段名
|
||||||
|
String[] scrubberFields = {"scrubberSize", "scrubberEnabledSize", "scrubberDisabledSize"};
|
||||||
|
for (String fieldName : scrubberFields) {
|
||||||
|
try {
|
||||||
|
java.lang.reflect.Field scrubberField = timeBar.getClass().getDeclaredField(fieldName);
|
||||||
|
scrubberField.setAccessible(true);
|
||||||
|
scrubberField.setInt(timeBar, scrubberSizePx);
|
||||||
|
break; // 成功设置后退出循环
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// 继续尝试下一个字段名
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新视图
|
||||||
|
timeBar.requestLayout();
|
||||||
|
timeBar.invalidate();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果反射失败,使用备用方案
|
||||||
|
e.printStackTrace();
|
||||||
|
// 备用方案:重新设置布局参数
|
||||||
|
timeBar.getLayoutParams().height = barHeightPx;
|
||||||
|
timeBar.requestLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void start() {
|
private void start() {
|
||||||
removeCallbacks(refresh);
|
removeCallbacks(refresh);
|
||||||
post(refresh);
|
post(refresh);
|
||||||
@@ -102,7 +187,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)) {
|
if (duration > TimeUnit.HOURS.toMillis(3)) {
|
||||||
timeBar.setKeyTimeIncrement(TimeUnit.MINUTES.toMillis(5));
|
timeBar.setKeyTimeIncrement(TimeUnit.MINUTES.toMillis(5));
|
||||||
} else if (duration > TimeUnit.MINUTES.toMillis(30)) {
|
} else if (duration > TimeUnit.MINUTES.toMillis(30)) {
|
||||||
@@ -124,8 +209,22 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void seekToTimeBarPosition(long positionMs) {
|
private void seekToTimeBarPosition(long positionMs) {
|
||||||
|
// 先设置播放位置
|
||||||
player.seekTo(positionMs);
|
player.seekTo(positionMs);
|
||||||
refresh();
|
// 延迟刷新进度条,确保播放器已经处理了跳转操作
|
||||||
|
removeCallbacks(refresh);
|
||||||
|
postDelayed(() -> {
|
||||||
|
// 只有在非拖动状态下才刷新进度条位置
|
||||||
|
if (!scrubbing) {
|
||||||
|
refresh();
|
||||||
|
// 确保进度条位置与实际播放位置一致
|
||||||
|
long actualPosition = player.getPosition();
|
||||||
|
if (Math.abs(actualPosition - positionMs) > 100) { // 如果差异超过100ms,再次调整
|
||||||
|
timeBar.setPosition(actualPosition);
|
||||||
|
positionView.setText(player.stringToTime(actualPosition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 50); // 延迟50ms刷新
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -148,6 +247,17 @@ public class CustomSeekView extends FrameLayout implements TimeBar.OnScrubListen
|
|||||||
@Override
|
@Override
|
||||||
public void onScrubStop(@NonNull TimeBar timeBar, long position, boolean canceled) {
|
public void onScrubStop(@NonNull TimeBar timeBar, long position, boolean canceled) {
|
||||||
scrubbing = false;
|
scrubbing = false;
|
||||||
if (!canceled) seekToTimeBarPosition(position);
|
if (!canceled) {
|
||||||
|
// 立即设置进度条位置到目标位置,避免圆球跳回原始位置
|
||||||
|
timeBar.setPosition(position);
|
||||||
|
positionView.setText(player.stringToTime(position));
|
||||||
|
|
||||||
|
// 调整播放位置
|
||||||
|
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.ViewEmptyBinding;
|
||||||
import com.fongmi.android.tv.databinding.ViewProgressBinding;
|
import com.fongmi.android.tv.databinding.ViewProgressBinding;
|
||||||
|
import com.airbnb.lottie.LottieAnimationView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -47,7 +48,8 @@ public class ProgressLayout extends RelativeLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initView() {
|
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.setTag(TAG_PROGRESS);
|
||||||
mEmptyView.setVisibility(GONE);
|
mEmptyView.setVisibility(GONE);
|
||||||
mProgressView = ViewProgressBinding.inflate(LayoutInflater.from(getContext())).getRoot();
|
mProgressView = ViewProgressBinding.inflate(LayoutInflater.from(getContext())).getRoot();
|
||||||
@@ -103,21 +105,46 @@ public class ProgressLayout extends RelativeLayout {
|
|||||||
case CONTENT:
|
case CONTENT:
|
||||||
mEmptyView.setVisibility(GONE);
|
mEmptyView.setVisibility(GONE);
|
||||||
mProgressView.setVisibility(GONE);
|
mProgressView.setVisibility(GONE);
|
||||||
|
pauseLottieAnimation();
|
||||||
setContentVisibility(true);
|
setContentVisibility(true);
|
||||||
break;
|
break;
|
||||||
case PROGRESS:
|
case PROGRESS:
|
||||||
mEmptyView.setVisibility(GONE);
|
mEmptyView.setVisibility(GONE);
|
||||||
mProgressView.setVisibility(VISIBLE);
|
mProgressView.setVisibility(VISIBLE);
|
||||||
|
pauseLottieAnimation();
|
||||||
setContentVisibility(false);
|
setContentVisibility(false);
|
||||||
break;
|
break;
|
||||||
case EMPTY:
|
case EMPTY:
|
||||||
mEmptyView.setVisibility(VISIBLE);
|
mEmptyView.setVisibility(VISIBLE);
|
||||||
mProgressView.setVisibility(GONE);
|
mProgressView.setVisibility(GONE);
|
||||||
|
playLottieAnimation();
|
||||||
setContentVisibility(false);
|
setContentVisibility(false);
|
||||||
break;
|
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) {
|
private void setContentVisibility(boolean visible) {
|
||||||
for (View view : mContentViews) {
|
for (View view : mContentViews) {
|
||||||
if (visible) showView(view);
|
if (visible) showView(view);
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.fongmi.android.tv.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.fongmi.android.tv.bean.History;
|
||||||
|
import com.fongmi.android.tv.databinding.DialogLastWatchBinding;
|
||||||
|
import com.fongmi.android.tv.ui.activity.VideoActivity;
|
||||||
|
import com.fongmi.android.tv.utils.ResUtil;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
public class LastWatchDialog {
|
||||||
|
|
||||||
|
private final DialogLastWatchBinding binding;
|
||||||
|
private final AlertDialog dialog;
|
||||||
|
private final Activity activity;
|
||||||
|
private final History history;
|
||||||
|
|
||||||
|
public static LastWatchDialog create(Activity activity, History history) {
|
||||||
|
return new LastWatchDialog(activity, history);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LastWatchDialog(Activity activity, History history) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.history = history;
|
||||||
|
this.binding = DialogLastWatchBinding.inflate(LayoutInflater.from(activity));
|
||||||
|
this.dialog = new MaterialAlertDialogBuilder(activity).setView(binding.getRoot()).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
initView();
|
||||||
|
initEvent();
|
||||||
|
dialog.getWindow().setDimAmount(0.5f);
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView() {
|
||||||
|
binding.content.setText(history.getVodName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initEvent() {
|
||||||
|
binding.play.setOnClickListener(v -> {
|
||||||
|
dismiss();
|
||||||
|
VideoActivity.start(activity, history.getSiteKey(), history.getVodId(), history.getVodName(), history.getVodPic());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss() {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package com.fongmi.android.tv.ui.dialog;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.AccelerateInterpolator;
|
||||||
|
import android.view.animation.DecelerateInterpolator;
|
||||||
|
import android.widget.PopupWindow;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.fongmi.android.tv.R;
|
||||||
|
import com.fongmi.android.tv.bean.History;
|
||||||
|
import com.fongmi.android.tv.ui.activity.VideoActivity;
|
||||||
|
|
||||||
|
public class LastWatchToast {
|
||||||
|
|
||||||
|
private final Activity activity;
|
||||||
|
private final History history;
|
||||||
|
private final Handler handler;
|
||||||
|
private PopupWindow popupWindow;
|
||||||
|
private View contentView;
|
||||||
|
private static final int ANIMATION_DURATION = 300; // 动画持续时间(毫秒)
|
||||||
|
private static final int DISPLAY_DURATION = 2500; // 显示持续时间(毫秒)
|
||||||
|
|
||||||
|
public static LastWatchToast create(Activity activity, History history) {
|
||||||
|
return new LastWatchToast(activity, history);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LastWatchToast(Activity activity, History history) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.history = history;
|
||||||
|
this.handler = new Handler(Looper.getMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
if (popupWindow != null && popupWindow.isShowing()) {
|
||||||
|
// 如果已经显示,先取消当前显示的,然后重新显示
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
contentView = LayoutInflater.from(activity).inflate(R.layout.view_last_watch_toast, null);
|
||||||
|
TextView title = contentView.findViewById(R.id.title);
|
||||||
|
TextView content = contentView.findViewById(R.id.content);
|
||||||
|
|
||||||
|
title.setText(R.string.last_watch);
|
||||||
|
content.setText(history.getVodName());
|
||||||
|
|
||||||
|
// 设置点击事件
|
||||||
|
contentView.setOnClickListener(v -> {
|
||||||
|
dismiss();
|
||||||
|
VideoActivity.start(activity, history.getSiteKey(), history.getVodId(), history.getVodName(), history.getVodPic());
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化时设置透明度为0,准备执行淡入动画
|
||||||
|
contentView.setAlpha(0f);
|
||||||
|
|
||||||
|
// 创建PopupWindow
|
||||||
|
popupWindow = new PopupWindow(contentView,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
true);
|
||||||
|
|
||||||
|
// 设置背景为透明,避免PopupWindow有默认背景
|
||||||
|
popupWindow.setBackgroundDrawable(null);
|
||||||
|
popupWindow.setOutsideTouchable(true);
|
||||||
|
|
||||||
|
// 在屏幕中央显示
|
||||||
|
popupWindow.showAtLocation(activity.getWindow().getDecorView(), Gravity.CENTER, 0, 0);
|
||||||
|
|
||||||
|
// 淡入动画
|
||||||
|
animateIn();
|
||||||
|
|
||||||
|
// 一段时间后自动关闭
|
||||||
|
handler.removeCallbacksAndMessages(null);
|
||||||
|
handler.postDelayed(this::animateOut, DISPLAY_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateIn() {
|
||||||
|
ObjectAnimator fadeIn = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
|
||||||
|
fadeIn.setDuration(ANIMATION_DURATION);
|
||||||
|
fadeIn.setInterpolator(new DecelerateInterpolator());
|
||||||
|
fadeIn.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateOut() {
|
||||||
|
if (contentView == null || popupWindow == null || !popupWindow.isShowing()) return;
|
||||||
|
|
||||||
|
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
|
||||||
|
fadeOut.setDuration(ANIMATION_DURATION);
|
||||||
|
fadeOut.setInterpolator(new AccelerateInterpolator());
|
||||||
|
fadeOut.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fadeOut.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismiss() {
|
||||||
|
handler.removeCallbacksAndMessages(null);
|
||||||
|
if (popupWindow != null && popupWindow.isShowing()) {
|
||||||
|
popupWindow.dismiss();
|
||||||
|
}
|
||||||
|
popupWindow = null;
|
||||||
|
contentView = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.fongmi.android.tv.utils;
|
||||||
|
|
||||||
|
import android.os.StatFs;
|
||||||
|
|
||||||
|
import com.fongmi.android.tv.App;
|
||||||
|
import com.fongmi.android.tv.impl.Callback;
|
||||||
|
import com.github.catvod.utils.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存自动清理管理器
|
||||||
|
*/
|
||||||
|
public class CacheCleaner {
|
||||||
|
|
||||||
|
// 默认缓存清理阈值 200MB
|
||||||
|
private static final long DEFAULT_CACHE_THRESHOLD = 200 * 1024 * 1024;
|
||||||
|
// 最小保留空间 500MB
|
||||||
|
private static final long MIN_FREE_SPACE = 500 * 1024 * 1024;
|
||||||
|
// 单例实例
|
||||||
|
private static CacheCleaner instance;
|
||||||
|
// 缓存清理阈值
|
||||||
|
private long cacheThreshold;
|
||||||
|
|
||||||
|
private CacheCleaner() {
|
||||||
|
this.cacheThreshold = DEFAULT_CACHE_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CacheCleaner get() {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (CacheCleaner.class) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new CacheCleaner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置缓存阈值
|
||||||
|
* @param threshold 阈值大小(字节)
|
||||||
|
*/
|
||||||
|
public void setCacheThreshold(long threshold) {
|
||||||
|
this.cacheThreshold = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查缓存,如果超过阈值则清理
|
||||||
|
*/
|
||||||
|
public void checkAndClean() {
|
||||||
|
App.execute(() -> {
|
||||||
|
try {
|
||||||
|
// 获取当前缓存大小
|
||||||
|
long cacheSize = FileUtil.getDirectorySize(Path.cache());
|
||||||
|
// 获取剩余存储空间
|
||||||
|
long freeSpace = getAvailableStorageSpace();
|
||||||
|
|
||||||
|
// 如果缓存超过阈值或可用空间低于最小要求,清理缓存
|
||||||
|
if (cacheSize > cacheThreshold || freeSpace < MIN_FREE_SPACE) {
|
||||||
|
cleanCache();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理缓存
|
||||||
|
*/
|
||||||
|
private void cleanCache() {
|
||||||
|
FileUtil.clearCache(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void success() {
|
||||||
|
// 缓存清理成功
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设备可用存储空间
|
||||||
|
* @return 可用空间(字节)
|
||||||
|
*/
|
||||||
|
private long getAvailableStorageSpace() {
|
||||||
|
try {
|
||||||
|
StatFs stat = new StatFs(Path.cache().getPath());
|
||||||
|
return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,13 @@ package com.fongmi.android.tv.utils;
|
|||||||
|
|
||||||
import com.fongmi.android.tv.App;
|
import com.fongmi.android.tv.App;
|
||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
|
import com.github.catvod.utils.Logger;
|
||||||
import com.github.catvod.utils.Path;
|
import com.github.catvod.utils.Path;
|
||||||
import com.google.common.net.HttpHeaders;
|
import com.google.common.net.HttpHeaders;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@@ -47,14 +49,22 @@ public class Download {
|
|||||||
private void doInBackground() {
|
private void doInBackground() {
|
||||||
try (Response res = OkHttp.newCall(url, url).execute()) {
|
try (Response res = OkHttp.newCall(url, url).execute()) {
|
||||||
Path.create(file);
|
Path.create(file);
|
||||||
download(res.body().byteStream(), Double.parseDouble(res.header(HttpHeaders.CONTENT_LENGTH, "1")));
|
long expectedLength = Long.parseLong(res.header(HttpHeaders.CONTENT_LENGTH, "0"));
|
||||||
|
download(res.body().byteStream(), expectedLength);
|
||||||
|
|
||||||
|
// 验证下载的文件
|
||||||
|
if (!verifyDownloadedFile(file, expectedLength)) {
|
||||||
|
App.post(() -> {if (callback != null) callback.error("下载的文件可能已损坏,请重试");});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
App.post(() -> {if (callback != null) callback.success(file);});
|
App.post(() -> {if (callback != null) callback.success(file);});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
App.post(() -> {if (callback != null) callback.error(e.getMessage());});
|
App.post(() -> {if (callback != null) callback.error(e.getMessage());});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void download(InputStream is, double length) throws Exception {
|
private void download(InputStream is, long length) throws Exception {
|
||||||
try (BufferedInputStream input = new BufferedInputStream(is); FileOutputStream os = new FileOutputStream(file)) {
|
try (BufferedInputStream input = new BufferedInputStream(is); FileOutputStream os = new FileOutputStream(file)) {
|
||||||
byte[] buffer = new byte[4096];
|
byte[] buffer = new byte[4096];
|
||||||
int readBytes;
|
int readBytes;
|
||||||
@@ -68,6 +78,45 @@ public class Download {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean verifyDownloadedFile(File file, long expectedLength) {
|
||||||
|
try {
|
||||||
|
// 检查文件大小
|
||||||
|
if (file.length() != expectedLength) {
|
||||||
|
Logger.e("File size mismatch: expected " + expectedLength + ", actual " + file.length());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查APK文件头 (ZIP文件头)
|
||||||
|
if (file.length() < 4) return false;
|
||||||
|
|
||||||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
||||||
|
byte[] header = new byte[4];
|
||||||
|
fis.read(header);
|
||||||
|
// ZIP文件头应该是 0x504B0304 (PK..)
|
||||||
|
if (header[0] != 0x50 || header[1] != 0x4B || header[2] != 0x03 || header[3] != 0x04) {
|
||||||
|
Logger.e("Invalid APK file header");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 额外验证:检查APK文件是否完整
|
||||||
|
// 尝试读取ZIP文件结构
|
||||||
|
fis.getChannel().position(0);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead = fis.read(buffer);
|
||||||
|
if (bytesRead < 4) {
|
||||||
|
Logger.e("APK file too small or corrupted");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.d("APK file verification passed: " + file.getName() + " (" + file.length() + " bytes)");
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("File verification failed: " + e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
|
||||||
void progress(int progress);
|
void progress(int progress);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.core.content.FileProvider;
|
|||||||
import com.fongmi.android.tv.App;
|
import com.fongmi.android.tv.App;
|
||||||
import com.fongmi.android.tv.R;
|
import com.fongmi.android.tv.R;
|
||||||
import com.fongmi.android.tv.impl.Callback;
|
import com.fongmi.android.tv.impl.Callback;
|
||||||
|
import com.github.catvod.utils.Logger;
|
||||||
import com.github.catvod.utils.Path;
|
import com.github.catvod.utils.Path;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
@@ -34,11 +35,35 @@ public class FileUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void openFile(File file) {
|
public static void openFile(File file) {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
try {
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
intent.setDataAndType(getShareUri(file), FileUtil.getMimeType(file.getName()));
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
App.get().startActivity(intent);
|
|
||||||
|
// 对于APK文件,使用特定的MIME类型
|
||||||
|
String mimeType = file.getName().toLowerCase().endsWith(".apk") ?
|
||||||
|
"application/vnd.android.package-archive" : getMimeType(file.getName());
|
||||||
|
|
||||||
|
intent.setDataAndType(getShareUri(file), mimeType);
|
||||||
|
|
||||||
|
// 添加额外的安装权限检查
|
||||||
|
if (file.getName().toLowerCase().endsWith(".apk")) {
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
App.get().startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("Failed to open file: " + e.getMessage());
|
||||||
|
// 如果失败,尝试使用通用方式
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.setDataAndType(getShareUri(file), "*/*");
|
||||||
|
App.get().startActivity(intent);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.e("Fallback open file also failed: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void gzipCompress(File target) {
|
public static void gzipCompress(File target) {
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import android.Manifest;
|
|||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@@ -25,6 +28,7 @@ public class Notify {
|
|||||||
public static final int ID = 9527;
|
public static final int ID = 9527;
|
||||||
private AlertDialog mDialog;
|
private AlertDialog mDialog;
|
||||||
private Toast mToast;
|
private Toast mToast;
|
||||||
|
private Handler mHandler;
|
||||||
|
|
||||||
private static class Loader {
|
private static class Loader {
|
||||||
static volatile Notify INSTANCE = new Notify();
|
static volatile Notify INSTANCE = new Notify();
|
||||||
@@ -57,6 +61,14 @@ public class Notify {
|
|||||||
get().makeText(text);
|
get().makeText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showCenter(int resId) {
|
||||||
|
if (resId != 0) showCenter(ResUtil.getString(resId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showCenter(String text) {
|
||||||
|
get().makeTextCenter(text);
|
||||||
|
}
|
||||||
|
|
||||||
public static void progress(Context context) {
|
public static void progress(Context context) {
|
||||||
dismiss();
|
dismiss();
|
||||||
get().create(context);
|
get().create(context);
|
||||||
@@ -78,12 +90,38 @@ public class Notify {
|
|||||||
|
|
||||||
private void makeText(String message) {
|
private void makeText(String message) {
|
||||||
if (mToast != null) mToast.cancel();
|
if (mToast != null) mToast.cancel();
|
||||||
|
if (mHandler == null) mHandler = new Handler(Looper.getMainLooper());
|
||||||
if (TextUtils.isEmpty(message)) return;
|
if (TextUtils.isEmpty(message)) return;
|
||||||
mToast = new Toast(App.get());
|
mToast = new Toast(App.get());
|
||||||
TextView view = (TextView) LayoutInflater.from(App.get()).inflate(R.layout.view_toast, null);
|
TextView view = (TextView) LayoutInflater.from(App.get()).inflate(R.layout.view_toast, null);
|
||||||
view.setText(message);
|
view.setText(message);
|
||||||
mToast.setView(view);
|
mToast.setView(view);
|
||||||
mToast.setDuration(Toast.LENGTH_LONG);
|
mToast.setDuration(Toast.LENGTH_SHORT);
|
||||||
mToast.show();
|
mToast.show();
|
||||||
|
|
||||||
|
// 1秒后取消Toast
|
||||||
|
mHandler.removeCallbacksAndMessages(null);
|
||||||
|
mHandler.postDelayed(() -> {
|
||||||
|
if (mToast != null) mToast.cancel();
|
||||||
|
}, 1000); // 1000毫秒 = 1秒
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeTextCenter(String message) {
|
||||||
|
if (mToast != null) mToast.cancel();
|
||||||
|
if (mHandler == null) mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
if (TextUtils.isEmpty(message)) return;
|
||||||
|
mToast = new Toast(App.get());
|
||||||
|
TextView view = (TextView) LayoutInflater.from(App.get()).inflate(R.layout.view_toast, null);
|
||||||
|
view.setText(message);
|
||||||
|
mToast.setView(view);
|
||||||
|
mToast.setDuration(Toast.LENGTH_SHORT);
|
||||||
|
mToast.setGravity(Gravity.CENTER, 0, 0);
|
||||||
|
mToast.show();
|
||||||
|
|
||||||
|
// 1秒后取消Toast
|
||||||
|
mHandler.removeCallbacksAndMessages(null);
|
||||||
|
mHandler.postDelayed(() -> {
|
||||||
|
if (mToast != null) mToast.cancel();
|
||||||
|
}, 1000); // 1000毫秒 = 1秒
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="?attr/colorPrimary" />
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<?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:paddingTop="8dp"
|
||||||
|
android:paddingBottom="24dp"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="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>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="@string/last_watch"
|
||||||
|
android:textColor="?attr/colorPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/play"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/play"
|
||||||
|
android:background="@drawable/shape_blue"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -19,6 +19,6 @@
|
|||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:text="@string/error_empty"
|
android:text="@string/error_empty"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</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>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="@drawable/bg_toast"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:src="@drawable/ic_notify_play"
|
||||||
|
android:contentDescription="@string/play"
|
||||||
|
android:tint="@color/yellow_500" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:text="@string/last_watch"
|
||||||
|
android:textColor="@color/yellow_500"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
@@ -73,11 +73,14 @@
|
|||||||
<string name="setting_subscription">订阅管理</string>
|
<string name="setting_subscription">订阅管理</string>
|
||||||
<string name="setting_player">播放设置</string>
|
<string name="setting_player">播放设置</string>
|
||||||
<string name="setting_incognito">无痕模式</string>
|
<string name="setting_incognito">无痕模式</string>
|
||||||
|
<string name="setting_live_tab_visible">隐藏直播</string>
|
||||||
<string name="setting_quality">图片品质</string>
|
<string name="setting_quality">图片品质</string>
|
||||||
<string name="setting_size">图片尺寸</string>
|
<string name="setting_size">图片尺寸</string>
|
||||||
<string name="setting_doh">DoH</string>
|
<string name="setting_doh">DoH</string>
|
||||||
<string name="setting_proxy">Proxy</string>
|
<string name="setting_proxy">Proxy</string>
|
||||||
<string name="setting_cache">缓存</string>
|
<string name="setting_cache">缓存</string>
|
||||||
|
<string name="setting_auto_clean">自动清理缓存</string>
|
||||||
|
<string name="setting_cache_threshold">清理阈值</string>
|
||||||
<string name="setting_backup">备份</string>
|
<string name="setting_backup">备份</string>
|
||||||
<string name="setting_restore">恢复</string>
|
<string name="setting_restore">恢复</string>
|
||||||
<string name="setting_version">版本</string>
|
<string name="setting_version">版本</string>
|
||||||
@@ -88,6 +91,8 @@
|
|||||||
<string name="setting_app">应用设置</string>
|
<string name="setting_app">应用设置</string>
|
||||||
<string name="setting_network">网络设置</string>
|
<string name="setting_network">网络设置</string>
|
||||||
<string name="setting_data">数据管理</string>
|
<string name="setting_data">数据管理</string>
|
||||||
|
<string name="app_version">v3.0.3</string>
|
||||||
|
<string name="about_github">在GitHub上查看</string>
|
||||||
|
|
||||||
<!-- Backup & Restore -->
|
<!-- Backup & Restore -->
|
||||||
<string name="restore_select">选择备份</string>
|
<string name="restore_select">选择备份</string>
|
||||||
@@ -121,7 +126,7 @@
|
|||||||
<string name="dialog_edit">修改</string>
|
<string name="dialog_edit">修改</string>
|
||||||
<string name="dialog_positive">确定</string>
|
<string name="dialog_positive">确定</string>
|
||||||
<string name="dialog_negative">取消</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_hint">请输入接口…</string>
|
||||||
<string name="dialog_config_name">请输入名称…</string>
|
<string name="dialog_config_name">请输入名称…</string>
|
||||||
<string name="dialog_config_url">请输入地址…</string>
|
<string name="dialog_config_url">请输入地址…</string>
|
||||||
@@ -143,6 +148,7 @@
|
|||||||
<string name="error_cast_file">不支持的文件格式</string>
|
<string name="error_cast_file">不支持的文件格式</string>
|
||||||
<string name="error_device_limit">设备授权数已达上限</string>
|
<string name="error_device_limit">设备授权数已达上限</string>
|
||||||
<string name="error_live_empty">该订阅无直播内容</string>
|
<string name="error_live_empty">该订阅无直播内容</string>
|
||||||
|
<string name="error_no_live">当前源没有直播内容</string>
|
||||||
|
|
||||||
<!-- Update -->
|
<!-- Update -->
|
||||||
<string name="update_version">发现新版本 <xliff:g name="name">%s</xliff:g></string>
|
<string name="update_version">发现新版本 <xliff:g name="name">%s</xliff:g></string>
|
||||||
@@ -166,6 +172,7 @@
|
|||||||
<string name="none">无</string>
|
<string name="none">无</string>
|
||||||
<string name="times">倍</string>
|
<string name="times">倍</string>
|
||||||
<string name="lines">行</string>
|
<string name="lines">行</string>
|
||||||
|
<string name="last_watch">上次播放</string>
|
||||||
|
|
||||||
<string-array name="select_decode">
|
<string-array name="select_decode">
|
||||||
<item>软解</item>
|
<item>软解</item>
|
||||||
@@ -209,4 +216,10 @@
|
|||||||
<item>选择字幕</item>
|
<item>选择字幕</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string name="source_hint">您还没有添加视频源\n点击下方按钮添加</string>
|
||||||
|
<string name="add_source">添加源</string>
|
||||||
|
<string name="source_hint_setting">添加视频源</string>
|
||||||
|
<string name="source_hint_live">添加直播源</string>
|
||||||
|
<string name="source_hint_wall">添加壁纸源</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -76,7 +76,9 @@
|
|||||||
<string name="setting_size">圖片尺寸</string>
|
<string name="setting_size">圖片尺寸</string>
|
||||||
<string name="setting_doh">DoH</string>
|
<string name="setting_doh">DoH</string>
|
||||||
<string name="setting_proxy">Proxy</string>
|
<string name="setting_proxy">Proxy</string>
|
||||||
<string name="setting_cache">暫存</string>
|
<string name="setting_cache">緩存</string>
|
||||||
|
<string name="setting_auto_clean">自動清理緩存</string>
|
||||||
|
<string name="setting_cache_threshold">清理閾值</string>
|
||||||
<string name="setting_backup">備份</string>
|
<string name="setting_backup">備份</string>
|
||||||
<string name="setting_restore">還原</string>
|
<string name="setting_restore">還原</string>
|
||||||
<string name="setting_version">版本</string>
|
<string name="setting_version">版本</string>
|
||||||
@@ -87,6 +89,8 @@
|
|||||||
<string name="setting_app">應用設置</string>
|
<string name="setting_app">應用設置</string>
|
||||||
<string name="setting_network">網絡設置</string>
|
<string name="setting_network">網絡設置</string>
|
||||||
<string name="setting_data">數據管理</string>
|
<string name="setting_data">數據管理</string>
|
||||||
|
<string name="app_version">v3.0.3</string>
|
||||||
|
<string name="about_github">在GitHub上查看</string>
|
||||||
|
|
||||||
<!-- Backup & Restore -->
|
<!-- Backup & Restore -->
|
||||||
<string name="restore_select">選擇備份</string>
|
<string name="restore_select">選擇備份</string>
|
||||||
@@ -137,7 +141,11 @@
|
|||||||
<string name="error_play_flag">暫無線路資料</string>
|
<string name="error_play_flag">暫無線路資料</string>
|
||||||
<string name="error_play_timeout">連線逾時</string>
|
<string name="error_play_timeout">連線逾時</string>
|
||||||
<string name="error_detail">暫無播放資料</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>
|
||||||
|
<string name="error_no_live">當前源沒有直播內容</string>
|
||||||
|
|
||||||
<!-- Update -->
|
<!-- Update -->
|
||||||
<string name="update_version">發現新版本 <xliff:g name="name">%s</xliff:g></string>
|
<string name="update_version">發現新版本 <xliff:g name="name">%s</xliff:g></string>
|
||||||
@@ -204,4 +212,8 @@
|
|||||||
<item>選擇字幕</item>
|
<item>選擇字幕</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string name="config_set_current">已設為當前點播源</string>
|
||||||
|
|
||||||
|
<string name="source_hint">點我添加源</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
|
|
||||||
<!-- Push -->
|
<!-- Push -->
|
||||||
<string name="push">Push</string>
|
<string name="push">Push</string>
|
||||||
<string name="push_image" translatable="false">data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCADwAPADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvN7bxhffFCaRPB+oWZ8KSQX1hd65C7reWd/GwRPKjZdrAHLZPB4OcY3aPxe1j+z/CiWMeo6lo95rN3DpdpqOlWn2ma2mkJ2vt/u/KQT2zXXafafYbGGAyNO6KA8zKqtI38TkKAMsck4AGSafmI42H4S2T7Z7/AFjWNQ1JvDx8OT3rXjI00JOWmKrwJicnzBzzVO78L+JfAdi174a1C88RwaZoK6fZeGb6WNftdyjArPJdPzvK5B6A8dMV6PRRcLGL4a8VWHiaK7S1u7Wa+sJRa6hbWtwJvslwFDPEzDHK7sdB0+orarloPDOo6f47bUtPudPsfD9xbOb7TorBRPdXhYYnaYEHIQbec5/IjqaACiiikMKKimuIrdQZZFjBzjccZwCTj8AT+FcVbfFrTNdl0dPDdpeeI4dYtbq4sdSs4j9g3QEqUln/AOWe5xtBKkZpgd1UX2iIXAg81PPKlxFuG4qCATj0yRz71wVjp/jnxZb2E+sXMPhW0vNImttS0izcS3NvdOSFlhuVPBVeRjPP5i/ovw98PeC5bHXLqV7vWNN0lNJfxDq0+65ktVbdiV+FJLcliM5oEdnRXiev/tWeG21R9G8D6bqPxI11Tg2+gRFreM88yXB+RRx1G6vmb9ov9or4iaes+jar4lsvDmqS5RvDXhKXzbi2BGALq852tkn5Isk4525BNqDZLmkfY/xI+O3gT4Sqn/CUeIrWwuHIC2ke6a4Oe/lICwHuRj3rvq/NT4Xfs1apF8XvhtbeLA41zWJn1++0uYkvZ2MBDJ5+cnfM4K7eq4AJ3MQn6V0pRUbWCMnLcKKKKgsKKKKACiiigAooooAKKKKACiiigAooooA4T4xag2h+G7HW21bVdLtdK1O2u7mPR7X7RLex79n2ZkHOx2dckcjFd0DkA0yaLzoZI9zJvUruQ4YZHUHsa8w03xO3wa06503xXPeDwpo9rB5fjLW75Jpb6eWVgY2RBuypKjOOnX1L3Fsep0VV/tSz3zobuAPAVWVTIAYywyoYdiQRjPrXOeJvil4d8K2eoTz3v219PuILW7ttOQ3M0EkxAjDomSuc55xxSGdbSEgdTiuDvtY8d61caja6Potjof2DVoI0vdYmM0WoWOA0rxLH80b/AMI3gjrz6DfCW11a4d/Eeq3/AIiSHXl17TY7iUxf2fIgxHGhjILIvXaxIJ5INMRcuPiv4eFzp0NjcS62LzVW0Yy6TGbmO3uVXcyzMvCBR1J6d6qaXqHjzxDcaRdzadY+FbW31C5j1HT7qQXct1aqCsLxSJgIWPzEMMjgfXrrPTNP0OK6a0tbXT45pnu7gwxrEHkbl5HwBlj1LHk968s8XftSeDtD1JtG0D7Z468R52jSvDUJumBzj55B8ij15JGDxTWuyFtudP4d+EemaU/h+81W+1DxPrmhSXUljrGq3Ba4j+0Z8xcrtDLtO0BgcAYFTeL/AIjeB/g3o0Z1vVtN8O2aL+5tFwrMOeI4UG49/uivL7yL4y/Ej59Y1bT/AIReH5RkWmnkXurOvXBk+4h6cryOeKs+EPhD4D+H94dQ0/Rn13XmO59e8RSG8u2b+8C3Cn3UCnp1YteiGN8bviB8TPk+GvgWSw0uQceJvGGbW3x03xwD55B1wenHIqnH8ALTxMZNX+K/jW+8di0BuJraSb7BotoB8xJRSAQoH3ieg5Fdf8QPiFo/w98PN4g8caubCw5+z2i8z3bgZ2RRjqTxz0Gckgc14Z8QbzUPHHhgeMvjF9o8E/DS2kX+yfAdk5S+1iT7yC4OQctj7vBABPyY3tUb9NBO3XUrfEv9oaN/CF7pvwrji8DfD2wf7Jc+Kre0ET3UxGPs+nQjaXlIGS/BHUmMYZtf9mL9l218L6pY+N/GGl+Tr87CTR9Bum82TT16i4uWIG+4P3ugCHsrYWLofhf8L72+1XTfiH8QtLt9O1C1jC+FfBMMYW10C36q7RgAed0PIypAJAYARegeOfHLeAfh/wCL/GtzIDPptg7W7Sfda4f5IV+hcov/AAKm5fZiJR+1Iwfgj/xX/wAcvil8QHzJaWlwnhXS3PIEVvhrgqe6tLtYY9TXvtea/s5eA3+HPwX8L6POpW/NqLu8LcsbiYmWQMe5Bfb/AMBFelVnLcuOwUUUVJQUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAVFc2sN5C8NxEk8LjDRyKGVvqDUtFAHGa38HfBviKLXY9Q0OG4XXJoZ9R/eSIbiSH/AFTMVYEFe2MdBXSWeg6bpupahqFpp9tbX+oMjXl1DCqy3BRdqGRgMttXgZ6CvOvHH7SngfwXfHS476XxJ4hJ2ponh+I3l0zehCfKp6feI61yd1q3xn+JibkTTvhB4ek4868K32rSL/sp9yM4zwfmGRV2fUm66HsPjDx54d+H+mtqHiPWrLRrQdJLuYIW9lHVj7AE15G37Q/iX4ifuvhT4Fu9atW4/wCEi8QBrHTR/tKD88o6cDB5qDw78FfA3hfVP7Xu7S68c+JDy+teKJjdPu9URvlUZJxgAj1rubzV7q+ULJKRGOBGnyqPbApaINWec3nwZv8AxlJ5/wAVvHl94rXO4+HNDJstMX/ZbaQ0g9yQeTXd6Db6Z4N0saZ4X0ex8O6eP+WNjCqFvdmxlj7nn3ptWLWza4jlmd0t7WFTJNczMFjiUDJZmPAAAJpXbHZIYqzXk4UB5pXP1JrjfiB8XLPwBrUHhXw9ph8b/Eu8GLXQ7RgYrQkcSXL5wigHcQSOOSVB31zuofFHXfixeXvhz4RSjS/D0BMOs/Ea+iIhhAALpaA43uAcbvcEbRtkq/oz+Av2Z/hze6zEJotMlk/e6tcYfVfEdyckIhODsJz6D7x4+ZjMpKDUXrJ9DSNOVSLmtIrd/p5spTeEdI+C9nJ8WvjZrY8W+OFIFjbp81vaScmO3soTgF85O8gY+98uGdrvgXwLrfivxRbfFT4qW6jxABu8OeE3yYdEiJyskinrOcA8jKkAnDACJvw9+Hus+JPEtt8UvilbKPEIXPh3woR+40OHqrup/wCW3Q8jKkZPzYEfpdxcSXczyyuXkY5JNdlGiqCet5Pd9/8AgHm4rFPFSVo8sF8MVsv82+r6hcXEl3M8srF5GOSTUVFFanIM1bxNB4C8I+JPFlyFaLRdPluVRzgSSBTsT6scL/wIVn/s+2lh8Jfgz4NtfEeow2Ws+IphPI144V7m+uiZRH7vjC4/2a5f47Wp8Taf4A+G0fL+MNbSe/QDOdPtcTTZ9ORGR9DXqXiC4XVPip4Y0O3vtBki0+1m1O90e8t/Mvdn+rt7i3OMRhJAQW9Gx6VfQjqd/RRRUFhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQBDdWsN9byW9zDHcQSDa8UqhlYehB4Irx3xR+yX4D1jUW1bQob3wLr3JXUvC9y1m4J55QfJjPXCgn1r2iimm1sJpPc+eLjQfjr8OU2wXei/F7RY+lvqKDT9SC+iyD92T/tMST6VRs/2iPBf9oJpfjCw1r4Ya2/At/EFowtnP8AsTAEFf8AabaK+la5j4m6VZax8PvENvf2dvfW/wBgnfyrmJZE3CNiDhgRkVV090TZrZngGqfFjXPijql/4S+DBjjtLcmPWPiFeri0tFAyy22fvt2DfUjjEgy9Hk8O/B3TbnTfBHmX2t3m7+1vF18S95fSFizFWOdqluePQHk/Oa2h3c2m/s0/CqytHNta3djNLcQw/KsrB1ILY68sx+pzU3gex0uD+1fEXiLjw5oFq99e5x+82glIxkjLMRwuecY7181mGMrTr/UcPo9rn3uT5XhaWD/tbGvmSu0umjt823t0GSSaJ8O/C7+OvHTudPLH+z9Lzm41SfqFUH+HPU/ieOvRfDr4d6z4i8TW/wAU/inbofEe3Ph7wqw/caJD1R3Q/wDLbocHlTyfnwI/G/CHxK1CP4hWHxa+M/g3W59Gngjfw3eWdv5umaUpJ2v5WeGwAysTu/iCk7GX6o8O+JtD+J1rJqnhXxFZeJYT80gt5MTR56b4zhk+hA+lezhcHDA0+SG73fc+WzDM6uaVvaVNEto9l/XUs3FxJdTPLKxeRjkk1FT5I3hcpIjIw6qwwaZXSecFS20DXVxFEv3pGCj8TUVStrlr4R0XWvEt9/x5aLYzXsgzjdsQnaPc4IHvigDlvAca+PP2nvGWvgb9L8HafD4bsTn5ftD/AL24YD+8vEZ9sV33gu+HiLx54v1OLUdE1Sxs5o9KtvsVvi9s5Ix/pMFxIevz7GCjpnnsa5P9mjSz4G+BFvr/AIglEF7q/wBo8TatcyKRhpsyl2HXiMJnvxXafCFbiTwLZX95f6Vq13qTPfSalo9r9nguw7HZLt6ljGEyx6kVciEdpRRRUFhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFYXjz/kR/EX/YOuP/RTVu1hePP+RH8Rf9g64/8ARTU1uB8p2P8Aybv8H/8AsHT/APoUdRfETQ5vE194J+CNhK0MusSLr3iyeNsG3s0+ZImIOAcLnDD7wiP8VdV8OLewtfgH8LPEGtP5Wg6Bot1qV6/HKoUKoASMszDAHfpWB8IPEln4L8C+LPjj8QIXn13xxdmPTdLQF55rcHEFrCp+YhyoGOR5ccZPFedRwrWNrYlrsl9yPfxOPTyvDYGL01cv/AnZfr9x6H4v+I7/AAysf7SW3mvLnVwNK8J+Crb/AJelUBUdo/4UAIJPYMB1Kiuf8FfsV6fDoZ1nWdXvdE+Il5M17Lqnhmb7JFZuxBEEUajYY1OM8ZPPIGK7r4M/DHWJtcufiT8QVSXxzqkXl29mDui0W0OSttF6Ngnew6knnlifZq7qcfYx5U7vq/M8WvV+sT52rJaJdktvn5nzldt8avhYpTVdMsvjD4bj4FzZKLTV4155MfSQ9OFyT61e8F/GTwF8Rbo2Onay2h68p2yaF4gQ2tyj4GVG7hjz0BNe/wBcZ8RPg74M+K1mbfxR4fs9UYLtS5ZNlxH/ALkq4dfoDitbp7mFmtjGvNNudPbbcQtH6NjIP41518fo5NY8G+FfAFsxS8da3DaTbSQy2ULCWdwRzwFX8GNTyfBX4nfCdd/w38bf8JHoqf8yx4w/ertzkrFcDkdwAQoHcml+F9n4v8AiP8AHCLxZ4w8H3Xg6Hwxop020sZ3WSF7yaQmWWGQcOvlhVyOOcZOKaVtbkt30seofFbVo/C/gCS2s9Y07w3dXTRabp9xqUBmt/NcgLEYwDuDKGXFdhptmun6fa2qrGiwRLEFhQRoAoAwqjhRxwB0p11ZW98ipcwRXCK6yKsqBgGByGGe4PQ1PUFhRRRSGFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAVhePP+RH8Rf9g64/9FNW7WD483HwN4i2RvK/9nXOI413Mx8puAO5NNbgfB/jX4kWV58A/hN8NZdQaw0u40+PVvEl1Dy0NjHI2yMdi7sPlU9XEQ/ir6Q+DPw1vPGOt6f8RfGGljTBZ262/hTwww/d6LZgAK7L/wA92ULk/wAIAHYBfFf2N/2XJtelsfiN47tWeKIRjR9LuoyN4jUIk8in+EBRsB643dMZ+6K1m0tEYwTerCiiisTYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/Z</string>
|
<string name="push_image" translatable="false">data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCADwAPADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvN7bxhffFCaRPB+oWZ8KSQX1hd65C7reWd/GwRPKjZdrAHLZPB4OcY3aPxe1j+z/CiWMeo6lo95rN3DpdpqOlWn2ma2mkJ2vt/u/KQT2zXXafafYbGGAyNO6KA8zKqtI38TkKAMsck4AGSafmI42H4S2T7Z7/AFjWNQ1JvDx8OT3rXjI00JOWmKrwJicnzBzzVO78L+JfAdi174a1C88RwaZoK6fZeGb6WNftdyjArPJdPzvK5B6A8dMV6PRRcLGL4a8VWHiaK7S1u7Wa+sJRa6hbWtwJvslwFDPEzDHK7sdB0+orarloPDOo6f47bUtPudPsfD9xbOb7TorBRPdXhYYnaYEHIQbec5/IjqaACiiikMKKimuIrdQZZFjBzjccZwCTj8AT+FcVbfFrTNdl0dPDdpeeI4dYtbq4sdSs4j9g3QEqUln/AOWe5xtBKkZpgd1UX2iIXAg81PPKlxFuG4qCATj0yRz71wVjp/jnxZb2E+sXMPhW0vNImttS0izcS3NvdOSFlhuVPBVeRjPP5i/ovw98PeC5bHXLqV7vWNN0lNJfxDq0+65ktVbdiV+FJLcliM5oEdnRXiev/tWeG21R9G8D6bqPxI11Tg2+gRFreM88yXB+RRx1G6vmb9ov9or4iaes+jar4lsvDmqS5RvDXhKXzbi2BGALq852tkn5Isk4525BNqDZLmkfY/xI+O3gT4Sqn/CUeIrWwuHIC2ke6a4Oe/lICwHuRj3rvq/NT4Xfs1apF8XvhtbeLA41zWJn1++0uYkvZ2MBDJ5+cnfM4K7eq4AJ3MQn6V0pRUbWCMnLcKKKKgsKKKKACiiigAooooAKKKKACiiigAooooA4T4xag2h+G7HW21bVdLtdK1O2u7mPR7X7RLex79n2ZkHOx2dckcjFd0DkA0yaLzoZI9zJvUruQ4YZHUHsa8w03xO3wa06503xXPeDwpo9rB5fjLW75Jpb6eWVgY2RBuypKjOOnX1L3Fsep0VV/tSz3zobuAPAVWVTIAYywyoYdiQRjPrXOeJvil4d8K2eoTz3v219PuILW7ttOQ3M0EkxAjDomSuc55xxSGdbSEgdTiuDvtY8d61caja6Potjof2DVoI0vdYmM0WoWOA0rxLH80b/AMI3gjrz6DfCW11a4d/Eeq3/AIiSHXl17TY7iUxf2fIgxHGhjILIvXaxIJ5INMRcuPiv4eFzp0NjcS62LzVW0Yy6TGbmO3uVXcyzMvCBR1J6d6qaXqHjzxDcaRdzadY+FbW31C5j1HT7qQXct1aqCsLxSJgIWPzEMMjgfXrrPTNP0OK6a0tbXT45pnu7gwxrEHkbl5HwBlj1LHk968s8XftSeDtD1JtG0D7Z468R52jSvDUJumBzj55B8ij15JGDxTWuyFtudP4d+EemaU/h+81W+1DxPrmhSXUljrGq3Ba4j+0Z8xcrtDLtO0BgcAYFTeL/AIjeB/g3o0Z1vVtN8O2aL+5tFwrMOeI4UG49/uivL7yL4y/Ej59Y1bT/AIReH5RkWmnkXurOvXBk+4h6cryOeKs+EPhD4D+H94dQ0/Rn13XmO59e8RSG8u2b+8C3Cn3UCnp1YteiGN8bviB8TPk+GvgWSw0uQceJvGGbW3x03xwD55B1wenHIqnH8ALTxMZNX+K/jW+8di0BuJraSb7BotoB8xJRSAQoH3ieg5Fdf8QPiFo/w98PN4g8caubCw5+z2i8z3bgZ2RRjqTxz0Gckgc14Z8QbzUPHHhgeMvjF9o8E/DS2kX+yfAdk5S+1iT7yC4OQctj7vBABPyY3tUb9NBO3XUrfEv9oaN/CF7pvwrji8DfD2wf7Jc+Kre0ET3UxGPs+nQjaXlIGS/BHUmMYZtf9mL9l218L6pY+N/GGl+Tr87CTR9Bum82TT16i4uWIG+4P3ugCHsrYWLofhf8L72+1XTfiH8QtLt9O1C1jC+FfBMMYW10C36q7RgAed0PIypAJAYARegeOfHLeAfh/wCL/GtzIDPptg7W7Sfda4f5IV+hcov/AAKm5fZiJR+1Iwfgj/xX/wAcvil8QHzJaWlwnhXS3PIEVvhrgqe6tLtYY9TXvtea/s5eA3+HPwX8L6POpW/NqLu8LcsbiYmWQMe5Bfb/AMBFelVnLcuOwUUUVJQUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAVFc2sN5C8NxEk8LjDRyKGVvqDUtFAHGa38HfBviKLXY9Q0OG4XXJoZ9R/eSIbiSH/AFTMVYEFe2MdBXSWeg6bpupahqFpp9tbX+oMjXl1DCqy3BRdqGRgMttXgZ6CvOvHH7SngfwXfHS476XxJ4hJ2ponh+I3l0zehCfKp6feI61yd1q3xn+JibkTTvhB4ek4868K32rSL/sp9yM4zwfmGRV2fUm66HsPjDx54d+H+mtqHiPWrLRrQdJLuYIW9lHVj7AE15G37Q/iX4ifuvhT4Fu9atW4/wCEi8QBrHTR/tKD88o6cDB5qDw78FfA3hfVP7Xu7S68c+JDy+teKJjdPu9URvlUZJxgAj1rubzV7q+ULJKRGOBGnyqPbApaINWec3nwZv8AxlJ5/wAVvHl94rXO4+HNDJstMX/ZbaQ0g9yQeTXd6Db6Z4N0saZ4X0ex8O6eP+WNjCqFvdmxlj7nn3ptWLWza4jlmd0t7WFTJNczMFjiUDJZmPAAAJpXbHZIYqzXk4UB5pXP1JrjfiB8XLPwBrUHhXw9ph8b/Eu8GLXQ7RgYrQkcSXL5wigHcQSOOSVB31zuofFHXfixeXvhz4RSjS/D0BMOs/Ea+iIhhAALpaA43uAcbvcEbRtkq/oz+Av2Z/hze6zEJotMlk/e6tcYfVfEdyckIhODsJz6D7x4+ZjMpKDUXrJ9DSNOVSLmtIrd/p5spTeEdI+C9nJ8WvjZrY8W+OFIFjbp81vaScmO3soTgF85O8gY+98uGdrvgXwLrfivxRbfFT4qW6jxABu8OeE3yYdEiJyskinrOcA8jKkAnDACJvw9+Hus+JPEtt8UvilbKPEIXPh3woR+40OHqrup/wCW3Q8jKkZPzYEfpdxcSXczyyuXkY5JNdlGiqCet5Pd9/8AgHm4rFPFSVo8sF8MVsv82+r6hcXEl3M8srF5GOSTUVFFanIM1bxNB4C8I+JPFlyFaLRdPluVRzgSSBTsT6scL/wIVn/s+2lh8Jfgz4NtfEeow2Ws+IphPI144V7m+uiZRH7vjC4/2a5f47Wp8Taf4A+G0fL+MNbSe/QDOdPtcTTZ9ORGR9DXqXiC4XVPip4Y0O3vtBki0+1m1O90e8t/Mvdn+rt7i3OMRhJAQW9Gx6VfQjqd/RRRUFhRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFYXjz/kR/EX/YOuP/RTVu1hePP+RH8Rf9g64/9FNW7WD483HwN4i2RvK/9nXOI413Mx8puAO5NNbgfB/jX4kWV58A/hN8NZdQaw0u40+PVvEl1Dy0NjHI2yMdi7sPlU9XEQ/ir6Q+DPw1vPGOt6f8RfGGljTBZ262/hTwww/d6LZgAK7L/wA92ULk/wAIAHYBfFf2N/2XJtelsfiN47tWeKIRjR9LuoyN4jUIk8in+EBRsB643dMZ+6K1m0tEYwTerCiiisTYKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/Z</string>
|
||||||
|
|
||||||
<!-- Setting -->
|
<!-- Setting -->
|
||||||
<string name="setting_vod">Vod</string>
|
<string name="setting_vod">Vod</string>
|
||||||
@@ -78,17 +78,22 @@
|
|||||||
<string name="setting_data">Data Management</string>
|
<string name="setting_data">Data Management</string>
|
||||||
<string name="setting_player">Player setting</string>
|
<string name="setting_player">Player setting</string>
|
||||||
<string name="setting_incognito">Incognito mode</string>
|
<string name="setting_incognito">Incognito mode</string>
|
||||||
|
<string name="setting_live_tab_visible">Hide Live Tab</string>
|
||||||
<string name="setting_quality">Image quality</string>
|
<string name="setting_quality">Image quality</string>
|
||||||
<string name="setting_size">Image size</string>
|
<string name="setting_size">Image size</string>
|
||||||
<string name="setting_doh">DoH</string>
|
<string name="setting_doh">DoH</string>
|
||||||
<string name="setting_proxy">Proxy</string>
|
<string name="setting_proxy">Proxy</string>
|
||||||
<string name="setting_cache">Cache</string>
|
<string name="setting_cache">缓存</string>
|
||||||
<string name="setting_backup">Backup</string>
|
<string name="setting_auto_clean">Auto Clean Cache</string>
|
||||||
<string name="setting_restore">Restore</string>
|
<string name="setting_cache_threshold">Cache Threshold</string>
|
||||||
<string name="setting_version">Version</string>
|
<string name="setting_backup">备份</string>
|
||||||
|
<string name="setting_restore">恢复</string>
|
||||||
|
<string name="setting_version">版本</string>
|
||||||
<string name="setting_choose">Choose</string>
|
<string name="setting_choose">Choose</string>
|
||||||
<string name="setting_off">Off</string>
|
<string name="setting_off">Off</string>
|
||||||
<string name="setting_on">On</string>
|
<string name="setting_on">On</string>
|
||||||
|
<string name="app_version">v3.0.6</string>
|
||||||
|
<string name="about_github">View on GitHub</string>
|
||||||
|
|
||||||
<!-- Backup & Restore -->
|
<!-- Backup & Restore -->
|
||||||
<string name="restore_select">Select backup</string>
|
<string name="restore_select">Select backup</string>
|
||||||
@@ -122,7 +127,7 @@
|
|||||||
<string name="dialog_edit">Edit</string>
|
<string name="dialog_edit">Edit</string>
|
||||||
<string name="dialog_positive">OK</string>
|
<string name="dialog_positive">OK</string>
|
||||||
<string name="dialog_negative">Cancel</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_hint">Please enter the config…</string>
|
||||||
<string name="dialog_config_name">Please enter the name…</string>
|
<string name="dialog_config_name">Please enter the name…</string>
|
||||||
<string name="dialog_config_url">Please enter the url…</string>
|
<string name="dialog_config_url">Please enter the url…</string>
|
||||||
@@ -146,7 +151,10 @@
|
|||||||
<string name="error_cast_file">Unsupported file format</string>
|
<string name="error_cast_file">Unsupported file format</string>
|
||||||
<string name="error_device_limit">Device authorization limit reached</string>
|
<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_live_empty">This subscription has no live content</string>
|
||||||
<string name="error_empty">Not found</string>
|
<string name="error_no_live">Current source has no live content</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_detail">No play data</string>
|
||||||
<string name="error_play_flag">No flag 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>
|
<string name="error_play_code">Error code: <xliff:g name="name">%s</xliff:g></string>
|
||||||
@@ -168,12 +176,14 @@
|
|||||||
|
|
||||||
<!-- Hint -->
|
<!-- Hint -->
|
||||||
<string name="copied">Copied</string>
|
<string name="copied">Copied</string>
|
||||||
|
<string name="copied_to_clipboard">Error log copied to clipboard</string>
|
||||||
|
|
||||||
<!-- UNIT -->
|
<!-- UNIT -->
|
||||||
<string name="all">All</string>
|
<string name="all">All</string>
|
||||||
<string name="none">None</string>
|
<string name="none">None</string>
|
||||||
<string name="times">times</string>
|
<string name="times">times</string>
|
||||||
<string name="lines">lines</string>
|
<string name="lines">lines</string>
|
||||||
|
<string name="last_watch">上次播放</string>
|
||||||
|
|
||||||
<string-array name="select_decode">
|
<string-array name="select_decode">
|
||||||
<item>Soft</item>
|
<item>Soft</item>
|
||||||
@@ -224,4 +234,21 @@
|
|||||||
|
|
||||||
<string name="config_set_current">已设为当前点播源</string>
|
<string name="config_set_current">已设为当前点播源</string>
|
||||||
|
|
||||||
|
<string name="remember_setting">Remember settings</string>
|
||||||
|
<string name="target_size">Target size</string>
|
||||||
|
<string name="scan_result">Scan result</string>
|
||||||
|
|
||||||
|
<string name="source_hint">No video sources added yet\nClick the button below to add</string>
|
||||||
|
<string name="add_source">Add Source</string>
|
||||||
|
|
||||||
|
<!-- 隐私协议相关 -->
|
||||||
|
<string name="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>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -10,6 +10,13 @@
|
|||||||
|
|
||||||
<application>
|
<application>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ui.activity.PrivacyAgreementActivity"
|
||||||
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode|orientation"
|
||||||
|
android:exported="false"
|
||||||
|
android:screenOrientation="fullUser"
|
||||||
|
android:windowSoftInputMode="adjustPan" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.activity.HomeActivity"
|
android:name=".ui.activity.HomeActivity"
|
||||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode|orientation"
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode|orientation"
|
||||||
@@ -106,6 +113,11 @@
|
|||||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode"
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode"
|
||||||
android:screenOrientation="fullUser" />
|
android:screenOrientation="fullUser" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ui.activity.SettingPlayerActivity"
|
||||||
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode"
|
||||||
|
android:screenOrientation="fullUser" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.activity.VideoActivity"
|
android:name=".ui.activity.VideoActivity"
|
||||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode|orientation"
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|uiMode|orientation"
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.fongmi.android.tv;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@ import com.fongmi.android.tv.utils.Notify;
|
|||||||
import com.fongmi.android.tv.utils.ResUtil;
|
import com.fongmi.android.tv.utils.ResUtil;
|
||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
import com.github.catvod.utils.Github;
|
import com.github.catvod.utils.Github;
|
||||||
|
import com.github.catvod.utils.Logger;
|
||||||
import com.github.catvod.utils.Path;
|
import com.github.catvod.utils.Path;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
@@ -28,9 +31,10 @@ public class Updater implements Download.Callback {
|
|||||||
private final Download download;
|
private final Download download;
|
||||||
private AlertDialog dialog;
|
private AlertDialog dialog;
|
||||||
private boolean dev;
|
private boolean dev;
|
||||||
|
private boolean forceCheck; // 是否为手动检查
|
||||||
|
|
||||||
private File getFile() {
|
private File getFile() {
|
||||||
return Path.cache("update.apk");
|
return Path.root("Download", "XMBOX-update.apk");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getJson() {
|
private String getJson() {
|
||||||
@@ -38,7 +42,31 @@ public class Updater implements Download.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getApk() {
|
private String getApk() {
|
||||||
return Github.getApk(dev, BuildConfig.FLAVOR_mode + "-" + BuildConfig.FLAVOR_abi);
|
// 使用JSON中指定的具体下载路径
|
||||||
|
try {
|
||||||
|
String response = OkHttp.string(getJson());
|
||||||
|
JSONObject object = new JSONObject(response);
|
||||||
|
JSONObject downloads = object.optJSONObject("downloads");
|
||||||
|
if (downloads != null) {
|
||||||
|
String abi = BuildConfig.FLAVOR_abi;
|
||||||
|
String downloadPath = downloads.optString(abi);
|
||||||
|
if (!downloadPath.isEmpty()) {
|
||||||
|
// 直接构建完整URL,不通过Github.getApk()避免重复添加路径
|
||||||
|
String baseUrl = Github.useCnMirror() ?
|
||||||
|
"https://gitee.com/ochenoktochen/XMBOX-Release/raw/main" :
|
||||||
|
"https://raw.githubusercontent.com/Tosencen/XMBOX-Release/main";
|
||||||
|
String fullUrl = baseUrl + "/apk/" + (dev ? "dev" : "release") + "/" + downloadPath;
|
||||||
|
Logger.d("APK download URL: " + fullUrl);
|
||||||
|
return fullUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("Failed to get download path from JSON: " + e.getMessage());
|
||||||
|
}
|
||||||
|
// 回退到原来的方式
|
||||||
|
String fallbackUrl = Github.getApk(dev, BuildConfig.FLAVOR_mode + "-" + BuildConfig.FLAVOR_abi);
|
||||||
|
Logger.d("APK fallback URL: " + fallbackUrl);
|
||||||
|
return fallbackUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Updater create() {
|
public static Updater create() {
|
||||||
@@ -47,11 +75,13 @@ public class Updater implements Download.Callback {
|
|||||||
|
|
||||||
public Updater() {
|
public Updater() {
|
||||||
this.download = Download.create(getApk(), getFile(), this);
|
this.download = Download.create(getApk(), getFile(), this);
|
||||||
|
this.forceCheck = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Updater force() {
|
public Updater force() {
|
||||||
Notify.show(R.string.update_check);
|
Notify.show(R.string.update_check);
|
||||||
Setting.putUpdate(true);
|
Setting.putUpdate(true);
|
||||||
|
this.forceCheck = true; // 标记为手动检查
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,14 +109,86 @@ public class Updater implements Download.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void doInBackground(Activity activity) {
|
private void doInBackground(Activity activity) {
|
||||||
|
Logger.d("Updater: Starting update check...");
|
||||||
try {
|
try {
|
||||||
JSONObject object = new JSONObject(OkHttp.string(getJson()));
|
// 直接使用GitHub Releases API检测最新版本
|
||||||
String name = object.optString("name");
|
String releasesUrl = "https://api.github.com/repos/Tosencen/XMBOX/releases/latest";
|
||||||
String desc = object.optString("desc");
|
Logger.d("Updater: GitHub Releases API URL: " + releasesUrl);
|
||||||
int code = object.optInt("code");
|
|
||||||
if (need(code, name)) App.post(() -> show(activity, name, desc));
|
String response = OkHttp.string(releasesUrl);
|
||||||
|
Logger.d("Updater: API response length: " + response.length());
|
||||||
|
|
||||||
|
// 检查响应是否包含错误信息
|
||||||
|
if (response.contains("rate limit exceeded")) {
|
||||||
|
Logger.e("Updater: Rate limit exceeded");
|
||||||
|
if (forceCheck) {
|
||||||
|
App.post(() -> Notify.show("检查更新失败:API请求过于频繁,请稍后重试"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.contains("Not Found") || response.contains("404")) {
|
||||||
|
Logger.e("Updater: Release not found");
|
||||||
|
if (forceCheck) {
|
||||||
|
App.post(() -> Notify.show("检查更新失败:更新服务暂时不可用"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject release = new JSONObject(response);
|
||||||
|
String tagName = release.optString("tag_name");
|
||||||
|
String body = release.optString("body");
|
||||||
|
|
||||||
|
// 提取版本号(去掉v前缀)
|
||||||
|
String version = tagName.startsWith("v") ? tagName.substring(1) : tagName;
|
||||||
|
|
||||||
|
Logger.d("Updater: Remote version: " + version);
|
||||||
|
Logger.d("Updater: Local version: " + BuildConfig.VERSION_NAME);
|
||||||
|
|
||||||
|
// 比较版本号
|
||||||
|
if (needUpdate(version)) {
|
||||||
|
Logger.d("Updater: Update needed, showing dialog");
|
||||||
|
App.post(() -> show(activity, version, body));
|
||||||
|
} else {
|
||||||
|
Logger.d("Updater: No update needed");
|
||||||
|
if (forceCheck) {
|
||||||
|
App.post(() -> Notify.show("已是最新版本 " + version));
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Logger.e("Updater: Exception during update check: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
if (forceCheck) {
|
||||||
|
App.post(() -> Notify.show("检查更新失败:网络连接异常"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean needUpdate(String remoteVersion) {
|
||||||
|
if (!Setting.getUpdate()) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 简单的版本号比较,假设版本格式为 x.y.z
|
||||||
|
String[] remoteParts = remoteVersion.split("\\.");
|
||||||
|
String[] localParts = BuildConfig.VERSION_NAME.split("\\.");
|
||||||
|
|
||||||
|
// 确保两个版本号都有足够的段
|
||||||
|
int maxLength = Math.max(remoteParts.length, localParts.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < maxLength; i++) {
|
||||||
|
int remotePart = i < remoteParts.length ? Integer.parseInt(remoteParts[i]) : 0;
|
||||||
|
int localPart = i < localParts.length ? Integer.parseInt(localParts[i]) : 0;
|
||||||
|
|
||||||
|
if (remotePart > localPart) {
|
||||||
|
return true;
|
||||||
|
} else if (remotePart < localPart) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false; // 版本相同
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("Updater: Version comparison error: " + e.getMessage());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +211,31 @@ public class Updater implements Download.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void confirm(View view) {
|
private void confirm(View view) {
|
||||||
view.setEnabled(false);
|
// 跳转到GitHub Releases页面而不是直接下载
|
||||||
download.start();
|
try {
|
||||||
|
String url = "https://github.com/Tosencen/XMBOX/releases/tag/v3.0.8";
|
||||||
|
Logger.d("Updater: Attempting to open URL: " + url);
|
||||||
|
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(url));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|
||||||
|
// 检查是否有应用可以处理这个Intent
|
||||||
|
if (intent.resolveActivity(App.get().getPackageManager()) != null) {
|
||||||
|
App.get().startActivity(intent);
|
||||||
|
Logger.d("Updater: Successfully started browser intent");
|
||||||
|
dismiss();
|
||||||
|
} else {
|
||||||
|
Logger.e("Updater: No app can handle the URL");
|
||||||
|
Notify.show("没有找到可以打开链接的应用,请手动访问GitHub下载");
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("Updater: Failed to open GitHub releases page: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
Notify.show("无法打开更新页面,请手动访问GitHub下载");
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismiss() {
|
private void dismiss() {
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import com.fongmi.android.tv.utils.Util;
|
|||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
import com.google.android.flexbox.FlexDirection;
|
import com.google.android.flexbox.FlexDirection;
|
||||||
import com.google.android.flexbox.FlexboxLayoutManager;
|
import com.google.android.flexbox.FlexboxLayoutManager;
|
||||||
|
import com.airbnb.lottie.LottieAnimationView;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
@@ -151,12 +152,14 @@ public class CollectActivity extends BaseActivity implements CustomScroller.Call
|
|||||||
if (mCollectAdapter.getPosition() == 0) mSearchAdapter.addAll(result.getList());
|
if (mCollectAdapter.getPosition() == 0) mSearchAdapter.addAll(result.getList());
|
||||||
mCollectAdapter.add(Collect.create(result.getList()));
|
mCollectAdapter.add(Collect.create(result.getList()));
|
||||||
mCollectAdapter.add(result.getList());
|
mCollectAdapter.add(result.getList());
|
||||||
|
updateEmptyState();
|
||||||
});
|
});
|
||||||
mViewModel.result.observe(this, result -> {
|
mViewModel.result.observe(this, result -> {
|
||||||
boolean same = !result.getList().isEmpty() && mCollectAdapter.getActivated().getSite().equals(result.getList().get(0).getSite());
|
boolean same = !result.getList().isEmpty() && mCollectAdapter.getActivated().getSite().equals(result.getList().get(0).getSite());
|
||||||
if (same) mCollectAdapter.getActivated().getList().addAll(result.getList());
|
if (same) mCollectAdapter.getActivated().getList().addAll(result.getList());
|
||||||
if (same) mSearchAdapter.addAll(result.getList());
|
if (same) mSearchAdapter.addAll(result.getList());
|
||||||
mScroller.endLoading(result);
|
mScroller.endLoading(result);
|
||||||
|
updateEmptyState();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +190,7 @@ public class CollectActivity extends BaseActivity implements CustomScroller.Call
|
|||||||
mBinding.agent.setVisibility(View.GONE);
|
mBinding.agent.setVisibility(View.GONE);
|
||||||
mBinding.view.setVisibility(View.VISIBLE);
|
mBinding.view.setVisibility(View.VISIBLE);
|
||||||
mBinding.result.setVisibility(View.VISIBLE);
|
mBinding.result.setVisibility(View.VISIBLE);
|
||||||
|
updateEmptyState(); // 搜索开始时显示空状态
|
||||||
if (mExecutor != null) mExecutor.shutdownNow();
|
if (mExecutor != null) mExecutor.shutdownNow();
|
||||||
mExecutor = new PauseExecutor(20);
|
mExecutor = new PauseExecutor(20);
|
||||||
String keyword = mBinding.keyword.getText().toString().trim();
|
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);
|
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) {
|
private void search(Site site, String keyword) {
|
||||||
try {
|
try {
|
||||||
mViewModel.searchContent(site, keyword, false);
|
mViewModel.searchContent(site, keyword, false);
|
||||||
@@ -235,6 +260,7 @@ public class CollectActivity extends BaseActivity implements CustomScroller.Call
|
|||||||
mBinding.result.setVisibility(View.GONE);
|
mBinding.result.setVisibility(View.GONE);
|
||||||
mBinding.site.setVisibility(View.VISIBLE);
|
mBinding.site.setVisibility(View.VISIBLE);
|
||||||
mBinding.agent.setVisibility(View.VISIBLE);
|
mBinding.agent.setVisibility(View.VISIBLE);
|
||||||
|
mBinding.emptyLayout.getRoot().setVisibility(View.GONE); // 隐藏空状态动画
|
||||||
if (mExecutor != null) mExecutor.shutdownNow();
|
if (mExecutor != null) mExecutor.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.fongmi.android.tv.ui.activity;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.viewbinding.ViewBinding;
|
||||||
|
|
||||||
|
import com.fongmi.android.tv.R;
|
||||||
|
import com.fongmi.android.tv.databinding.ActivityCrashBinding;
|
||||||
|
import com.fongmi.android.tv.ui.base.BaseActivity;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import cat.ereza.customactivityoncrash.CustomActivityOnCrash;
|
||||||
|
|
||||||
|
public class CrashActivity extends BaseActivity {
|
||||||
|
|
||||||
|
private ActivityCrashBinding mBinding;
|
||||||
|
private String errorDetails;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ViewBinding getBinding() {
|
||||||
|
return mBinding = ActivityCrashBinding.inflate(getLayoutInflater());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initView(Bundle savedInstanceState) {
|
||||||
|
errorDetails = CustomActivityOnCrash.getAllErrorDetailsFromIntent(this, getIntent());
|
||||||
|
mBinding.error.setText(errorDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initEvent() {
|
||||||
|
mBinding.copy.setOnClickListener(v -> copyErrorToClipboard());
|
||||||
|
mBinding.restart.setOnClickListener(v -> CustomActivityOnCrash.restartApplication(this, Objects.requireNonNull(CustomActivityOnCrash.getConfigFromIntent(getIntent()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyErrorToClipboard() {
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip = ClipData.newPlainText(getString(R.string.crash_details_title), errorDetails);
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||||
|
showError();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showError() {
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle(R.string.crash_details_title)
|
||||||
|
.setMessage(errorDetails)
|
||||||
|
.setPositiveButton(R.string.crash_details_close, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.base.BaseActivity;
|
||||||
import com.fongmi.android.tv.ui.dialog.SyncDialog;
|
import com.fongmi.android.tv.ui.dialog.SyncDialog;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import com.airbnb.lottie.LottieAnimationView;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
@@ -59,6 +60,25 @@ public class HistoryActivity extends BaseActivity implements HistoryAdapter.OnCl
|
|||||||
private void getHistory() {
|
private void getHistory() {
|
||||||
mAdapter.addAll(History.get());
|
mAdapter.addAll(History.get());
|
||||||
mBinding.delete.setVisibility(mAdapter.getItemCount() > 0 ? View.VISIBLE : View.GONE);
|
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) {
|
private void onSync(View view) {
|
||||||
@@ -67,7 +87,10 @@ public class HistoryActivity extends BaseActivity implements HistoryAdapter.OnCl
|
|||||||
|
|
||||||
private void onDelete(View view) {
|
private void onDelete(View view) {
|
||||||
if (mAdapter.isDelete()) {
|
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) {
|
} else if (mAdapter.getItemCount() > 0) {
|
||||||
mAdapter.setDelete(true);
|
mAdapter.setDelete(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -91,6 +114,7 @@ public class HistoryActivity extends BaseActivity implements HistoryAdapter.OnCl
|
|||||||
if (mAdapter.getItemCount() > 0) return;
|
if (mAdapter.getItemCount() > 0) return;
|
||||||
mBinding.delete.setVisibility(View.GONE);
|
mBinding.delete.setVisibility(View.GONE);
|
||||||
mAdapter.setDelete(false);
|
mAdapter.setDelete(false);
|
||||||
|
updateEmptyState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import androidx.viewbinding.ViewBinding;
|
|||||||
|
|
||||||
import com.fongmi.android.tv.App;
|
import com.fongmi.android.tv.App;
|
||||||
import com.fongmi.android.tv.R;
|
import com.fongmi.android.tv.R;
|
||||||
|
import com.fongmi.android.tv.Setting;
|
||||||
import com.fongmi.android.tv.Updater;
|
import com.fongmi.android.tv.Updater;
|
||||||
import com.fongmi.android.tv.api.config.LiveConfig;
|
import com.fongmi.android.tv.api.config.LiveConfig;
|
||||||
import com.fongmi.android.tv.api.config.VodConfig;
|
import com.fongmi.android.tv.api.config.VodConfig;
|
||||||
@@ -33,7 +34,6 @@ import com.fongmi.android.tv.server.Server;
|
|||||||
import com.fongmi.android.tv.ui.base.BaseActivity;
|
import com.fongmi.android.tv.ui.base.BaseActivity;
|
||||||
import com.fongmi.android.tv.ui.custom.FragmentStateManager;
|
import com.fongmi.android.tv.ui.custom.FragmentStateManager;
|
||||||
import com.fongmi.android.tv.ui.fragment.SettingFragment;
|
import com.fongmi.android.tv.ui.fragment.SettingFragment;
|
||||||
import com.fongmi.android.tv.ui.fragment.SettingPlayerFragment;
|
|
||||||
import com.fongmi.android.tv.ui.fragment.VodFragment;
|
import com.fongmi.android.tv.ui.fragment.VodFragment;
|
||||||
import com.fongmi.android.tv.utils.FileChooser;
|
import com.fongmi.android.tv.utils.FileChooser;
|
||||||
import com.fongmi.android.tv.utils.Notify;
|
import com.fongmi.android.tv.utils.Notify;
|
||||||
@@ -63,6 +63,18 @@ public class HomeActivity extends BaseActivity implements NavigationBarView.OnIt
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initView(Bundle savedInstanceState) {
|
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;
|
orientation = getResources().getConfiguration().orientation;
|
||||||
Updater.create().release().start(this);
|
Updater.create().release().start(this);
|
||||||
initFragment(savedInstanceState);
|
initFragment(savedInstanceState);
|
||||||
@@ -95,7 +107,6 @@ public class HomeActivity extends BaseActivity implements NavigationBarView.OnIt
|
|||||||
public Fragment getItem(int position) {
|
public Fragment getItem(int position) {
|
||||||
if (position == 0) return VodFragment.newInstance();
|
if (position == 0) return VodFragment.newInstance();
|
||||||
if (position == 1) return SettingFragment.newInstance();
|
if (position == 1) return SettingFragment.newInstance();
|
||||||
if (position == 2) return SettingPlayerFragment.newInstance();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -143,7 +154,7 @@ public class HomeActivity extends BaseActivity implements NavigationBarView.OnIt
|
|||||||
private void setNavigation() {
|
private void setNavigation() {
|
||||||
mBinding.navigation.getMenu().findItem(R.id.vod).setVisible(true);
|
mBinding.navigation.getMenu().findItem(R.id.vod).setVisible(true);
|
||||||
mBinding.navigation.getMenu().findItem(R.id.setting).setVisible(true);
|
mBinding.navigation.getMenu().findItem(R.id.setting).setVisible(true);
|
||||||
mBinding.navigation.getMenu().findItem(R.id.live).setVisible(LiveConfig.hasUrl());
|
mBinding.navigation.getMenu().findItem(R.id.live).setVisible(LiveConfig.hasUrl() && !Setting.isLiveTabVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean openLive() {
|
private boolean openLive() {
|
||||||
@@ -179,7 +190,13 @@ public class HomeActivity extends BaseActivity implements NavigationBarView.OnIt
|
|||||||
if (mBinding.navigation.getSelectedItemId() == item.getItemId()) return false;
|
if (mBinding.navigation.getSelectedItemId() == item.getItemId()) return false;
|
||||||
if (item.getItemId() == R.id.setting) return mManager.change(1);
|
if (item.getItemId() == R.id.setting) return mManager.change(1);
|
||||||
if (item.getItemId() == R.id.vod) return mManager.change(0);
|
if (item.getItemId() == R.id.vod) return mManager.change(0);
|
||||||
if (item.getItemId() == R.id.live) return openLive();
|
if (item.getItemId() == R.id.live) {
|
||||||
|
if (LiveConfig.isEmpty()) {
|
||||||
|
Notify.showCenter(R.string.error_no_live);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return openLive();
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,8 +221,6 @@ public class HomeActivity extends BaseActivity implements NavigationBarView.OnIt
|
|||||||
protected void onBackPress() {
|
protected void onBackPress() {
|
||||||
if (!mBinding.navigation.getMenu().findItem(R.id.vod).isVisible()) {
|
if (!mBinding.navigation.getMenu().findItem(R.id.vod).isVisible()) {
|
||||||
setNavigation();
|
setNavigation();
|
||||||
} else if (mManager.isVisible(2)) {
|
|
||||||
change(1);
|
|
||||||
} else if (mManager.isVisible(1)) {
|
} else if (mManager.isVisible(1)) {
|
||||||
mBinding.navigation.setSelectedItemId(R.id.vod);
|
mBinding.navigation.setSelectedItemId(R.id.vod);
|
||||||
} else if (mManager.canBack(0)) {
|
} else if (mManager.canBack(0)) {
|
||||||
|
|||||||
@@ -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.ui.dialog.SyncDialog;
|
||||||
import com.fongmi.android.tv.utils.Notify;
|
import com.fongmi.android.tv.utils.Notify;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
import com.airbnb.lottie.LottieAnimationView;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
@@ -63,6 +64,25 @@ public class KeepActivity extends BaseActivity implements KeepAdapter.OnClickLis
|
|||||||
private void getKeep() {
|
private void getKeep() {
|
||||||
mAdapter.addAll(Keep.getVod());
|
mAdapter.addAll(Keep.getVod());
|
||||||
mBinding.delete.setVisibility(mAdapter.getItemCount() > 0 ? View.VISIBLE : View.GONE);
|
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) {
|
private void onSync(View view) {
|
||||||
@@ -71,7 +91,10 @@ public class KeepActivity extends BaseActivity implements KeepAdapter.OnClickLis
|
|||||||
|
|
||||||
private void onDelete(View view) {
|
private void onDelete(View view) {
|
||||||
if (mAdapter.isDelete()) {
|
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) {
|
} else if (mAdapter.getItemCount() > 0) {
|
||||||
mAdapter.setDelete(true);
|
mAdapter.setDelete(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -114,6 +137,7 @@ public class KeepActivity extends BaseActivity implements KeepAdapter.OnClickLis
|
|||||||
if (mAdapter.getItemCount() > 0) return;
|
if (mAdapter.getItemCount() > 0) return;
|
||||||
mBinding.delete.setVisibility(View.GONE);
|
mBinding.delete.setVisibility(View.GONE);
|
||||||
mAdapter.setDelete(false);
|
mAdapter.setDelete(false);
|
||||||
|
updateEmptyState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+25
-53
@@ -1,41 +1,38 @@
|
|||||||
package com.fongmi.android.tv.ui.fragment;
|
package com.fongmi.android.tv.ui.activity;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.viewbinding.ViewBinding;
|
import androidx.viewbinding.ViewBinding;
|
||||||
|
|
||||||
import com.fongmi.android.tv.R;
|
import com.fongmi.android.tv.R;
|
||||||
import com.fongmi.android.tv.Setting;
|
import com.fongmi.android.tv.Setting;
|
||||||
import com.fongmi.android.tv.databinding.FragmentSettingPlayerBinding;
|
import com.fongmi.android.tv.databinding.ActivitySettingPlayerBinding;
|
||||||
import com.fongmi.android.tv.impl.BufferCallback;
|
import com.fongmi.android.tv.impl.BufferCallback;
|
||||||
import com.fongmi.android.tv.impl.SpeedCallback;
|
import com.fongmi.android.tv.impl.SpeedCallback;
|
||||||
import com.fongmi.android.tv.impl.UaCallback;
|
import com.fongmi.android.tv.impl.UaCallback;
|
||||||
import com.fongmi.android.tv.ui.base.BaseFragment;
|
import com.fongmi.android.tv.ui.base.BaseActivity;
|
||||||
import com.fongmi.android.tv.ui.dialog.BufferDialog;
|
import com.fongmi.android.tv.ui.dialog.BufferDialog;
|
||||||
import com.fongmi.android.tv.ui.dialog.SpeedDialog;
|
import com.fongmi.android.tv.ui.dialog.SpeedDialog;
|
||||||
import com.fongmi.android.tv.ui.dialog.UaDialog;
|
import com.fongmi.android.tv.ui.dialog.UaDialog;
|
||||||
import com.fongmi.android.tv.utils.ResUtil;
|
import com.fongmi.android.tv.utils.ResUtil;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
public class SettingPlayerFragment extends BaseFragment implements UaCallback, BufferCallback, SpeedCallback {
|
public class SettingPlayerActivity extends BaseActivity implements UaCallback, BufferCallback, SpeedCallback {
|
||||||
|
|
||||||
private FragmentSettingPlayerBinding mBinding;
|
private ActivitySettingPlayerBinding mBinding;
|
||||||
private DecimalFormat format;
|
private DecimalFormat format;
|
||||||
private String[] background;
|
private String[] background;
|
||||||
private String[] caption;
|
private String[] caption;
|
||||||
private String[] render;
|
private String[] render;
|
||||||
private String[] scale;
|
private String[] scale;
|
||||||
|
|
||||||
public static SettingPlayerFragment newInstance() {
|
public static void start(Activity activity) {
|
||||||
return new SettingPlayerFragment();
|
activity.startActivity(new Intent(activity, SettingPlayerActivity.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSwitch(boolean value) {
|
private String getSwitch(boolean value) {
|
||||||
@@ -43,14 +40,13 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
|
protected ViewBinding getBinding() {
|
||||||
return mBinding = FragmentSettingPlayerBinding.inflate(inflater, container, false);
|
return mBinding = ActivitySettingPlayerBinding.inflate(getLayoutInflater());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initView() {
|
protected void initView(Bundle savedInstanceState) {
|
||||||
format = new DecimalFormat("0.#");
|
format = new DecimalFormat("0.#");
|
||||||
mBinding.back.setOnClickListener(v -> requireActivity().onBackPressed());
|
|
||||||
mBinding.uaText.setText(Setting.getUa());
|
mBinding.uaText.setText(Setting.getUa());
|
||||||
mBinding.tunnelSwitch.setChecked(Setting.isTunnel());
|
mBinding.tunnelSwitch.setChecked(Setting.isTunnel());
|
||||||
mBinding.audioDecodeSwitch.setChecked(Setting.isAudioPrefer());
|
mBinding.audioDecodeSwitch.setChecked(Setting.isAudioPrefer());
|
||||||
@@ -63,44 +59,25 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B
|
|||||||
mBinding.renderText.setText((render = ResUtil.getStringArray(R.array.select_render))[Setting.getRender()]);
|
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.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()]);
|
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
|
@Override
|
||||||
protected void initEvent() {
|
protected void initEvent() {
|
||||||
|
mBinding.back.setOnClickListener(v -> finish());
|
||||||
mBinding.ua.setOnClickListener(this::onUa);
|
mBinding.ua.setOnClickListener(this::onUa);
|
||||||
mBinding.aac.setOnClickListener(this::setAAC);
|
|
||||||
mBinding.scale.setOnClickListener(this::onScale);
|
mBinding.scale.setOnClickListener(this::onScale);
|
||||||
mBinding.speed.setOnClickListener(this::onSpeed);
|
mBinding.speed.setOnClickListener(this::onSpeed);
|
||||||
mBinding.buffer.setOnClickListener(this::onBuffer);
|
mBinding.buffer.setOnClickListener(this::onBuffer);
|
||||||
mBinding.render.setOnClickListener(this::setRender);
|
mBinding.render.setOnClickListener(this::setRender);
|
||||||
mBinding.tunnel.setOnClickListener(this::setTunnel);
|
|
||||||
mBinding.caption.setOnClickListener(this::setCaption);
|
mBinding.caption.setOnClickListener(this::setCaption);
|
||||||
mBinding.caption.setOnLongClickListener(this::onCaption);
|
mBinding.caption.setOnLongClickListener(this::onCaption);
|
||||||
mBinding.background.setOnClickListener(this::onBackground);
|
mBinding.background.setOnClickListener(this::onBackground);
|
||||||
mBinding.audioDecode.setOnClickListener(this::setAudioDecode);
|
|
||||||
mBinding.danmakuLoad.setOnClickListener(this::setDanmakuLoad);
|
// 直接给开关按钮设置点击监听器,避免双重点击冲突
|
||||||
|
mBinding.tunnelSwitch.setOnClickListener(this::setTunnel);
|
||||||
|
mBinding.audioDecodeSwitch.setOnClickListener(this::setAudioDecode);
|
||||||
|
mBinding.aacSwitch.setOnClickListener(this::setAAC);
|
||||||
|
mBinding.danmakuLoadSwitch.setOnClickListener(this::setDanmakuLoad);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUa(View view) {
|
private void onUa(View view) {
|
||||||
@@ -116,11 +93,11 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B
|
|||||||
private void setAAC(View view) {
|
private void setAAC(View view) {
|
||||||
boolean isChecked = !Setting.isPreferAAC();
|
boolean isChecked = !Setting.isPreferAAC();
|
||||||
Setting.putPreferAAC(isChecked);
|
Setting.putPreferAAC(isChecked);
|
||||||
mBinding.aacSwitch.setChecked(isChecked);
|
// 不需要再次调用 setChecked,因为点击已经触发了状态变化
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onScale(View view) {
|
private void onScale(View view) {
|
||||||
new MaterialAlertDialogBuilder(getActivity()).setTitle(R.string.player_scale).setNegativeButton(R.string.dialog_negative, null).setSingleChoiceItems(scale, Setting.getScale(), (dialog, which) -> {
|
new com.google.android.material.dialog.MaterialAlertDialogBuilder(this).setTitle(R.string.player_scale).setNegativeButton(R.string.dialog_negative, null).setSingleChoiceItems(scale, Setting.getScale(), (dialog, which) -> {
|
||||||
mBinding.scaleText.setText(scale[which]);
|
mBinding.scaleText.setText(scale[which]);
|
||||||
Setting.putScale(which);
|
Setting.putScale(which);
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
@@ -157,7 +134,7 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B
|
|||||||
private void setTunnel(View view) {
|
private void setTunnel(View view) {
|
||||||
boolean isChecked = !Setting.isTunnel();
|
boolean isChecked = !Setting.isTunnel();
|
||||||
Setting.putTunnel(isChecked);
|
Setting.putTunnel(isChecked);
|
||||||
mBinding.tunnelSwitch.setChecked(isChecked);
|
// 不需要再次调用 setChecked,因为点击已经触发了状态变化
|
||||||
if (isChecked && Setting.getRender() == 1) setRender(view);
|
if (isChecked && Setting.getRender() == 1) setRender(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +149,7 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onBackground(View view) {
|
private void onBackground(View view) {
|
||||||
new MaterialAlertDialogBuilder(getActivity()).setTitle(R.string.player_background).setNegativeButton(R.string.dialog_negative, null).setSingleChoiceItems(background, Setting.getBackground(), (dialog, which) -> {
|
new com.google.android.material.dialog.MaterialAlertDialogBuilder(this).setTitle(R.string.player_background).setNegativeButton(R.string.dialog_negative, null).setSingleChoiceItems(background, Setting.getBackground(), (dialog, which) -> {
|
||||||
mBinding.backgroundText.setText(background[which]);
|
mBinding.backgroundText.setText(background[which]);
|
||||||
Setting.putBackground(which);
|
Setting.putBackground(which);
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
@@ -182,17 +159,12 @@ public class SettingPlayerFragment extends BaseFragment implements UaCallback, B
|
|||||||
private void setAudioDecode(View view) {
|
private void setAudioDecode(View view) {
|
||||||
boolean isChecked = !Setting.isAudioPrefer();
|
boolean isChecked = !Setting.isAudioPrefer();
|
||||||
Setting.putAudioPrefer(isChecked);
|
Setting.putAudioPrefer(isChecked);
|
||||||
mBinding.audioDecodeSwitch.setChecked(isChecked);
|
// 不需要再次调用 setChecked,因为点击已经触发了状态变化
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDanmakuLoad(View view) {
|
private void setDanmakuLoad(View view) {
|
||||||
boolean isChecked = !Setting.isDanmakuLoad();
|
boolean isChecked = !Setting.isDanmakuLoad();
|
||||||
Setting.putDanmakuLoad(isChecked);
|
Setting.putDanmakuLoad(isChecked);
|
||||||
mBinding.danmakuLoadSwitch.setChecked(isChecked);
|
// 不需要再次调用 setChecked,因为点击已经触发了状态变化
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHiddenChanged(boolean hidden) {
|
|
||||||
if (!hidden) initView();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,17 +4,24 @@ import android.Manifest;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.BatteryManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
import android.text.style.ClickableSpan;
|
import android.text.style.ClickableSpan;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -22,6 +29,8 @@ import android.view.ViewGroup;
|
|||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -63,6 +72,7 @@ import com.fongmi.android.tv.event.RefreshEvent;
|
|||||||
import com.fongmi.android.tv.model.SiteViewModel;
|
import com.fongmi.android.tv.model.SiteViewModel;
|
||||||
import com.fongmi.android.tv.player.Players;
|
import com.fongmi.android.tv.player.Players;
|
||||||
import com.fongmi.android.tv.player.exo.ExoUtil;
|
import com.fongmi.android.tv.player.exo.ExoUtil;
|
||||||
|
import com.fongmi.android.tv.player.Source;
|
||||||
import com.fongmi.android.tv.service.PlaybackService;
|
import com.fongmi.android.tv.service.PlaybackService;
|
||||||
import com.fongmi.android.tv.ui.adapter.EpisodeAdapter;
|
import com.fongmi.android.tv.ui.adapter.EpisodeAdapter;
|
||||||
import com.fongmi.android.tv.ui.adapter.FlagAdapter;
|
import com.fongmi.android.tv.ui.adapter.FlagAdapter;
|
||||||
@@ -98,6 +108,7 @@ import com.github.catvod.utils.Trans;
|
|||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||||
import com.permissionx.guolindev.PermissionX;
|
import com.permissionx.guolindev.PermissionX;
|
||||||
|
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
|
||||||
@@ -148,6 +159,11 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
private Clock mClock;
|
private Clock mClock;
|
||||||
private String tag;
|
private String tag;
|
||||||
private PiP mPiP;
|
private PiP mPiP;
|
||||||
|
private Handler mHandler;
|
||||||
|
private Runnable mTimeUpdateRunnable;
|
||||||
|
private BroadcastReceiver mBatteryReceiver;
|
||||||
|
private int mBatteryLevel = -1;
|
||||||
|
private boolean mIsCharging = false;
|
||||||
|
|
||||||
public static void push(FragmentActivity activity, String text) {
|
public static void push(FragmentActivity activity, String text) {
|
||||||
if (FileChooser.isValid(activity, Uri.parse(text))) file(activity, FileChooser.getPathFromUri(activity, Uri.parse(text)));
|
if (FileChooser.isValid(activity, Uri.parse(text))) file(activity, FileChooser.getPathFromUri(activity, Uri.parse(text)));
|
||||||
@@ -302,6 +318,91 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
showProgress();
|
showProgress();
|
||||||
showDanmaku();
|
showDanmaku();
|
||||||
checkId();
|
checkId();
|
||||||
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
initTimeBatteryUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTimeBatteryUpdate() {
|
||||||
|
mBatteryReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mTimeUpdateRunnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
updateTimeBattery();
|
||||||
|
mHandler.postDelayed(this, 30000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTimeBattery() {
|
||||||
|
TextView timeBattery = findViewById(R.id.time_battery);
|
||||||
|
TextView batteryText = findViewById(R.id.battery_icon);
|
||||||
|
android.widget.ImageView chargingIndicator = findViewById(R.id.charging_indicator);
|
||||||
|
|
||||||
|
// 只在全屏模式下显示
|
||||||
|
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 {
|
||||||
|
if (timeBattery != null) {
|
||||||
|
timeBattery.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if (batteryText != null) {
|
||||||
|
batteryText.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if (chargingIndicator != null) {
|
||||||
|
chargingIndicator.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startTimeBatteryUpdates() {
|
||||||
|
registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||||
|
updateTimeBattery();
|
||||||
|
mHandler.post(mTimeUpdateRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopTimeBatteryUpdates() {
|
||||||
|
try {
|
||||||
|
if (mBatteryReceiver != null) {
|
||||||
|
unregisterReceiver(mBatteryReceiver);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
mHandler.removeCallbacks(mTimeUpdateRunnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -441,7 +542,10 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
mBinding.swipeLayout.setRefreshing(false);
|
mBinding.swipeLayout.setRefreshing(false);
|
||||||
if (result.getList().isEmpty()) setEmpty(result.hasMsg());
|
if (result.getList().isEmpty()) setEmpty(result.hasMsg());
|
||||||
else setDetail(result.getList().get(0));
|
else setDetail(result.getList().get(0));
|
||||||
Notify.show(result.getMsg());
|
// 只在有错误或重要消息时显示提示
|
||||||
|
if (result.hasMsg() && result.getList().isEmpty()) {
|
||||||
|
Notify.show(result.getMsg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setEmpty(boolean finish) {
|
private void setEmpty(boolean finish) {
|
||||||
@@ -960,6 +1064,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
mBinding.control.bottom.setVisibility(isLock() ? View.GONE : View.VISIBLE);
|
mBinding.control.bottom.setVisibility(isLock() ? View.GONE : View.VISIBLE);
|
||||||
mBinding.control.top.setVisibility(isLock() ? View.GONE : View.VISIBLE);
|
mBinding.control.top.setVisibility(isLock() ? View.GONE : View.VISIBLE);
|
||||||
mBinding.control.getRoot().setVisibility(View.VISIBLE);
|
mBinding.control.getRoot().setVisibility(View.VISIBLE);
|
||||||
|
updateTimeBattery();
|
||||||
setR1Callback();
|
setR1Callback();
|
||||||
checkPlayImg();
|
checkPlayImg();
|
||||||
}
|
}
|
||||||
@@ -1389,6 +1494,55 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
this.rotate = rotate;
|
this.rotate = rotate;
|
||||||
if (fullscreen && rotate) noPadding(mBinding.control.getRoot());
|
if (fullscreen && rotate) noPadding(mBinding.control.getRoot());
|
||||||
if (fullscreen && !rotate) setPadding(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() {
|
public boolean isStop() {
|
||||||
@@ -1490,10 +1644,40 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSeekEnd(long time) {
|
public void onSeekEnd(long time) {
|
||||||
mBinding.widget.seek.setVisibility(View.GONE);
|
handleLandscapeSeek(time);
|
||||||
mPlayers.seek(time);
|
}
|
||||||
showProgress();
|
|
||||||
onPlay();
|
// 添加新的方法,处理横屏模式下的特殊逻辑
|
||||||
|
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
|
@Override
|
||||||
@@ -1555,6 +1739,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
if (isAutoRotate() && isPort() && newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && !isRotate()) exitFullscreen();
|
if (isAutoRotate() && isPort() && newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && !isRotate()) exitFullscreen();
|
||||||
if (isAutoRotate() && isPort() && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) enterFullscreen();
|
if (isAutoRotate() && isPort() && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) enterFullscreen();
|
||||||
if (isFullscreen()) Util.hideSystemUI(this);
|
if (isFullscreen()) Util.hideSystemUI(this);
|
||||||
|
updateTimeBattery();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1574,6 +1759,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
startTimeBatteryUpdates();
|
||||||
if (isRedirect()) onPlay();
|
if (isRedirect()) onPlay();
|
||||||
setRedirect(false);
|
setRedirect(false);
|
||||||
}
|
}
|
||||||
@@ -1581,6 +1767,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
stopTimeBatteryUpdates();
|
||||||
if (isRedirect()) onPaused();
|
if (isRedirect()) onPaused();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1608,14 +1795,17 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
|
|||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
stopSearch();
|
stopSearch();
|
||||||
mClock.release();
|
|
||||||
mPlayers.release();
|
mPlayers.release();
|
||||||
|
mClock.release();
|
||||||
Timer.get().reset();
|
Timer.get().reset();
|
||||||
RefreshEvent.history();
|
RefreshEvent.history();
|
||||||
PlaybackService.stop();
|
PlaybackService.stop();
|
||||||
|
mHandler.removeCallbacksAndMessages(null);
|
||||||
App.removeCallbacks(mR1, mR2, mR3, mR4);
|
App.removeCallbacks(mR1, mR2, mR3, mR4);
|
||||||
|
EventBus.getDefault().unregister(this);
|
||||||
mViewModel.result.removeObserver(mObserveDetail);
|
mViewModel.result.removeObserver(mObserveDetail);
|
||||||
mViewModel.player.removeObserver(mObservePlayer);
|
mViewModel.player.removeObserver(mObservePlayer);
|
||||||
mViewModel.search.removeObserver(mObserveSearch);
|
mViewModel.search.removeObserver(mObserveSearch);
|
||||||
|
stopTimeBatteryUpdates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.fongmi.android.tv.api.config.VodConfig;
|
|||||||
import com.fongmi.android.tv.bean.Config;
|
import com.fongmi.android.tv.bean.Config;
|
||||||
import com.fongmi.android.tv.databinding.AdapterConfigBinding;
|
import com.fongmi.android.tv.databinding.AdapterConfigBinding;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ConfigAdapter extends RecyclerView.Adapter<ConfigAdapter.ViewHolder> {
|
public class ConfigAdapter extends RecyclerView.Adapter<ConfigAdapter.ViewHolder> {
|
||||||
@@ -26,19 +27,36 @@ public class ConfigAdapter extends RecyclerView.Adapter<ConfigAdapter.ViewHolder
|
|||||||
|
|
||||||
void onTextClick(Config item);
|
void onTextClick(Config item);
|
||||||
|
|
||||||
|
void onCopyClick(Config item);
|
||||||
|
|
||||||
void onDeleteClick(Config item);
|
void onDeleteClick(Config item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigAdapter addAll(int type) {
|
public ConfigAdapter addAll(int type) {
|
||||||
mItems = Config.getAll(type);
|
mItems = new ArrayList<>();
|
||||||
mItems.remove(type == 0 ? VodConfig.get().getConfig() : LiveConfig.get().getConfig());
|
List<Config> configs = Config.getAll(type);
|
||||||
|
Config currentConfig = type == 0 ? VodConfig.get().getConfig() : LiveConfig.get().getConfig();
|
||||||
|
|
||||||
|
for (Config config : configs) {
|
||||||
|
if (config.equals(currentConfig) || config.isEmpty()) continue;
|
||||||
|
mItems.add(config);
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addItem(Config item) {
|
||||||
|
if (item.isEmpty()) return;
|
||||||
|
|
||||||
|
mItems.add(0, item);
|
||||||
|
notifyItemInserted(0);
|
||||||
|
}
|
||||||
|
|
||||||
public int remove(Config item) {
|
public int remove(Config item) {
|
||||||
|
int position = mItems.indexOf(item);
|
||||||
item.delete();
|
item.delete();
|
||||||
mItems.remove(item);
|
mItems.remove(item);
|
||||||
notifyDataSetChanged();
|
notifyItemRemoved(position);
|
||||||
return getItemCount();
|
return getItemCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +76,7 @@ public class ConfigAdapter extends RecyclerView.Adapter<ConfigAdapter.ViewHolder
|
|||||||
Config item = mItems.get(position);
|
Config item = mItems.get(position);
|
||||||
holder.binding.text.setText(item.getDesc());
|
holder.binding.text.setText(item.getDesc());
|
||||||
holder.binding.text.setOnClickListener(v -> mListener.onTextClick(item));
|
holder.binding.text.setOnClickListener(v -> mListener.onTextClick(item));
|
||||||
|
holder.binding.copy.setOnClickListener(v -> mListener.onCopyClick(item));
|
||||||
holder.binding.delete.setOnClickListener(v -> mListener.onDeleteClick(item));
|
holder.binding.delete.setOnClickListener(v -> mListener.onDeleteClick(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ public class TypeAdapter extends RecyclerView.Adapter<TypeAdapter.ViewHolder> {
|
|||||||
|
|
||||||
public void addAll(Result result) {
|
public void addAll(Result result) {
|
||||||
mItems.addAll(result.getTypes());
|
mItems.addAll(result.getTypes());
|
||||||
if (!result.getList().isEmpty()) mItems.add(0, home());
|
|
||||||
if (!mItems.isEmpty()) mItems.get(0).setActivated(true);
|
if (!mItems.isEmpty()) mItems.get(0).setActivated(true);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import android.view.WindowManager;
|
|||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback;
|
import androidx.activity.OnBackPressedCallback;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
import androidx.viewbinding.ViewBinding;
|
import androidx.viewbinding.ViewBinding;
|
||||||
|
|
||||||
import com.fongmi.android.tv.R;
|
import com.fongmi.android.tv.R;
|
||||||
@@ -32,6 +33,7 @@ public abstract class BaseActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if (transparent()) setTransparent(this);
|
if (transparent()) setTransparent(this);
|
||||||
setContentView(getBinding().getRoot());
|
setContentView(getBinding().getRoot());
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.view.MotionEvent;
|
|||||||
import android.view.ScaleGestureDetector;
|
import android.view.ScaleGestureDetector;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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;
|
if (isEdge(e1) || changeScale || lock || e1.getPointerCount() > 1) return true;
|
||||||
float deltaX = e2.getX() - e1.getX();
|
float deltaX = e2.getX() - e1.getX();
|
||||||
float deltaY = e1.getY() - e2.getY();
|
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 (touch) checkFunc(distanceX, distanceY, e2);
|
||||||
if (changeTime) listener.onSeek(time = (long) (deltaX * 50));
|
if (changeTime) listener.onSeek(time = (long) (deltaX * 50));
|
||||||
if (changeBright) setBright(deltaY);
|
if (changeBright) setBright(deltaY);
|
||||||
@@ -145,9 +157,32 @@ public class CustomKeyDownVod extends GestureDetector.SimpleOnGestureListener im
|
|||||||
|
|
||||||
private void checkFunc(float distanceX, float distanceY, MotionEvent e2) {
|
private void checkFunc(float distanceX, float distanceY, MotionEvent e2) {
|
||||||
int four = ResUtil.getScreenWidth(activity) / 4;
|
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;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.fongmi.android.tv.ui.dialog;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.viewbinding.ViewBinding;
|
||||||
|
|
||||||
|
import com.fongmi.android.tv.databinding.DialogAboutBinding;
|
||||||
|
|
||||||
|
public class AboutDialog extends BaseDialog {
|
||||||
|
|
||||||
|
private DialogAboutBinding binding;
|
||||||
|
|
||||||
|
public static void show(FragmentActivity activity) {
|
||||||
|
new AboutDialog().show(activity.getSupportFragmentManager(), "AboutDialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(Fragment fragment) {
|
||||||
|
new AboutDialog().show(fragment.getChildFragmentManager(), "AboutDialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) {
|
||||||
|
binding = DialogAboutBinding.inflate(inflater, container, false);
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initEvent() {
|
||||||
|
binding.github.setOnClickListener(v -> openGitHub());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openGitHub() {
|
||||||
|
try {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse("https://github.com/Tosencen/XMBOX/releases/latest"));
|
||||||
|
startActivity(intent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
binding = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.fongmi.android.tv.ui.dialog;
|
package com.fongmi.android.tv.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
@@ -22,11 +23,20 @@ public class BufferDialog {
|
|||||||
return new BufferDialog(fragment);
|
return new BufferDialog(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BufferDialog create(Activity activity) {
|
||||||
|
return new BufferDialog(activity);
|
||||||
|
}
|
||||||
|
|
||||||
public BufferDialog(Fragment fragment) {
|
public BufferDialog(Fragment fragment) {
|
||||||
this.callback = (BufferCallback) fragment;
|
this.callback = (BufferCallback) fragment;
|
||||||
this.binding = DialogBufferBinding.inflate(LayoutInflater.from(fragment.getContext()));
|
this.binding = DialogBufferBinding.inflate(LayoutInflater.from(fragment.getContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BufferDialog(Activity activity) {
|
||||||
|
this.callback = (BufferCallback) activity;
|
||||||
|
this.binding = DialogBufferBinding.inflate(LayoutInflater.from(activity));
|
||||||
|
}
|
||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
initDialog();
|
initDialog();
|
||||||
initView();
|
initView();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.fongmi.android.tv.api.config.WallConfig;
|
|||||||
import com.fongmi.android.tv.bean.Config;
|
import com.fongmi.android.tv.bean.Config;
|
||||||
import com.fongmi.android.tv.databinding.DialogConfigBinding;
|
import com.fongmi.android.tv.databinding.DialogConfigBinding;
|
||||||
import com.fongmi.android.tv.impl.ConfigCallback;
|
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.ui.custom.CustomTextListener;
|
||||||
import com.fongmi.android.tv.utils.FileChooser;
|
import com.fongmi.android.tv.utils.FileChooser;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
@@ -144,9 +145,75 @@ public class ConfigDialog {
|
|||||||
private void onPositive(DialogInterface dialog, int which) {
|
private void onPositive(DialogInterface dialog, int which) {
|
||||||
String url = binding.url.getText().toString().trim();
|
String url = binding.url.getText().toString().trim();
|
||||||
String name = binding.name.getText().toString().trim();
|
String name = binding.name.getText().toString().trim();
|
||||||
|
|
||||||
|
// 如果是编辑模式,更新现有配置
|
||||||
if (edit) Config.find(ori, type).url(url).name(name).update();
|
if (edit) Config.find(ori, type).url(url).name(name).update();
|
||||||
if (url.isEmpty()) Config.delete(ori, type);
|
|
||||||
|
// 如果URL为空,删除配置
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
Config.delete(ori, type);
|
||||||
|
dialog.dismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有URL不为空时,才设置配置
|
||||||
|
// 保存原始URL,以便在添加失败时恢复
|
||||||
|
String originalUrl = ori;
|
||||||
callback.setConfig(Config.find(url, type));
|
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();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,8 @@ public class ControlDialog extends BaseDialog implements ParseAdapter.OnClickLis
|
|||||||
private void setSpeed(@NonNull Slider slider, float value, boolean fromUser) {
|
private void setSpeed(@NonNull Slider slider, float value, boolean fromUser) {
|
||||||
parent.control.action.speed.setText(player.setSpeed(value));
|
parent.control.action.speed.setText(player.setSpeed(value));
|
||||||
if (history != null) history.setSpeed(player.getSpeed());
|
if (history != null) history.setSpeed(player.getSpeed());
|
||||||
|
// 实时更新倍速数值显示
|
||||||
|
binding.speedValue.setText(String.format("%.1fx", value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setScaleText() {
|
private void setScaleText() {
|
||||||
@@ -179,6 +181,8 @@ public class ControlDialog extends BaseDialog implements ParseAdapter.OnClickLis
|
|||||||
binding.player.setText(parent.control.action.player.getText());
|
binding.player.setText(parent.control.action.player.getText());
|
||||||
binding.decode.setVisibility(parent.control.action.decode.getVisibility());
|
binding.decode.setVisibility(parent.control.action.decode.getVisibility());
|
||||||
binding.danmaku.setVisibility(parent.control.action.danmaku.getVisibility());
|
binding.danmaku.setVisibility(parent.control.action.danmaku.getVisibility());
|
||||||
|
// 初始化倍速数值显示
|
||||||
|
binding.speedValue.setText(String.format("%.1fx", Math.max(player.getSpeed(), 0.5f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParseVisible(boolean visible) {
|
public void setParseVisible(boolean visible) {
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
package com.fongmi.android.tv.ui.dialog;
|
package com.fongmi.android.tv.ui.dialog;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.fongmi.android.tv.App;
|
||||||
import com.fongmi.android.tv.bean.Config;
|
import com.fongmi.android.tv.bean.Config;
|
||||||
import com.fongmi.android.tv.databinding.DialogHistoryBinding;
|
import com.fongmi.android.tv.databinding.DialogHistoryBinding;
|
||||||
import com.fongmi.android.tv.impl.ConfigCallback;
|
import com.fongmi.android.tv.impl.ConfigCallback;
|
||||||
import com.fongmi.android.tv.ui.adapter.ConfigAdapter;
|
import com.fongmi.android.tv.ui.adapter.ConfigAdapter;
|
||||||
import com.fongmi.android.tv.ui.custom.SpaceItemDecoration;
|
import com.fongmi.android.tv.ui.custom.SpaceItemDecoration;
|
||||||
|
import com.fongmi.android.tv.utils.Notify;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
public class HistoryDialog implements ConfigAdapter.OnClickListener {
|
public class HistoryDialog implements ConfigAdapter.OnClickListener {
|
||||||
@@ -45,7 +50,6 @@ public class HistoryDialog implements ConfigAdapter.OnClickListener {
|
|||||||
binding.recycler.setHasFixedSize(true);
|
binding.recycler.setHasFixedSize(true);
|
||||||
binding.recycler.setAdapter(adapter.addAll(type));
|
binding.recycler.setAdapter(adapter.addAll(type));
|
||||||
binding.recycler.addItemDecoration(new SpaceItemDecoration(1, 8));
|
binding.recycler.addItemDecoration(new SpaceItemDecoration(1, 8));
|
||||||
binding.recycler.post(() -> binding.recycler.scrollToPosition(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDialog() {
|
private void setDialog() {
|
||||||
@@ -56,12 +60,53 @@ public class HistoryDialog implements ConfigAdapter.OnClickListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextClick(Config item) {
|
public void onTextClick(Config item) {
|
||||||
callback.setConfig(item);
|
// 防止重复点击和空值
|
||||||
|
if (!dialog.isShowing() || item == null) return;
|
||||||
|
|
||||||
|
// 检查callback是否有效
|
||||||
|
if (callback == null) {
|
||||||
|
dialog.dismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先关闭对话框,避免时序冲突
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
|
|
||||||
|
// 延迟执行配置设置,确保对话框完全关闭
|
||||||
|
App.post(() -> {
|
||||||
|
try {
|
||||||
|
// 双重检查callback和item是否仍然有效
|
||||||
|
if (callback != null && item != null && !item.isEmpty()) {
|
||||||
|
callback.setConfig(item);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
// 如果出现异常,显示错误提示
|
||||||
|
try {
|
||||||
|
Notify.show("配置切换失败: " + e.getMessage());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 150); // 增加延迟到150毫秒
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCopyClick(Config item) {
|
||||||
|
ClipboardManager manager = (ClipboardManager) App.get().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
manager.setPrimaryClip(ClipData.newPlainText("url", item.getUrl()));
|
||||||
|
Notify.showCenter("复制成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeleteClick(Config item) {
|
public void onDeleteClick(Config item) {
|
||||||
if (adapter.remove(item) == 0) dialog.dismiss();
|
int count = adapter.remove(item);
|
||||||
|
if (count == 0) {
|
||||||
|
dialog.dismiss();
|
||||||
|
} else {
|
||||||
|
// 强制重新测量布局高度
|
||||||
|
binding.recycler.requestLayout();
|
||||||
|
dialog.getWindow().setLayout(dialog.getWindow().getAttributes().width, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.fongmi.android.tv.ui.dialog;
|
package com.fongmi.android.tv.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
@@ -22,11 +23,20 @@ public class SpeedDialog {
|
|||||||
return new SpeedDialog(fragment);
|
return new SpeedDialog(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SpeedDialog create(Activity activity) {
|
||||||
|
return new SpeedDialog(activity);
|
||||||
|
}
|
||||||
|
|
||||||
public SpeedDialog(Fragment fragment) {
|
public SpeedDialog(Fragment fragment) {
|
||||||
this.callback = (SpeedCallback) fragment;
|
this.callback = (SpeedCallback) fragment;
|
||||||
this.binding = DialogSpeedBinding.inflate(LayoutInflater.from(fragment.getContext()));
|
this.binding = DialogSpeedBinding.inflate(LayoutInflater.from(fragment.getContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SpeedDialog(Activity activity) {
|
||||||
|
this.callback = (SpeedCallback) activity;
|
||||||
|
this.binding = DialogSpeedBinding.inflate(LayoutInflater.from(activity));
|
||||||
|
}
|
||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
initDialog();
|
initDialog();
|
||||||
initView();
|
initView();
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ public class TimerDialog extends BaseDialog implements Timer.Callback {
|
|||||||
binding.time2.setOnClickListener(this::setTimer);
|
binding.time2.setOnClickListener(this::setTimer);
|
||||||
binding.time3.setOnClickListener(this::setTimer);
|
binding.time3.setOnClickListener(this::setTimer);
|
||||||
binding.time4.setOnClickListener(this::setTimer);
|
binding.time4.setOnClickListener(this::setTimer);
|
||||||
|
binding.time5.setOnClickListener(this::setTimer);
|
||||||
|
binding.time6.setOnClickListener(this::setTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setTimer(View view) {
|
private void setTimer(View view) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.fongmi.android.tv.ui.dialog;
|
package com.fongmi.android.tv.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -27,12 +28,22 @@ public class UaDialog {
|
|||||||
return new UaDialog(fragment);
|
return new UaDialog(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static UaDialog create(Activity activity) {
|
||||||
|
return new UaDialog(activity);
|
||||||
|
}
|
||||||
|
|
||||||
public UaDialog(Fragment fragment) {
|
public UaDialog(Fragment fragment) {
|
||||||
this.callback = (UaCallback) fragment;
|
this.callback = (UaCallback) fragment;
|
||||||
this.binding = DialogUaBinding.inflate(LayoutInflater.from(fragment.getContext()));
|
this.binding = DialogUaBinding.inflate(LayoutInflater.from(fragment.getContext()));
|
||||||
this.append = true;
|
this.append = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UaDialog(Activity activity) {
|
||||||
|
this.callback = (UaCallback) activity;
|
||||||
|
this.binding = DialogUaBinding.inflate(LayoutInflater.from(activity));
|
||||||
|
this.append = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
initDialog();
|
initDialog();
|
||||||
initView();
|
initView();
|
||||||
|
|||||||
@@ -3,9 +3,17 @@ package com.fongmi.android.tv.ui.fragment;
|
|||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.text.style.RelativeSizeSpan;
|
||||||
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -31,7 +39,9 @@ import com.fongmi.android.tv.impl.ProxyCallback;
|
|||||||
import com.fongmi.android.tv.impl.SiteCallback;
|
import com.fongmi.android.tv.impl.SiteCallback;
|
||||||
import com.fongmi.android.tv.player.Source;
|
import com.fongmi.android.tv.player.Source;
|
||||||
import com.fongmi.android.tv.ui.activity.HomeActivity;
|
import com.fongmi.android.tv.ui.activity.HomeActivity;
|
||||||
|
import com.fongmi.android.tv.ui.activity.SettingPlayerActivity;
|
||||||
import com.fongmi.android.tv.ui.base.BaseFragment;
|
import com.fongmi.android.tv.ui.base.BaseFragment;
|
||||||
|
import com.fongmi.android.tv.ui.dialog.AboutDialog;
|
||||||
import com.fongmi.android.tv.ui.dialog.ConfigDialog;
|
import com.fongmi.android.tv.ui.dialog.ConfigDialog;
|
||||||
import com.fongmi.android.tv.ui.dialog.HistoryDialog;
|
import com.fongmi.android.tv.ui.dialog.HistoryDialog;
|
||||||
import com.fongmi.android.tv.ui.dialog.LiveDialog;
|
import com.fongmi.android.tv.ui.dialog.LiveDialog;
|
||||||
@@ -91,25 +101,10 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initView() {
|
protected void initView() {
|
||||||
mBinding.vodUrl.setText(VodConfig.getDesc());
|
setSourceHintText(mBinding.vodUrl, VodConfig.getDesc(), R.string.source_hint_setting);
|
||||||
mBinding.liveUrl.setText(LiveConfig.getDesc());
|
setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc(), R.string.source_hint_live);
|
||||||
mBinding.wallUrl.setText(WallConfig.getDesc());
|
// setSourceHintText(mBinding.wallUrl, WallConfig.getDesc(), R.string.source_hint_wall); // 壁纸功能已移除
|
||||||
mBinding.versionText.setText(BuildConfig.VERSION_NAME);
|
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();
|
setOtherText();
|
||||||
setCacheText();
|
setCacheText();
|
||||||
@@ -122,7 +117,30 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
|
|||||||
mBinding.dohText.setText(getDohList()[getDohIndex()]);
|
mBinding.dohText.setText(getDohList()[getDohIndex()]);
|
||||||
mBinding.proxyText.setText(getProxy(Setting.getProxy()));
|
mBinding.proxyText.setText(getProxy(Setting.getProxy()));
|
||||||
mBinding.incognitoSwitch.setChecked(Setting.isIncognito());
|
mBinding.incognitoSwitch.setChecked(Setting.isIncognito());
|
||||||
|
mBinding.liveTabVisibleSwitch.setChecked(Setting.isLiveTabVisible());
|
||||||
mBinding.sizeText.setText((size = ResUtil.getStringArray(R.array.select_size))[Setting.getSize()]);
|
mBinding.sizeText.setText((size = ResUtil.getStringArray(R.array.select_size))[Setting.getSize()]);
|
||||||
|
setLiveSettingsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLiveSettingsVisibility() {
|
||||||
|
boolean isLiveTabVisible = !Setting.isLiveTabVisible(); // 注意:这里取反,因为开关是"隐藏直播"
|
||||||
|
|
||||||
|
// 获取直播容器的布局参数
|
||||||
|
LinearLayout.LayoutParams liveContainerParams = (LinearLayout.LayoutParams) mBinding.liveContainer.getLayoutParams();
|
||||||
|
|
||||||
|
if (isLiveTabVisible) {
|
||||||
|
// 直播开关打开:显示直播模块,间距为12dp
|
||||||
|
mBinding.liveContainer.setVisibility(View.VISIBLE);
|
||||||
|
liveContainerParams.topMargin = (int) TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP, 12, getResources().getDisplayMetrics());
|
||||||
|
} else {
|
||||||
|
// 直播开关关闭:隐藏直播模块,间距为0dp(这样视频模块和下一个模块之间会有正常间距)
|
||||||
|
mBinding.liveContainer.setVisibility(View.GONE);
|
||||||
|
liveContainerParams.topMargin = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用布局参数
|
||||||
|
mBinding.liveContainer.setLayoutParams(liveContainerParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCacheText() {
|
private void setCacheText() {
|
||||||
@@ -138,54 +156,84 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
|
|||||||
protected void initEvent() {
|
protected void initEvent() {
|
||||||
mBinding.vod.setOnClickListener(this::onVod);
|
mBinding.vod.setOnClickListener(this::onVod);
|
||||||
mBinding.live.setOnClickListener(this::onLive);
|
mBinding.live.setOnClickListener(this::onLive);
|
||||||
mBinding.wall.setOnClickListener(this::onWall);
|
// mBinding.wall.setOnClickListener(this::onWall); // 壁纸功能已移除
|
||||||
mBinding.proxy.setOnClickListener(this::onProxy);
|
mBinding.proxy.setOnClickListener(this::onProxy);
|
||||||
mBinding.cache.setOnClickListener(this::onCache);
|
mBinding.cache.setOnClickListener(this::onCache);
|
||||||
mBinding.backup.setOnClickListener(this::onBackup);
|
mBinding.backup.setOnClickListener(this::onBackup);
|
||||||
mBinding.player.setOnClickListener(this::onPlayer);
|
mBinding.player.setOnClickListener(this::onPlayer);
|
||||||
mBinding.restore.setOnClickListener(this::onRestore);
|
mBinding.restore.setOnClickListener(this::onRestore);
|
||||||
mBinding.version.setOnClickListener(this::onVersion);
|
mBinding.version.setOnClickListener(this::onVersion);
|
||||||
|
mBinding.about.setOnClickListener(this::onAbout);
|
||||||
mBinding.vod.setOnLongClickListener(this::onVodEdit);
|
mBinding.vod.setOnLongClickListener(this::onVodEdit);
|
||||||
mBinding.vodHome.setOnClickListener(this::onVodHome);
|
mBinding.vodHome.setOnClickListener(this::onVodHome);
|
||||||
mBinding.live.setOnLongClickListener(this::onLiveEdit);
|
mBinding.live.setOnLongClickListener(this::onLiveEdit);
|
||||||
mBinding.liveHome.setOnClickListener(this::onLiveHome);
|
mBinding.liveHome.setOnClickListener(this::onLiveHome);
|
||||||
mBinding.wall.setOnLongClickListener(this::onWallEdit);
|
// mBinding.wall.setOnLongClickListener(this::onWallEdit); // 壁纸功能已移除
|
||||||
mBinding.vodHistory.setOnClickListener(this::onVodHistory);
|
mBinding.vodHistory.setOnClickListener(this::onVodHistory);
|
||||||
mBinding.version.setOnLongClickListener(this::onVersionDev);
|
mBinding.version.setOnLongClickListener(this::onVersionDev);
|
||||||
mBinding.liveHistory.setOnClickListener(this::onLiveHistory);
|
mBinding.liveHistory.setOnClickListener(this::onLiveHistory);
|
||||||
mBinding.wallDefault.setOnClickListener(this::setWallDefault);
|
// mBinding.wallDefault.setOnClickListener(this::setWallDefault); // 壁纸功能已移除
|
||||||
mBinding.wallRefresh.setOnClickListener(this::setWallRefresh);
|
// mBinding.wallRefresh.setOnClickListener(this::setWallRefresh); // 壁纸功能已移除
|
||||||
mBinding.incognito.setOnClickListener(this::setIncognito);
|
mBinding.incognitoSwitch.setOnClickListener(this::setIncognito);
|
||||||
|
mBinding.liveTabVisibleSwitch.setOnClickListener(this::setLiveTabVisible);
|
||||||
mBinding.size.setOnClickListener(this::setSize);
|
mBinding.size.setOnClickListener(this::setSize);
|
||||||
mBinding.doh.setOnClickListener(this::setDoh);
|
mBinding.doh.setOnClickListener(this::setDoh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setConfig(Config config) {
|
public void setConfig(Config config) {
|
||||||
if (config.getUrl().startsWith("file") && !PermissionX.isGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
// 添加Fragment状态检查,防止在无效状态下执行
|
||||||
PermissionX.init(this).permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE).request((allGranted, grantedList, deniedList) -> load(config));
|
if (getActivity() == null || !isAdded() || isDetached()) return;
|
||||||
} else {
|
|
||||||
load(config);
|
// 如果URL为空,不进行任何操作
|
||||||
|
if (config == null || config.isEmpty()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (config.getUrl().startsWith("file") && !PermissionX.isGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
PermissionX.init(this).permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE).request((allGranted, grantedList, deniedList) -> {
|
||||||
|
if (getActivity() != null && isAdded()) {
|
||||||
|
load(config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
load(config);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void load(Config config) {
|
private void load(Config config) {
|
||||||
switch (config.getType()) {
|
// 再次检查Fragment状态,防止在异步回调中执行
|
||||||
case 0:
|
if (getActivity() == null || !isAdded() || isDetached()) return;
|
||||||
Notify.progress(getActivity());
|
|
||||||
VodConfig.load(config, getCallback(0));
|
try {
|
||||||
mBinding.vodUrl.setText(config.getDesc());
|
switch (config.getType()) {
|
||||||
break;
|
case 0:
|
||||||
case 1:
|
Notify.progress(getActivity());
|
||||||
Notify.progress(getActivity());
|
VodConfig.load(config, getCallback(0));
|
||||||
LiveConfig.load(config, getCallback(1));
|
if (mBinding != null && mBinding.vodUrl != null) {
|
||||||
mBinding.liveUrl.setText(config.getDesc());
|
mBinding.vodUrl.setText(config.getDesc());
|
||||||
break;
|
}
|
||||||
case 2:
|
break;
|
||||||
Notify.progress(getActivity());
|
case 1:
|
||||||
WallConfig.load(config, getCallback(2));
|
Notify.progress(getActivity());
|
||||||
mBinding.wallUrl.setText(config.getDesc());
|
LiveConfig.load(config, getCallback(1));
|
||||||
break;
|
if (mBinding != null && mBinding.liveUrl != null) {
|
||||||
|
mBinding.liveUrl.setText(config.getDesc());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Notify.progress(getActivity());
|
||||||
|
WallConfig.load(config, getCallback(2));
|
||||||
|
// if (mBinding != null && mBinding.wallUrl != null) { // 壁纸功能已移除
|
||||||
|
// mBinding.wallUrl.setText(config.getDesc());
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Notify.dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,18 +241,35 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
|
|||||||
return new Callback() {
|
return new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void success(String result) {
|
public void success(String result) {
|
||||||
|
// 检查Fragment是否还在活动状态
|
||||||
|
if (getActivity() == null || !isAdded()) return;
|
||||||
Notify.show(result);
|
Notify.show(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void success() {
|
public void success() {
|
||||||
|
// 检查Fragment是否还在活动状态
|
||||||
|
if (getActivity() == null || !isAdded()) return;
|
||||||
setConfig(type);
|
setConfig(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void error(String msg) {
|
public void error(String msg) {
|
||||||
|
// 检查Fragment是否还在活动状态
|
||||||
|
if (getActivity() == null || !isAdded()) return;
|
||||||
Notify.show(msg);
|
Notify.show(msg);
|
||||||
setConfig(type);
|
Notify.dismiss();
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
setSourceHintText(mBinding.vodUrl, VodConfig.getDesc(), R.string.source_hint_setting);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc(), R.string.source_hint_live);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// setSourceHintText(mBinding.wallUrl, WallConfig.getDesc(), R.string.source_hint_wall); // 壁纸功能已移除
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -216,24 +281,37 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
|
|||||||
Notify.dismiss();
|
Notify.dismiss();
|
||||||
RefreshEvent.video();
|
RefreshEvent.video();
|
||||||
RefreshEvent.config();
|
RefreshEvent.config();
|
||||||
mBinding.vodUrl.setText(VodConfig.getDesc());
|
setSourceHintText(mBinding.vodUrl, VodConfig.getDesc(), R.string.source_hint_setting);
|
||||||
mBinding.liveUrl.setText(LiveConfig.getDesc());
|
setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc(), R.string.source_hint_live);
|
||||||
mBinding.wallUrl.setText(WallConfig.getDesc());
|
// setSourceHintText(mBinding.wallUrl, WallConfig.getDesc(), R.string.source_hint_wall); // 壁纸功能已移除
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
setCacheText();
|
setCacheText();
|
||||||
Notify.dismiss();
|
Notify.dismiss();
|
||||||
RefreshEvent.config();
|
RefreshEvent.config();
|
||||||
mBinding.liveUrl.setText(LiveConfig.getDesc());
|
setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc(), R.string.source_hint_live);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
setCacheText();
|
setCacheText();
|
||||||
Notify.dismiss();
|
Notify.dismiss();
|
||||||
mBinding.wallUrl.setText(WallConfig.getDesc());
|
// setSourceHintText(mBinding.wallUrl, WallConfig.getDesc(), R.string.source_hint_wall); // 壁纸功能已移除
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setSourceHintText(TextView textView, String desc, int hintStringRes) {
|
||||||
|
if (TextUtils.isEmpty(desc)) {
|
||||||
|
SpannableString spannable = new SpannableString(getString(hintStringRes));
|
||||||
|
spannable.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.white)), 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
spannable.setSpan(new RelativeSizeSpan(0.8f), 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
int alpha = (int)(255 * 0.5f);
|
||||||
|
spannable.setSpan(new ForegroundColorSpan(android.graphics.Color.argb(alpha, 255, 255, 255)), 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
textView.setText(spannable);
|
||||||
|
} else {
|
||||||
|
textView.setText(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSite(Site item) {
|
public void setSite(Site item) {
|
||||||
VodConfig.get().setHome(item);
|
VodConfig.get().setHome(item);
|
||||||
@@ -293,13 +371,17 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onPlayer(View view) {
|
private void onPlayer(View view) {
|
||||||
getRoot().change(2);
|
SettingPlayerActivity.start(requireActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onVersion(View view) {
|
private void onVersion(View view) {
|
||||||
Updater.create().force().release().start(getActivity());
|
Updater.create().force().release().start(getActivity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onAbout(View view) {
|
||||||
|
AboutDialog.show(this);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean onVersionDev(View view) {
|
private boolean onVersionDev(View view) {
|
||||||
Updater.create().force().dev().start(getActivity());
|
Updater.create().force().dev().start(getActivity());
|
||||||
return true;
|
return true;
|
||||||
@@ -323,7 +405,17 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
|
|||||||
private void setIncognito(View view) {
|
private void setIncognito(View view) {
|
||||||
boolean isChecked = !Setting.isIncognito();
|
boolean isChecked = !Setting.isIncognito();
|
||||||
Setting.putIncognito(isChecked);
|
Setting.putIncognito(isChecked);
|
||||||
mBinding.incognitoSwitch.setChecked(isChecked);
|
// 不需要再次调用 setChecked,因为点击已经触发了状态变化
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLiveTabVisible(View view) {
|
||||||
|
boolean isChecked = !Setting.isLiveTabVisible();
|
||||||
|
Setting.putLiveTabVisible(isChecked);
|
||||||
|
// 发送刷新事件,通知主界面更新导航栏
|
||||||
|
RefreshEvent.config();
|
||||||
|
// 更新直播设置项的可见性
|
||||||
|
setLiveSettingsVisibility();
|
||||||
|
// 不需要再次调用 setChecked,因为点击已经触发了状态变化
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSize(View view) {
|
private void setSize(View view) {
|
||||||
@@ -415,9 +507,9 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit
|
|||||||
@Override
|
@Override
|
||||||
public void onHiddenChanged(boolean hidden) {
|
public void onHiddenChanged(boolean hidden) {
|
||||||
if (hidden) return;
|
if (hidden) return;
|
||||||
mBinding.vodUrl.setText(VodConfig.getDesc());
|
setSourceHintText(mBinding.vodUrl, VodConfig.getDesc(), R.string.source_hint_setting);
|
||||||
mBinding.liveUrl.setText(LiveConfig.getDesc());
|
setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc(), R.string.source_hint_live);
|
||||||
mBinding.wallUrl.setText(WallConfig.getDesc());
|
// setSourceHintText(mBinding.wallUrl, WallConfig.getDesc(), R.string.source_hint_wall); // 壁纸功能已移除
|
||||||
setCacheText();
|
setCacheText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import com.fongmi.android.tv.R;
|
|||||||
import com.fongmi.android.tv.Setting;
|
import com.fongmi.android.tv.Setting;
|
||||||
import com.fongmi.android.tv.api.config.VodConfig;
|
import com.fongmi.android.tv.api.config.VodConfig;
|
||||||
import com.fongmi.android.tv.bean.Class;
|
import com.fongmi.android.tv.bean.Class;
|
||||||
|
import com.fongmi.android.tv.bean.Config;
|
||||||
|
import com.fongmi.android.tv.bean.History;
|
||||||
import com.fongmi.android.tv.bean.Hot;
|
import com.fongmi.android.tv.bean.Hot;
|
||||||
import com.fongmi.android.tv.bean.Result;
|
import com.fongmi.android.tv.bean.Result;
|
||||||
import com.fongmi.android.tv.bean.Site;
|
import com.fongmi.android.tv.bean.Site;
|
||||||
@@ -35,6 +37,7 @@ import com.fongmi.android.tv.event.CastEvent;
|
|||||||
import com.fongmi.android.tv.event.RefreshEvent;
|
import com.fongmi.android.tv.event.RefreshEvent;
|
||||||
import com.fongmi.android.tv.event.StateEvent;
|
import com.fongmi.android.tv.event.StateEvent;
|
||||||
import com.fongmi.android.tv.impl.Callback;
|
import com.fongmi.android.tv.impl.Callback;
|
||||||
|
import com.fongmi.android.tv.impl.ConfigCallback;
|
||||||
import com.fongmi.android.tv.impl.FilterCallback;
|
import com.fongmi.android.tv.impl.FilterCallback;
|
||||||
import com.fongmi.android.tv.impl.SiteCallback;
|
import com.fongmi.android.tv.impl.SiteCallback;
|
||||||
import com.fongmi.android.tv.model.SiteViewModel;
|
import com.fongmi.android.tv.model.SiteViewModel;
|
||||||
@@ -42,13 +45,17 @@ import com.fongmi.android.tv.ui.activity.CollectActivity;
|
|||||||
import com.fongmi.android.tv.ui.activity.HistoryActivity;
|
import com.fongmi.android.tv.ui.activity.HistoryActivity;
|
||||||
import com.fongmi.android.tv.ui.activity.KeepActivity;
|
import com.fongmi.android.tv.ui.activity.KeepActivity;
|
||||||
import com.fongmi.android.tv.ui.activity.VideoActivity;
|
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.adapter.TypeAdapter;
|
||||||
import com.fongmi.android.tv.ui.base.BaseFragment;
|
import com.fongmi.android.tv.ui.base.BaseFragment;
|
||||||
|
import com.fongmi.android.tv.ui.dialog.ConfigDialog;
|
||||||
import com.fongmi.android.tv.ui.dialog.FilterDialog;
|
import com.fongmi.android.tv.ui.dialog.FilterDialog;
|
||||||
|
import com.fongmi.android.tv.ui.dialog.LastWatchToast;
|
||||||
import com.fongmi.android.tv.ui.dialog.LinkDialog;
|
import com.fongmi.android.tv.ui.dialog.LinkDialog;
|
||||||
import com.fongmi.android.tv.ui.dialog.ReceiveDialog;
|
import com.fongmi.android.tv.ui.dialog.ReceiveDialog;
|
||||||
import com.fongmi.android.tv.ui.dialog.SiteDialog;
|
import com.fongmi.android.tv.ui.dialog.SiteDialog;
|
||||||
import com.fongmi.android.tv.utils.FileChooser;
|
import com.fongmi.android.tv.utils.FileChooser;
|
||||||
|
import com.fongmi.android.tv.utils.Notify;
|
||||||
import com.fongmi.android.tv.utils.ResUtil;
|
import com.fongmi.android.tv.utils.ResUtil;
|
||||||
import com.fongmi.android.tv.utils.UrlUtil;
|
import com.fongmi.android.tv.utils.UrlUtil;
|
||||||
import com.github.catvod.net.OkHttp;
|
import com.github.catvod.net.OkHttp;
|
||||||
@@ -68,7 +75,7 @@ import okhttp3.Call;
|
|||||||
import okhttp3.Headers;
|
import okhttp3.Headers;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
public class VodFragment extends BaseFragment implements SiteCallback, FilterCallback, TypeAdapter.OnClickListener {
|
public class VodFragment extends BaseFragment implements SiteCallback, FilterCallback, TypeAdapter.OnClickListener, ConfigCallback {
|
||||||
|
|
||||||
private FragmentVodBinding mBinding;
|
private FragmentVodBinding mBinding;
|
||||||
private SiteViewModel mViewModel;
|
private SiteViewModel mViewModel;
|
||||||
@@ -99,10 +106,37 @@ public class VodFragment extends BaseFragment implements SiteCallback, FilterCal
|
|||||||
EventBus.getDefault().register(this);
|
EventBus.getDefault().register(this);
|
||||||
setRecyclerView();
|
setRecyclerView();
|
||||||
setViewModel();
|
setViewModel();
|
||||||
showProgress();
|
initStartupState(); // 根据是否已有配置来设置初始状态
|
||||||
setLogo();
|
setLogo();
|
||||||
initHot();
|
initHot();
|
||||||
getHot();
|
getHot();
|
||||||
|
// 检查是否需要显示上次播放弹窗
|
||||||
|
checkLastWatchDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化启动状态:区分已有配置和无配置的情况
|
||||||
|
private void initStartupState() {
|
||||||
|
// 检查是否已经有保存的配置,添加空值检查
|
||||||
|
boolean hasExistingConfig = false;
|
||||||
|
try {
|
||||||
|
Config config = VodConfig.get().getConfig();
|
||||||
|
hasExistingConfig = config != null &&
|
||||||
|
config.getUrl() != null &&
|
||||||
|
!config.getUrl().isEmpty();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果获取配置时出错,认为没有配置
|
||||||
|
hasExistingConfig = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasExistingConfig) {
|
||||||
|
// 已有配置:显示加载状态,确保不显示添加源提示
|
||||||
|
showProgress();
|
||||||
|
mBinding.emptySourceHint.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
// 无配置:立即显示空源提示,不显示加载状态
|
||||||
|
hideProgress();
|
||||||
|
checkEmptySource();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -127,6 +161,23 @@ public class VodFragment extends BaseFragment implements SiteCallback, FilterCal
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加检查上次播放历史并显示弹窗的方法
|
||||||
|
private void checkLastWatchDialog() {
|
||||||
|
if (App.isAppJustLaunched()) {
|
||||||
|
List<History> histories = History.get();
|
||||||
|
if (!histories.isEmpty()) {
|
||||||
|
App.setAppLaunched();
|
||||||
|
App.post(() -> {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
LastWatchToast.create(getActivity(), histories.get(0)).show();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
App.setAppLaunched();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setRecyclerView() {
|
private void setRecyclerView() {
|
||||||
mBinding.type.setHasFixedSize(true);
|
mBinding.type.setHasFixedSize(true);
|
||||||
mBinding.type.setItemAnimator(null);
|
mBinding.type.setItemAnimator(null);
|
||||||
@@ -173,10 +224,157 @@ public class VodFragment extends BaseFragment implements SiteCallback, FilterCal
|
|||||||
setFabVisible(0);
|
setFabVisible(0);
|
||||||
hideProgress();
|
hideProgress();
|
||||||
checkRetry();
|
checkRetry();
|
||||||
|
checkEmptySource(); // 添加检查是否显示空源提示
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改checkEmptySource方法,增强鲁棒性
|
||||||
|
private void checkEmptySource() {
|
||||||
|
// 检查是否有基础配置文件,添加空值检查
|
||||||
|
boolean hasBaseConfig = false;
|
||||||
|
try {
|
||||||
|
Config config = VodConfig.get().getConfig();
|
||||||
|
hasBaseConfig = config != null &&
|
||||||
|
config.getUrl() != null &&
|
||||||
|
!config.getUrl().isEmpty();
|
||||||
|
} catch (Exception e) {
|
||||||
|
hasBaseConfig = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有有效的站点配置
|
||||||
|
boolean hasValidSites = false;
|
||||||
|
boolean hasValidHome = false;
|
||||||
|
try {
|
||||||
|
hasValidSites = VodConfig.get().getSites().size() > 0;
|
||||||
|
Site site = getSite();
|
||||||
|
hasValidHome = site != null && site.getKey() != null && !site.getKey().isEmpty();
|
||||||
|
} catch (Exception e) {
|
||||||
|
hasValidSites = false;
|
||||||
|
hasValidHome = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有在完全没有配置文件或配置文件无效时才显示空源提示
|
||||||
|
boolean isEmpty = !hasBaseConfig || (!hasValidSites || !hasValidHome);
|
||||||
|
|
||||||
|
if (mBinding.emptySourceHint != null) {
|
||||||
|
mBinding.emptySourceHint.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
|
||||||
|
if (isEmpty) {
|
||||||
|
// 设置整个布局的点击事件
|
||||||
|
mBinding.emptySourceHint.setOnClickListener(this::onAddSource);
|
||||||
|
// 设置按钮的点击事件
|
||||||
|
if (mBinding.addSourceBtn != null) {
|
||||||
|
mBinding.addSourceBtn.setOnClickListener(this::onAddSource);
|
||||||
|
}
|
||||||
|
// 空源状态下隐藏所有悬浮按钮
|
||||||
|
hideFabButtons();
|
||||||
|
// 启动Lottie动画
|
||||||
|
try {
|
||||||
|
LottieAnimationView lottieView = mBinding.emptySourceHint.findViewById(R.id.lottieAnimation);
|
||||||
|
if (lottieView != null) {
|
||||||
|
lottieView.playAnimation();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加源按钮点击事件处理
|
||||||
|
private void onAddSource(View view) {
|
||||||
|
ConfigDialog.create(this).type(0).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实现ConfigCallback接口
|
||||||
|
@Override
|
||||||
|
public void setConfig(Config config) {
|
||||||
|
if (config == null || config.isEmpty()) return;
|
||||||
|
|
||||||
|
// 检查Fragment是否还在活动状态,增强检查
|
||||||
|
if (!isValidFragmentState()) return;
|
||||||
|
|
||||||
|
// 安全地隐藏空源提示
|
||||||
|
try {
|
||||||
|
if (mBinding != null && mBinding.emptySourceHint != null) {
|
||||||
|
mBinding.emptySourceHint.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Notify.progress(getActivity());
|
||||||
|
VodConfig.load(config, new Callback() {
|
||||||
|
@Override
|
||||||
|
public void success() {
|
||||||
|
// 双重检查Fragment是否还在活动状态
|
||||||
|
if (!isValidFragmentState()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Notify.dismiss();
|
||||||
|
RefreshEvent.config();
|
||||||
|
RefreshEvent.video();
|
||||||
|
homeContent();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String msg) {
|
||||||
|
// 双重检查Fragment是否还在活动状态
|
||||||
|
if (!isValidFragmentState()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Notify.dismiss();
|
||||||
|
Notify.show(msg);
|
||||||
|
// 加载失败时重新显示空源提示
|
||||||
|
checkEmptySource();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加Fragment状态检查方法
|
||||||
|
private boolean isValidFragmentState() {
|
||||||
|
return getActivity() != null &&
|
||||||
|
!getActivity().isFinishing() &&
|
||||||
|
!getActivity().isDestroyed() &&
|
||||||
|
isAdded() &&
|
||||||
|
!isDetached() &&
|
||||||
|
!isRemoving() &&
|
||||||
|
getView() != null &&
|
||||||
|
mBinding != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFabVisible(int position) {
|
private void setFabVisible(int position) {
|
||||||
if (mAdapter.getItemCount() == 0) {
|
// 检查是否为空源状态 - 使用与checkEmptySource相同的逻辑,添加空值检查
|
||||||
|
boolean hasBaseConfig = false;
|
||||||
|
boolean hasValidSites = false;
|
||||||
|
boolean hasValidHome = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Config config = VodConfig.get().getConfig();
|
||||||
|
hasBaseConfig = config != null &&
|
||||||
|
config.getUrl() != null &&
|
||||||
|
!config.getUrl().isEmpty();
|
||||||
|
|
||||||
|
hasValidSites = VodConfig.get().getSites().size() > 0;
|
||||||
|
|
||||||
|
Site site = getSite();
|
||||||
|
hasValidHome = site != null && site.getKey() != null && !site.getKey().isEmpty();
|
||||||
|
} catch (Exception e) {
|
||||||
|
hasBaseConfig = false;
|
||||||
|
hasValidSites = false;
|
||||||
|
hasValidHome = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEmpty = !hasBaseConfig || (!hasValidSites || !hasValidHome);
|
||||||
|
|
||||||
|
if (isEmpty) {
|
||||||
|
// 空源状态下隐藏所有悬浮按钮
|
||||||
|
hideFabButtons();
|
||||||
|
} else if (mAdapter.getItemCount() == 0) {
|
||||||
mBinding.top.setVisibility(View.INVISIBLE);
|
mBinding.top.setVisibility(View.INVISIBLE);
|
||||||
mBinding.link.setVisibility(View.VISIBLE);
|
mBinding.link.setVisibility(View.VISIBLE);
|
||||||
mBinding.filter.setVisibility(View.GONE);
|
mBinding.filter.setVisibility(View.GONE);
|
||||||
@@ -191,6 +389,13 @@ public class VodFragment extends BaseFragment implements SiteCallback, FilterCal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 隐藏所有悬浮按钮的方法
|
||||||
|
private void hideFabButtons() {
|
||||||
|
mBinding.top.setVisibility(View.GONE);
|
||||||
|
mBinding.link.setVisibility(View.GONE);
|
||||||
|
mBinding.filter.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkRetry() {
|
private void checkRetry() {
|
||||||
mBinding.retry.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
mBinding.retry.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
@@ -247,6 +452,14 @@ public class VodFragment extends BaseFragment implements SiteCallback, FilterCal
|
|||||||
private void homeContent() {
|
private void homeContent() {
|
||||||
showProgress();
|
showProgress();
|
||||||
setFabVisible(0);
|
setFabVisible(0);
|
||||||
|
// 安全地隐藏空源提示
|
||||||
|
try {
|
||||||
|
if (mBinding != null && mBinding.emptySourceHint != null) {
|
||||||
|
mBinding.emptySourceHint.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
mAdapter.clear();
|
mAdapter.clear();
|
||||||
mViewModel.homeContent();
|
mViewModel.homeContent();
|
||||||
mBinding.pager.setAdapter(new PageAdapter(getChildFragmentManager()));
|
mBinding.pager.setAdapter(new PageAdapter(getChildFragmentManager()));
|
||||||
@@ -296,6 +509,7 @@ public class VodFragment extends BaseFragment implements SiteCallback, FilterCal
|
|||||||
switch (event.getType()) {
|
switch (event.getType()) {
|
||||||
case EMPTY:
|
case EMPTY:
|
||||||
hideProgress();
|
hideProgress();
|
||||||
|
checkEmptySource(); // 添加检查是否显示空源提示
|
||||||
break;
|
break;
|
||||||
case PROGRESS:
|
case PROGRESS:
|
||||||
showProgress();
|
showProgress();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class Timer {
|
|||||||
|
|
||||||
public void delay() {
|
public void delay() {
|
||||||
cancel();
|
cancel();
|
||||||
set(TimeUnit.MINUTES.toMillis(5) + tick);
|
set(TimeUnit.MINUTES.toMillis(15) + tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:color="@color/white" android:state_checked="true" />
|
<item android:color="@color/black" android:state_checked="true" />
|
||||||
<item android:color="@color/white" android:state_checked="false" />
|
<item android:color="@color/white" android:state_checked="false" />
|
||||||
</selector>
|
</selector>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:color="@color/white" android:state_focused="true" android:state_selected="true" />
|
<item android:color="@color/white" android:state_focused="true" android:state_selected="true" />
|
||||||
<item android:color="@color/green_400" android:state_selected="true" />
|
<item android:color="@color/primary" android:state_selected="true" />
|
||||||
<item android:color="@color/white" />
|
<item android:color="@color/white" />
|
||||||
</selector>
|
</selector>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:color="@color/white" android:state_checked="true" />
|
<item android:color="@color/black" android:state_checked="true" />
|
||||||
<item android:color="@color/white" android:state_checked="false" />
|
<item android:color="@color/white" android:state_checked="false" />
|
||||||
</selector>
|
</selector>
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 703 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 890 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 869 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 64 B After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#1AFFFFFF" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<size android:height="48dp" />
|
||||||
|
</shape>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:tint="?attr/colorControlNormal"
|
android:viewportWidth="24"
|
||||||
android:viewportWidth="960"
|
android:viewportHeight="24">
|
||||||
android:viewportHeight="960">
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="#FF000000"
|
||||||
android:pathData="M438,617L613,503Q627,494 627,478Q627,462 613,453L438,339Q423,329 407.5,337.37Q392,345.74 392,364L392,592Q392,610.26 407.5,618.63Q423,627 438,617ZM140,800Q116,800 98,782Q80,764 80,740L80,220Q80,196 98,178Q116,160 140,160L820,160Q844.75,160 862.38,178Q880,196 880,220L880,740Q880,764 862.38,782Q844.75,800 820,800L140,800ZM140,740L140,740Q140,740 140,740Q140,740 140,740L140,220Q140,220 140,220Q140,220 140,220L140,220Q140,220 140,220Q140,220 140,220L140,740Q140,740 140,740Q140,740 140,740ZM140,740L820,740Q820,740 820,740Q820,740 820,740L820,220Q820,220 820,220Q820,220 820,220L140,220Q140,220 140,220Q140,220 140,220L140,740Q140,740 140,740Q140,740 140,740Z" />
|
android:fillAlpha="0"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:strokeColor="#FF000000"
|
||||||
|
android:pathData="M3,6C3,4.89543 3.89543,4 5,4H19C20.1046,4 21,4.89543 21,6V18C21,19.1046 20.1046,20 19,20H5C3.89543,20 3,19.1046 3,18V6Z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:fillAlpha="0"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:strokeColor="#FF000000"
|
||||||
|
android:pathData="M10,9l5,3l-5,3l0,-6z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<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="#FF000000"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:strokeColor="#FF000000"
|
||||||
|
android:pathData="M3,6C3,4.89543 3.89543,4 5,4H19C20.1046,4 21,4.89543 21,6V18C21,19.1046 20.1046,20 19,20H5C3.89543,20 3,19.1046 3,18V6Z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:strokeColor="#FF000000"
|
||||||
|
android:pathData="M10,9l5,3l-5,3l0,-6z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="48dp"
|
android:width="24dp"
|
||||||
android:height="48dp"
|
android:height="24dp"
|
||||||
android:tint="?attr/colorControlNormal"
|
android:viewportWidth="24"
|
||||||
android:viewportWidth="960"
|
android:viewportHeight="24">
|
||||||
android:viewportHeight="960">
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="#FF000000"
|
||||||
android:pathData="M546,880L414,880Q403,880 394.5,873Q386,866 384,855L368,754Q349,747 328,735Q307,723 291,710L198,753Q187,758 176,754.5Q165,751 159,740L93,623Q87,613 90,602Q93,591 102,584L188,521Q186,512 185.5,500.5Q185,489 185,480Q185,471 185.5,459.5Q186,448 188,439L102,376Q93,369 90,358Q87,347 93,337L159,220Q165,209 176,205.5Q187,202 198,207L291,250Q307,237 328,225Q349,213 368,207L384,105Q386,94 394.5,87Q403,80 414,80L546,80Q557,80 565.5,87Q574,94 576,105L592,206Q611,213 632.5,224.5Q654,236 669,250L762,207Q773,202 784,205.5Q795,209 801,220L867,336Q873,346 870.5,357.5Q868,369 858,376L772,437Q774,447 774.5,458.5Q775,470 775,480Q775,490 774.5,501Q774,512 772,522L858,584Q867,591 870,602Q873,613 867,623L801,740Q795,751 784,754.5Q773,758 762,753L669,710Q653,723 632.5,735.5Q612,748 592,754L576,855Q574,866 565.5,873Q557,880 546,880ZM480,610Q534,610 572,572Q610,534 610,480Q610,426 572,388Q534,350 480,350Q426,350 388,388Q350,426 350,480Q350,534 388,572Q426,610 480,610ZM480,550Q451,550 430.5,529.5Q410,509 410,480Q410,451 430.5,430.5Q451,410 480,410Q509,410 529.5,430.5Q550,451 550,480Q550,509 529.5,529.5Q509,550 480,550ZM480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480ZM436,820L524,820L538,708Q571,700 600.5,683Q630,666 654,642L760,688L800,616L706,547Q710,530 712.5,513.5Q715,497 715,480Q715,463 713,446.5Q711,430 706,413L800,344L760,272L654,318Q631,292 602,274.5Q573,257 538,252L524,140L436,140L422,252Q388,259 358.5,276Q329,293 306,318L200,272L160,344L254,413Q250,430 247.5,446.5Q245,463 245,480Q245,497 247.5,513.5Q250,530 254,547L160,616L200,688L306,642Q330,666 359.5,683Q389,700 422,708L436,820Z" />
|
android:fillAlpha="0"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:strokeColor="#FF000000"
|
||||||
|
android:pathData="M12,12m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:fillAlpha="0"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:strokeColor="#FF000000"
|
||||||
|
android:pathData="M19.14,12.94C19.18,12.64 19.2,12.32 19.2,12C19.2,11.68 19.18,11.36 19.14,11.06L21.16,9.48C21.34,9.34 21.4,9.08 21.28,8.88L19.36,5.52C19.24,5.32 18.98,5.24 18.76,5.32L16.36,6.34C15.84,5.94 15.28,5.62 14.66,5.38L14.3,2.8C14.26,2.58 14.08,2.4 13.84,2.4H10.16C9.92,2.4 9.74,2.58 9.7,2.8L9.34,5.38C8.72,5.62 8.16,5.94 7.64,6.34L5.24,5.32C5.02,5.24 4.76,5.32 4.64,5.52L2.72,8.88C2.6,9.08 2.66,9.34 2.84,9.48L4.86,11.06C4.82,11.36 4.8,11.68 4.8,12C4.8,12.32 4.82,12.64 4.86,12.94L2.84,14.52C2.66,14.66 2.6,14.92 2.72,15.12L4.64,18.48C4.76,18.68 5.02,18.76 5.24,18.68L7.64,17.66C8.16,18.06 8.72,18.38 9.34,18.62L9.7,21.2C9.74,21.42 9.92,21.6 10.16,21.6H13.84C14.08,21.6 14.26,21.42 14.3,21.2L14.66,18.62C15.28,18.38 15.84,18.06 16.36,17.66L18.76,18.68C18.98,18.76 19.24,18.68 19.36,18.48L21.28,15.12C21.4,14.92 21.34,14.66 21.16,14.52L19.14,12.94Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<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="#FF000000"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:strokeColor="#FF000000"
|
||||||
|
android:pathData="M12,12m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:strokeColor="#FF000000"
|
||||||
|
android:pathData="M19.14,12.94C19.18,12.64 19.2,12.32 19.2,12C19.2,11.68 19.18,11.36 19.14,11.06L21.16,9.48C21.34,9.34 21.4,9.08 21.28,8.88L19.36,5.52C19.24,5.32 18.98,5.24 18.76,5.32L16.36,6.34C15.84,5.94 15.28,5.62 14.66,5.38L14.3,2.8C14.26,2.58 14.08,2.4 13.84,2.4H10.16C9.92,2.4 9.74,2.58 9.7,2.8L9.34,5.38C8.72,5.62 8.16,5.94 7.64,6.34L5.24,5.32C5.02,5.24 4.76,5.32 4.64,5.52L2.72,8.88C2.6,9.08 2.66,9.34 2.84,9.48L4.86,11.06C4.82,11.36 4.8,11.68 4.8,12C4.8,12.32 4.82,12.64 4.86,12.94L2.84,14.52C2.66,14.66 2.6,14.92 2.72,15.12L4.64,18.48C4.76,18.68 5.02,18.76 5.24,18.68L7.64,17.66C8.16,18.06 8.72,18.38 9.34,18.62L9.7,21.2C9.74,21.42 9.92,21.6 10.16,21.6H13.84C14.08,21.6 14.26,21.42 14.3,21.2L14.66,18.62C15.28,18.38 15.84,18.06 16.36,17.66L18.76,18.68C18.98,18.76 19.24,18.68 19.36,18.48L21.28,15.12C21.4,14.92 21.34,14.66 21.16,14.52L19.14,12.94Z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="960"
|
android:viewportWidth="960"
|
||||||
android:viewportHeight="960">
|
android:viewportHeight="960">
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#000000"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M140,160L214,312L344,312L270,160L359,160L433,312L563,312L489,160L578,160L652,312L782,312L708,160L820,160Q844,160 862,178Q880,196 880,220L880,740Q880,764 862,782Q844,800 820,800L140,800Q116,800 98,782Q80,764 80,740L80,220Q80,196 98,178Q116,160 140,160L140,160ZM140,372L140,740Q140,740 140,740Q140,740 140,740L820,740Q820,740 820,740Q820,740 820,740L820,372L140,372ZM140,372L140,372L140,740Q140,740 140,740Q140,740 140,740L140,740Q140,740 140,740Q140,740 140,740L140,372Z" />
|
||||||
|
</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:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z" />
|
||||||
|
</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="#FFFFFFFF"
|
||||||
|
android:pathData="M20,3H4C2.9,3 2,3.9 2,5v14c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V5C22,3.9 21.1,3 20,3zM9,17H7v-5h2V17zM13,17h-2V7h2V17zM17,17h-2v-9h2V17z" />
|
||||||
|
</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="#FFFFFFFF"
|
||||||
|
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12V5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94V12H5V6.3l7,-3.11v8.8z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,17v2h6v-2H3zM3,5v2h10V5H3zM13,21v-2h8v-2h-8v-2h-2v6H13zM7,9v2H3v2h4v2h2V9H7zM21,13v-2H11v2H21zM15,9h2V7h4V5h-4V3h-2V9z" />
|
||||||
|
</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="#FFFFFFFF"
|
||||||
|
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
|
||||||
|
</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="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>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ic_nav_live_selected" android:state_checked="true" />
|
||||||
|
<item android:drawable="@drawable/ic_nav_live" android:state_checked="false" />
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ic_nav_setting_selected" android:state_checked="true" />
|
||||||
|
<item android:drawable="@drawable/ic_nav_setting" android:state_checked="false" />
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ic_nav_vod_selected" android:state_checked="true" />
|
||||||
|
<item android:drawable="@drawable/ic_nav_vod" android:state_checked="false" />
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?attr/colorControlHighlight">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@color/primary" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="14dp"
|
||||||
|
android:left="12dp"
|
||||||
|
android:right="12dp"
|
||||||
|
android:top="14dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="14dp" />
|
||||||
|
<solid android:color="#212121" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?attr/colorControlHighlight">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<solid android:color="#f5f5f5" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@color/black_20" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="8dp"
|
||||||
|
android:left="12dp"
|
||||||
|
android:right="12dp"
|
||||||
|
android:top="8dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?attr/colorControlHighlight">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:color="#802196F3">
|
android:color="#80FFEB3B">
|
||||||
<item android:id="@android:id/mask">
|
<item android:id="@android:id/mask">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="#f5f5f5" />
|
<solid android:color="#f5f5f5" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
android:color="?attr/colorControlHighlight">
|
android:color="?attr/colorControlHighlight">
|
||||||
<item android:id="@android:id/background">
|
<item android:id="@android:id/background">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="@color/yellow_500" />
|
<solid android:color="@color/primary" />
|
||||||
<corners android:radius="8dp" />
|
<corners android:radius="8dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="10dp"
|
android:bottom="10dp"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
android:color="?attr/colorControlHighlight">
|
android:color="?attr/colorControlHighlight">
|
||||||
<item android:id="@android:id/background">
|
<item android:id="@android:id/background">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="@color/yellow_500" />
|
<solid android:color="@color/primary" />
|
||||||
<corners android:radius="8dp" />
|
<corners android:radius="8dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="18dp"
|
android:bottom="18dp"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:color="#8066BB6A">
|
android:color="#80FFEB3B">
|
||||||
<item android:id="@android:id/mask">
|
<item android:id="@android:id/mask">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="#f5f5f5" />
|
<solid android:color="#f5f5f5" />
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<item android:id="@android:id/background">
|
<item android:id="@android:id/background">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="@color/black_20" />
|
<solid android:color="@color/black_20" />
|
||||||
<corners android:radius="8dp" />
|
<corners android:radius="12dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="14dp"
|
android:bottom="14dp"
|
||||||
android:left="16dp"
|
android:left="16dp"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
android:color="?attr/colorControlHighlight">
|
android:color="?attr/colorControlHighlight">
|
||||||
<item android:id="@android:id/background">
|
<item android:id="@android:id/background">
|
||||||
<shape android:shape="rectangle">
|
<shape android:shape="rectangle">
|
||||||
<solid android:color="@color/yellow_500" />
|
<solid android:color="@color/primary" />
|
||||||
<corners android:radius="8dp" />
|
<corners android:radius="8dp" />
|
||||||
<padding
|
<padding
|
||||||
android:bottom="18dp"
|
android:bottom="18dp"
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="14dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/white_10" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?attr/colorControlHighlight">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@color/primary" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="12dp"
|
||||||
|
android:left="16dp"
|
||||||
|
android:right="16dp"
|
||||||
|
android:top="12dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?attr/colorControlHighlight">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="@color/black_20" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="12dp"
|
||||||
|
android:left="16dp"
|
||||||
|
android:right="16dp"
|
||||||
|
android:top="12dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_20">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/top"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingEnd="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="慶餘年第一季:第一集" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/size"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="12sp"
|
||||||
|
tools:text="1920 x 1080" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time_battery"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
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="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"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_control_cast" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/info"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_control_info" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/keep"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_control_keep_off" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/setting"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_control_setting" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/center"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/prevRoot"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/shape_control"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/prev"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/exo_icon_previous" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="48dp"
|
||||||
|
android:layout_marginEnd="48dp"
|
||||||
|
android:background="@drawable/shape_control">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/play"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/exo_icon_play" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/nextRoot"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/shape_control"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/next"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/exo_icon_next" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/danmaku"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:src="@drawable/ic_control_danmaku_on" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/right"
|
||||||
|
layout="@layout/view_control_right"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="8dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bottom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/parse"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:itemCount="5"
|
||||||
|
tools:listitem="@layout/adapter_parse_dark" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp">
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSeekView
|
||||||
|
android:id="@+id/seek"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/full"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_control_full" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/action"
|
||||||
|
layout="@layout/view_control_vod_action"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</RelativeLayout>
|
||||||
@@ -154,5 +154,15 @@
|
|||||||
app:spanCount="2"
|
app:spanCount="2"
|
||||||
tools:listitem="@layout/adapter_vod_rect" />
|
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>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_back" />
|
android:src="@drawable/ic_back" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_action_sync" />
|
android:src="@drawable/ic_action_sync" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_action_delete"
|
android:src="@drawable/ic_action_delete"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -65,5 +65,15 @@
|
|||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingBottom="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>
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -14,14 +14,14 @@
|
|||||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
android:id="@+id/navigation"
|
android:id="@+id/navigation"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="68dp"
|
android:layout_height="70dp"
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
android:background="@color/transparent"
|
android:background="@color/transparent"
|
||||||
app:elevation="0dp"
|
app:elevation="0dp"
|
||||||
app:itemIconSize="24dp"
|
app:itemIconSize="24dp"
|
||||||
app:itemIconTint="@color/nav"
|
app:itemIconTint="@color/nav"
|
||||||
app:itemTextColor="@color/nav"
|
app:itemTextColor="@color/nav"
|
||||||
app:labelVisibilityMode="labeled"
|
app:labelVisibilityMode="unlabeled"
|
||||||
app:itemActiveIndicatorStyle="@style/Indicator"
|
app:itemActiveIndicatorStyle="@style/Indicator"
|
||||||
app:menu="@menu/menu_nav" />
|
app:menu="@menu/menu_nav" />
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_back" />
|
android:src="@drawable/ic_back" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_action_sync" />
|
android:src="@drawable/ic_action_sync" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_action_delete"
|
android:src="@drawable/ic_action_delete"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
@@ -65,5 +65,14 @@
|
|||||||
android:paddingEnd="8dp"
|
android:paddingEnd="8dp"
|
||||||
android:paddingBottom="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>
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -0,0 +1,367 @@
|
|||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/back"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:background="@drawable/shape_action_background"
|
||||||
|
android:src="@drawable/ic_back" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/setting_player"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:overScrollMode="never">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingEnd="24dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/render"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_render"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/renderText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="Surface" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/scale"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_scale"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/scaleText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="預設" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/caption"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_caption"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/captionText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:gravity="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="預設" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/buffer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_buffer"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bufferText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="1" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="@string/times"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/speed"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_speed"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/speedText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="1" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="@string/times"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/tunnel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/player_tunnel"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/tunnelSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/audioDecode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/player_audio_decode"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/audioDecodeSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/aac"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/player_aac"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/aacSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/danmakuLoad"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/player_danmaku_load"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/danmakuLoadSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/background"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_background"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/backgroundText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="畫中畫" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ua"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/shape_item"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_ua"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/uaText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:gravity="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="okhttp/4.11.0" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
</LinearLayout>
|
||||||
@@ -4,11 +4,14 @@
|
|||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
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:background="@drawable/shape_item"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/text"
|
android:textColor="@color/text"
|
||||||
android:textSize="14sp"
|
android:textSize="12sp"
|
||||||
tools:text="泥巴" />
|
tools:text="泥巴" />
|
||||||
|
|||||||
@@ -6,17 +6,37 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<FrameLayout
|
||||||
android:id="@+id/text"
|
|
||||||
style="@style/Widget.App.Button.OutlinedButton.SiteDialog"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1">
|
||||||
android:ellipsize="middle"
|
|
||||||
android:singleLine="true"
|
<TextView
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:id="@+id/text"
|
||||||
android:textSize="14sp"
|
android:layout_width="match_parent"
|
||||||
tools:text="https://fongmi.github.io/cat.json" />
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/shape_site_dialog"
|
||||||
|
android:gravity="center"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingTop="14dp"
|
||||||
|
android:paddingBottom="14dp"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="https://fongmi.github.io/cat.json" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/copy"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_setting_copy" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/delete"
|
android:id="@+id/delete"
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="关于"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="开发说明"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="本项目仅用于学习Android开发,代码改自FongMi/TV (https://github.com/FongMi/TV)。"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:layout_marginTop="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="免责声明"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="1.本项目仅供学习交流使用,不得用于商业用途\n2.项目中的内容均来自网络,如有侵权请联系删除\n3.使用本项目产生的一切后果由使用者自行承担"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:layout_marginTop="8dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/github"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:background="@drawable/shape_about_button"
|
||||||
|
android:text="我的GitHub"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:padding="12dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -15,9 +15,12 @@
|
|||||||
android:stepSize="1"
|
android:stepSize="1"
|
||||||
android:valueFrom="1"
|
android:valueFrom="1"
|
||||||
android:valueTo="10"
|
android:valueTo="10"
|
||||||
app:thumbColor="@color/yellow_500"
|
app:thumbColor="@color/accent"
|
||||||
app:tickVisible="false"
|
app:thumbRadius="10dp"
|
||||||
app:trackColorActive="@color/yellow_500"
|
app:tickVisible="true"
|
||||||
app:trackColorInactive="@color/yellow_50" />
|
app:tickColor="@color/black_50"
|
||||||
|
app:trackColorActive="@color/accent"
|
||||||
|
app:trackColorInactive="@color/white_20"
|
||||||
|
app:trackHeight="6dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -8,12 +8,30 @@
|
|||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
tools:background="@color/white">
|
tools:background="@color/white">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/control_speed"
|
android:orientation="horizontal"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:gravity="center_vertical">
|
||||||
android:textSize="16sp" />
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/control_speed"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/speedValue"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="1.0x"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
<com.google.android.material.slider.Slider
|
||||||
android:id="@+id/speed"
|
android:id="@+id/speed"
|
||||||
@@ -24,10 +42,13 @@
|
|||||||
android:stepSize="0.25"
|
android:stepSize="0.25"
|
||||||
android:valueFrom="0.5"
|
android:valueFrom="0.5"
|
||||||
android:valueTo="5"
|
android:valueTo="5"
|
||||||
app:thumbColor="@color/yellow_500"
|
app:thumbColor="@color/accent"
|
||||||
app:tickVisible="false"
|
app:thumbRadius="10dp"
|
||||||
app:trackColorActive="@color/yellow_500"
|
app:tickVisible="true"
|
||||||
app:trackColorInactive="@color/yellow_50" />
|
app:tickColor="@color/black_50"
|
||||||
|
app:trackColorActive="@color/accent"
|
||||||
|
app:trackColorInactive="@color/white_20"
|
||||||
|
app:trackHeight="6dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/parseText"
|
android:id="@+id/parseText"
|
||||||
|
|||||||
@@ -15,8 +15,12 @@
|
|||||||
android:stepSize="0.5"
|
android:stepSize="0.5"
|
||||||
android:valueFrom="2"
|
android:valueFrom="2"
|
||||||
android:valueTo="5"
|
android:valueTo="5"
|
||||||
app:tickVisible="false"
|
app:thumbColor="@color/accent"
|
||||||
|
app:thumbRadius="10dp"
|
||||||
|
app:tickVisible="true"
|
||||||
|
app:tickColor="@color/black_50"
|
||||||
app:trackColorActive="@color/accent"
|
app:trackColorActive="@color/accent"
|
||||||
app:trackColorInactive="@color/yellow_50" />
|
app:trackColorInactive="@color/white_20"
|
||||||
|
app:trackHeight="6dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -29,50 +29,80 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/time1"
|
android:id="@+id/time1"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="56dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:background="@drawable/shape_accent"
|
android:background="@drawable/shape_accent_no_border"
|
||||||
android:tag="5"
|
android:tag="5"
|
||||||
android:text="@string/timer_5"
|
android:text="@string/timer_5"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/time2"
|
android:id="@+id/time2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="56dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:background="@drawable/shape_accent"
|
android:background="@drawable/shape_accent_no_border"
|
||||||
android:tag="15"
|
android:tag="15"
|
||||||
android:text="@string/timer_15"
|
android:text="@string/timer_15"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/time3"
|
android:id="@+id/time3"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="56dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:background="@drawable/shape_accent"
|
android:background="@drawable/shape_accent_no_border"
|
||||||
android:tag="30"
|
android:tag="30"
|
||||||
android:text="@string/timer_30"
|
android:text="@string/timer_30"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/time4"
|
android:id="@+id/time4"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="56dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:background="@drawable/shape_accent"
|
android:background="@drawable/shape_accent_no_border"
|
||||||
android:tag="60"
|
android:tag="60"
|
||||||
android:text="@string/timer_60"
|
android:text="@string/timer_60"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time5"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="@drawable/shape_accent_no_border"
|
||||||
|
android:tag="120"
|
||||||
|
android:text="@string/timer_120"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time6"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="@drawable/shape_accent_no_border"
|
||||||
|
android:tag="180"
|
||||||
|
android:text="@string/timer_180"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -96,25 +126,41 @@
|
|||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="5:00" />
|
tools:text="5:00" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<TextView
|
||||||
android:id="@+id/delay"
|
android:id="@+id/delay"
|
||||||
style="?attr/materialButtonStyle"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="@drawable/shape_timer_delay_button"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingStart="30dp"
|
||||||
|
android:paddingEnd="30dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/timer_delay"
|
android:text="@string/timer_delay"
|
||||||
android:textColor="@color/white" />
|
android:textColor="@color/black"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<TextView
|
||||||
android:id="@+id/reset"
|
android:id="@+id/reset"
|
||||||
style="?attr/materialButtonOutlinedStyle"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
android:background="@drawable/shape_timer_reset_button"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingStart="30dp"
|
||||||
|
android:paddingEnd="30dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/timer_cancel"
|
android:text="@string/timer_cancel"
|
||||||
android:textColor="?android:attr/textColorPrimary" />
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
android:fillViewport="true"
|
android:fillViewport="true"
|
||||||
android:overScrollMode="never"
|
android:overScrollMode="never"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
@@ -58,15 +59,31 @@
|
|||||||
android:paddingBottom="24dp">
|
android:paddingBottom="24dp">
|
||||||
|
|
||||||
<!-- 源管理分组 -->
|
<!-- 源管理分组 -->
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:text="@string/setting_source"
|
android:orientation="horizontal"
|
||||||
android:textColor="@color/white"
|
android:gravity="center_vertical">
|
||||||
android:textSize="14sp"
|
|
||||||
android:alpha="0.7" />
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/setting_source"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:alpha="0.7" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="(单击视频输入框可加源)"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:alpha="0.5" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -77,10 +94,11 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/vod"
|
android:id="@+id/vod"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="56dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -112,7 +130,8 @@
|
|||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_setting_home" />
|
android:src="@drawable/potted_plant_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/vodHistory"
|
android:id="@+id/vodHistory"
|
||||||
@@ -121,11 +140,13 @@
|
|||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_setting_history" />
|
android:src="@drawable/ic_m3_list_alt"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/liveContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
@@ -135,10 +156,11 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/live"
|
android:id="@+id/live"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="56dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -170,7 +192,8 @@
|
|||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_setting_home" />
|
android:src="@drawable/potted_plant_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/liveHistory"
|
android:id="@+id/liveHistory"
|
||||||
@@ -179,67 +202,11 @@
|
|||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_setting_history" />
|
android:src="@drawable/ic_m3_list_alt"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/wall"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/setting_wall"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/wallUrl"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="middle"
|
|
||||||
android:gravity="end"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="https://" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/wallDefault"
|
|
||||||
android:layout_width="56dp"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:src="@drawable/ic_setting_switch" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/wallRefresh"
|
|
||||||
android:layout_width="56dp"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:src="@drawable/ic_setting_refresh" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- 应用设置分组 -->
|
<!-- 应用设置分组 -->
|
||||||
<TextView
|
<TextView
|
||||||
@@ -253,74 +220,136 @@
|
|||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:alpha="0.7" />
|
android:alpha="0.7" />
|
||||||
|
|
||||||
|
<!-- 将三个设置项放在一个共同的背景容器中 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/player"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
android:orientation="horizontal">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<!-- 播放器设置 -->
|
||||||
android:layout_width="wrap_content"
|
<LinearLayout
|
||||||
android:layout_height="wrap_content"
|
android:id="@+id/player"
|
||||||
android:text="@string/setting_player"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/incognito"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingBottom="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/setting_incognito"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
|
||||||
android:id="@+id/incognitoSwitch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/M3SwitchStyle" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/size"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/setting_size"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/sizeText"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:orientation="horizontal"
|
||||||
android:textColor="@color/white"
|
android:paddingTop="16dp"
|
||||||
android:textSize="16sp"
|
android:paddingBottom="12dp">
|
||||||
tools:text="Medium" />
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/hive_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/setting_player"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 无痕模式 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/incognito"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/domino_mask_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/setting_incognito"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/incognitoSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 显示直播 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/liveTabVisible"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_nav_live"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/setting_live_tab_visible"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/liveTabVisibleSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 图片尺寸 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/size"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/select_all_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/setting_size"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/sizeText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="Medium" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 网络设置分组 -->
|
<!-- 网络设置分组 -->
|
||||||
@@ -335,84 +364,117 @@
|
|||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:alpha="0.7" />
|
android:alpha="0.7" />
|
||||||
|
|
||||||
|
<!-- 将三个网络设置项放在一个共同的背景容器中 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/doh"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
android:orientation="horizontal">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<!-- DoH设置 -->
|
||||||
android:layout_width="wrap_content"
|
<LinearLayout
|
||||||
android:layout_height="wrap_content"
|
android:id="@+id/doh"
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/setting_doh"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/dohText"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:orientation="horizontal"
|
||||||
android:textColor="@color/white"
|
android:paddingTop="16dp"
|
||||||
android:textSize="16sp"
|
android:paddingBottom="16dp">
|
||||||
tools:text="Google" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/globe_book_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@+id/proxy"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginTop="12dp"
|
android:text="@string/setting_doh"
|
||||||
android:background="@drawable/shape_item"
|
android:textColor="@color/white"
|
||||||
android:orientation="horizontal">
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/dohText"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/setting_proxy"
|
android:gravity="end"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp"
|
||||||
|
tools:text="Google" />
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:id="@+id/proxyText"
|
|
||||||
|
<!-- 代理设置 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/proxy"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:orientation="horizontal"
|
||||||
android:textColor="@color/white"
|
android:paddingTop="16dp"
|
||||||
android:textSize="16sp"
|
android:paddingBottom="16dp">
|
||||||
tools:text="http" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_fab_link"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@+id/cache"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginTop="12dp"
|
android:text="@string/setting_proxy"
|
||||||
android:background="@drawable/shape_item"
|
android:textColor="@color/white"
|
||||||
android:orientation="horizontal">
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/proxyText"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/setting_cache"
|
android:gravity="end"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp"
|
||||||
|
tools:text="http" />
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:id="@+id/cacheText"
|
|
||||||
|
<!-- 缓存设置 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/cache"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:orientation="horizontal"
|
||||||
android:textColor="@color/white"
|
android:paddingTop="16dp"
|
||||||
android:textSize="16sp"
|
android:paddingBottom="16dp">
|
||||||
tools:text="1.0 MB" />
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/owl_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/setting_cache"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cacheText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="1.0 MB" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 备份与恢复分组 -->
|
<!-- 备份与恢复分组 -->
|
||||||
@@ -434,62 +496,111 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="0dp">
|
android:padding="0dp">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/backup"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:gravity="center_vertical"
|
||||||
android:paddingStart="16dp"
|
android:orientation="horizontal">
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:text="@string/setting_backup"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/restore"
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/photo_prints_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/backup"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:text="@string/setting_backup"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:gravity="center_vertical"
|
||||||
android:gravity="end"
|
android:orientation="horizontal">
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingTop="16dp"
|
<TextView
|
||||||
android:paddingEnd="16dp"
|
android:id="@+id/restore"
|
||||||
android:paddingBottom="16dp"
|
android:layout_width="match_parent"
|
||||||
android:text="@string/setting_restore"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="@color/white"
|
android:gravity="end"
|
||||||
android:textSize="16sp" />
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:text="@string/setting_restore"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/version"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="12dp"
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/version"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_weight="1"
|
||||||
android:text="@string/setting_version"
|
android:layout_marginEnd="12dp"
|
||||||
android:textColor="@color/white"
|
android:background="@drawable/shape_item"
|
||||||
android:textSize="16sp" />
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/versionText"
|
android:layout_width="24dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_setting_github"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/versionText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/setting_version"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="版本 3.0.3" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/about"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:layout_weight="1"
|
||||||
android:textColor="@color/white"
|
android:background="@drawable/shape_item"
|
||||||
android:textSize="16sp"
|
android:orientation="horizontal">
|
||||||
tools:text="1.2.1" />
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/egg_24px"
|
||||||
|
android:tint="@color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="关于"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
@@ -55,324 +55,407 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="16dp">
|
android:paddingStart="24dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="24dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<!-- 播放器基本设置模块 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/render"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
android:orientation="horizontal">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<!-- 渲染方式 -->
|
||||||
android:layout_width="wrap_content"
|
<LinearLayout
|
||||||
android:layout_height="wrap_content"
|
android:id="@+id/render"
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/player_render"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/renderText"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:orientation="horizontal"
|
||||||
android:textColor="@color/white"
|
android:padding="16dp"
|
||||||
android:textSize="16sp"
|
android:background="?attr/selectableItemBackground">
|
||||||
tools:text="Surface" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_render"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@+id/scale"
|
android:id="@+id/renderText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:gravity="end"
|
||||||
android:background="@drawable/shape_item"
|
android:textColor="@color/white"
|
||||||
android:orientation="horizontal">
|
android:textSize="16sp"
|
||||||
|
tools:text="Surface" />
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/player_scale"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
<!-- 分隔线 -->
|
||||||
android:id="@+id/scaleText"
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="#22FFFFFF" />
|
||||||
|
|
||||||
|
<!-- 缩放比例 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/scale"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:orientation="horizontal"
|
||||||
android:textColor="@color/white"
|
android:padding="16dp"
|
||||||
android:textSize="16sp"
|
android:background="?attr/selectableItemBackground">
|
||||||
tools:text="預設" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_scale"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@+id/caption"
|
android:id="@+id/scaleText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:gravity="end"
|
||||||
android:background="@drawable/shape_item"
|
android:textColor="@color/white"
|
||||||
android:orientation="horizontal">
|
android:textSize="16sp"
|
||||||
|
tools:text="預設" />
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/player_caption"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
<!-- 分隔线 -->
|
||||||
android:id="@+id/captionText"
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="#22FFFFFF" />
|
||||||
|
|
||||||
|
<!-- 字幕样式 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/caption"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="middle"
|
android:orientation="horizontal"
|
||||||
android:gravity="end"
|
android:padding="16dp"
|
||||||
android:singleLine="true"
|
android:background="?attr/selectableItemBackground">
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="預設" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_caption"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@+id/buffer"
|
android:id="@+id/captionText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:ellipsize="middle"
|
||||||
android:background="@drawable/shape_item"
|
android:gravity="end"
|
||||||
android:orientation="horizontal">
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="預設" />
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/player_buffer"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
<!-- 分隔线 -->
|
||||||
android:id="@+id/bufferText"
|
<View
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="1dp"
|
||||||
android:layout_weight="1"
|
android:layout_marginStart="16dp"
|
||||||
android:gravity="end"
|
android:background="#22FFFFFF" />
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="1" />
|
|
||||||
|
|
||||||
<TextView
|
<!-- 缓冲时间 -->
|
||||||
android:layout_width="wrap_content"
|
<LinearLayout
|
||||||
android:layout_height="wrap_content"
|
android:id="@+id/buffer"
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:text="@string/times"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/speed"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/player_speed"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/speedText"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="end"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="1" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:text="@string/times"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/tunnel"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingBottom="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/player_tunnel"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
|
||||||
android:id="@+id/tunnelSwitch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/M3SwitchStyle" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/audioDecode"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingBottom="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/player_audio_decode"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
|
||||||
android:id="@+id/audioDecodeSwitch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/M3SwitchStyle" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/aac"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingBottom="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/player_aac"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
|
||||||
android:id="@+id/aacSwitch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/M3SwitchStyle" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/danmakuLoad"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingBottom="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/player_danmaku_load"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
|
||||||
android:id="@+id/danmakuLoadSwitch"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/M3SwitchStyle" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/background"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/shape_item"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/player_background"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/backgroundText"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:orientation="horizontal"
|
||||||
android:textColor="@color/white"
|
android:padding="16dp"
|
||||||
android:textSize="16sp"
|
android:background="?attr/selectableItemBackground">
|
||||||
tools:text="畫中畫" />
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_buffer"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bufferText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="1" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="@string/times"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="#22FFFFFF" />
|
||||||
|
|
||||||
|
<!-- 长按倍速 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/speed"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_speed"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/speedText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="1" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="@string/times"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="#22FFFFFF" />
|
||||||
|
|
||||||
|
<!-- 后台播放 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/background"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_background"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/backgroundText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="畫中畫" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="#22FFFFFF" />
|
||||||
|
|
||||||
|
<!-- User-Agent -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ua"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/player_ua"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/uaText"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:gravity="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="okhttp/4.11.0" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 开关设置模块 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/ua"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:background="@drawable/shape_item"
|
android:background="@drawable/shape_item"
|
||||||
android:orientation="horizontal">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<!-- 隧道模式 -->
|
||||||
android:layout_width="wrap_content"
|
<LinearLayout
|
||||||
android:layout_height="wrap_content"
|
android:id="@+id/tunnel"
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/player_ua"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/uaText"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="middle"
|
android:orientation="horizontal"
|
||||||
android:gravity="end"
|
android:gravity="center_vertical"
|
||||||
android:singleLine="true"
|
android:padding="16dp"
|
||||||
android:textColor="@color/white"
|
android:background="?attr/selectableItemBackground">
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="okhttp/4.11.0" />
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/player_tunnel"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/tunnelSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="#22FFFFFF" />
|
||||||
|
|
||||||
|
<!-- 音频软解 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/audioDecode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/player_audio_decode"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/audioDecodeSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="#22FFFFFF" />
|
||||||
|
|
||||||
|
<!-- AAC优化 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/aac"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/player_aac"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/aacSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="#22FFFFFF" />
|
||||||
|
|
||||||
|
<!-- 弹幕加载 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/danmakuLoad"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:background="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/player_danmaku_load"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.fongmi.android.tv.ui.custom.CustomSwitch
|
||||||
|
android:id="@+id/danmakuLoadSwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
android:id="@+id/logo"
|
android:id="@+id/logo"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_logo" />
|
android:src="@drawable/ic_logo" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -66,14 +66,14 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_action_keep" />
|
android:src="@drawable/ic_action_keep" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/history"
|
android:id="@+id/history"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_action_history" />
|
android:src="@drawable/ic_action_history" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -100,12 +100,51 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
<!-- 新增空源提示文本 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/empty_source_hint"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<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"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/source_hint"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/add_source_btn"
|
||||||
|
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:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/retry"
|
android:id="@+id/retry"
|
||||||
android:layout_width="56dp"
|
android:layout_width="56dp"
|
||||||
android:layout_height="56dp"
|
android:layout_height="56dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="@drawable/shape_action_background"
|
||||||
android:src="@drawable/ic_action_retry"
|
android:src="@drawable/ic_action_retry"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
app:bar_height="2dp"
|
app:bar_height="2dp"
|
||||||
|
app:scrubber_enabled_size="12dp"
|
||||||
|
app:scrubber_disabled_size="12dp"
|
||||||
app:played_color="#FFEB3B"
|
app:played_color="#FFEB3B"
|
||||||
app:scrubber_color="#FFEB3B"
|
app:scrubber_color="#FFEB3B"
|
||||||
app:buffered_color="#80FFEB3B" />
|
app:buffered_color="#80FFEB3B" />
|
||||||
|
|||||||
@@ -47,6 +47,40 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time_battery"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
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: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
|
<ImageView
|
||||||
android:id="@+id/cast"
|
android:id="@+id/cast"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:src="@drawable/ic_empty" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:text="@string/error_no_live" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -43,7 +43,9 @@
|
|||||||
<string name="timer_15">15 分钟</string>
|
<string name="timer_15">15 分钟</string>
|
||||||
<string name="timer_30">30 分钟</string>
|
<string name="timer_30">30 分钟</string>
|
||||||
<string name="timer_60">1 小时</string>
|
<string name="timer_60">1 小时</string>
|
||||||
<string name="timer_delay">延长 5 分钟</string>
|
<string name="timer_120">2 小时</string>
|
||||||
|
<string name="timer_180">3 小时</string>
|
||||||
|
<string name="timer_delay">延长 15 分钟</string>
|
||||||
<string name="timer_cancel">取消定时器</string>
|
<string name="timer_cancel">取消定时器</string>
|
||||||
|
|
||||||
<!-- Hint -->
|
<!-- Hint -->
|
||||||
|
|||||||
@@ -41,7 +41,9 @@
|
|||||||
<string name="timer_15">15 分鐘</string>
|
<string name="timer_15">15 分鐘</string>
|
||||||
<string name="timer_30">30 分鐘</string>
|
<string name="timer_30">30 分鐘</string>
|
||||||
<string name="timer_60">1 小時</string>
|
<string name="timer_60">1 小時</string>
|
||||||
<string name="timer_delay">延長 5 分鐘</string>
|
<string name="timer_120">2 小時</string>
|
||||||
|
<string name="timer_180">3 小時</string>
|
||||||
|
<string name="timer_delay">延長 15 分鐘</string>
|
||||||
<string name="timer_cancel">取消定時器</string>
|
<string name="timer_cancel">取消定時器</string>
|
||||||
|
|
||||||
<!-- Hint -->
|
<!-- Hint -->
|
||||||
|
|||||||
@@ -5,5 +5,8 @@
|
|||||||
<color name="accent">#FFEB3B</color>
|
<color name="accent">#FFEB3B</color>
|
||||||
<color name="indicator">@color/white_80</color>
|
<color name="indicator">@color/white_80</color>
|
||||||
<color name="yellow_50">#4DFFEB3B</color>
|
<color name="yellow_50">#4DFFEB3B</color>
|
||||||
|
<color name="white_50">#80FFFFFF</color>
|
||||||
|
<color name="black_50">#80000000</color>
|
||||||
|
<color name="white_30">#4DFFFFFF</color>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -43,7 +43,9 @@
|
|||||||
<string name="timer_15">15 minutes</string>
|
<string name="timer_15">15 minutes</string>
|
||||||
<string name="timer_30">30 minutes</string>
|
<string name="timer_30">30 minutes</string>
|
||||||
<string name="timer_60">1 hour</string>
|
<string name="timer_60">1 hour</string>
|
||||||
<string name="timer_delay">Add 5 minutes</string>
|
<string name="timer_120">2 hours</string>
|
||||||
|
<string name="timer_180">3 hours</string>
|
||||||
|
<string name="timer_delay">Add 15 minutes</string>
|
||||||
<string name="timer_cancel">Cancel timer</string>
|
<string name="timer_cancel">Cancel timer</string>
|
||||||
|
|
||||||
<!-- Hint -->
|
<!-- Hint -->
|
||||||
|
|||||||
@@ -13,10 +13,11 @@
|
|||||||
<item name="android:windowTranslucentStatus">false</item>
|
<item name="android:windowTranslucentStatus">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="BaseTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
<style name="BaseTheme" parent="Theme.Material3.Dark.NoActionBar">
|
||||||
<item name="colorPrimary">@color/primary</item>
|
<item name="colorPrimary">@color/primary</item>
|
||||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
|
<item name="colorControlHighlight">@color/white_20</item>
|
||||||
<item name="android:windowBackground">@null</item>
|
<item name="android:windowBackground">@null</item>
|
||||||
<item name="android:windowDisablePreview">true</item>
|
<item name="android:windowDisablePreview">true</item>
|
||||||
<item name="android:navigationBarColor">@color/transparent</item>
|
<item name="android:navigationBarColor">@color/transparent</item>
|
||||||
@@ -30,17 +31,13 @@
|
|||||||
<style name="Control.Action">
|
<style name="Control.Action">
|
||||||
<item name="android:layout_width">wrap_content</item>
|
<item name="android:layout_width">wrap_content</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:background">?attr/selectableItemBackgroundBorderless</item>
|
<item name="android:background">@drawable/shape_action_background</item>
|
||||||
<item name="android:padding">8dp</item>
|
<item name="android:padding">8dp</item>
|
||||||
<item name="android:shadowColor">@color/grey_200</item>
|
|
||||||
<item name="android:shadowDx">1</item>
|
|
||||||
<item name="android:shadowDy">1</item>
|
|
||||||
<item name="android:shadowRadius">0.5</item>
|
|
||||||
<item name="android:textColor">@color/white</item>
|
<item name="android:textColor">@color/white</item>
|
||||||
<item name="android:textSize">14sp</item>
|
<item name="android:textSize">14sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="BottomSheetDialog" parent="Theme.Material3.DayNight.BottomSheetDialog">
|
<style name="BottomSheetDialog" parent="Theme.Material3.Dark.BottomSheetDialog">
|
||||||
<item name="colorPrimary">@color/primary</item>
|
<item name="colorPrimary">@color/primary</item>
|
||||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
@@ -60,7 +57,8 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Indicator" parent="Widget.Material3.BottomNavigationView.ActiveIndicator">
|
<style name="Indicator" parent="Widget.Material3.BottomNavigationView.ActiveIndicator">
|
||||||
<item name="android:color">@color/indicator</item>
|
<item name="android:color">@color/primary</item>
|
||||||
|
<item name="android:height">42dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="BottomNavigationView.TextAppearance" parent="TextAppearance.AppCompat">
|
<style name="BottomNavigationView.TextAppearance" parent="TextAppearance.AppCompat">
|
||||||
@@ -69,20 +67,20 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="BottomNavigation" parent="Widget.Material3.BottomNavigationView">
|
<style name="BottomNavigation" parent="Widget.Material3.BottomNavigationView">
|
||||||
<item name="itemPaddingTop">8dp</item>
|
<item name="itemPaddingTop">4dp</item>
|
||||||
<item name="itemPaddingBottom">8dp</item>
|
<item name="itemPaddingBottom">4dp</item>
|
||||||
<item name="itemTextAppearanceActive">@style/BottomNavigationView.TextAppearance</item>
|
<item name="itemTextAppearanceActive">@style/BottomNavigationView.TextAppearance</item>
|
||||||
<item name="itemTextAppearanceInactive">@style/BottomNavigationView.TextAppearance</item>
|
<item name="itemTextAppearanceInactive">@style/BottomNavigationView.TextAppearance</item>
|
||||||
|
<item name="itemRippleColor">@android:color/transparent</item>
|
||||||
|
<item name="android:background">@null</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- M3 Switch Style -->
|
<!-- M3 Switch Style (Material Design) - 使用CustomSwitch完全自定义 -->
|
||||||
<style name="M3SwitchStyle" parent="Widget.AppCompat.CompoundButton.Switch">
|
<style name="M3SwitchStyle" parent="Widget.AppCompat.CompoundButton.CheckBox">
|
||||||
<item name="android:thumb">@drawable/m3_switch_thumb</item>
|
<item name="android:button">@null</item>
|
||||||
<item name="track">@drawable/m3_switch_track</item>
|
<item name="android:background">@drawable/custom_switch_bg</item>
|
||||||
<item name="android:switchMinWidth">52dp</item>
|
<item name="android:minHeight">30dp</item>
|
||||||
<item name="android:switchPadding">8dp</item>
|
<item name="android:minWidth">50dp</item>
|
||||||
<item name="android:colorControlActivated">#FFEB3B</item>
|
|
||||||
<item name="android:colorControlHighlight">@android:color/transparent</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- 自定义数据源按钮样式 -->
|
<!-- 自定义数据源按钮样式 -->
|
||||||
|
|||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '8.8.2' apply false
|
id 'com.android.application' version '8.12.0' apply false
|
||||||
id 'com.android.library' version '8.8.2' apply false
|
id 'com.android.library' version '8.12.0' apply false
|
||||||
id 'com.chaquo.python' version '15.0.1' apply false
|
id 'com.chaquo.python' version '16.1.0' apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('clean', Delete) {
|
tasks.register('clean', Delete) {
|
||||||
@@ -10,6 +10,6 @@ tasks.register('clean', Delete) {
|
|||||||
|
|
||||||
project.ext {
|
project.ext {
|
||||||
gsonVersion = '2.11.0'
|
gsonVersion = '2.11.0'
|
||||||
media3Version = '1.6.1'
|
media3Version = '1.4.1'
|
||||||
okhttpVersion = '5.0.0-alpha.14'
|
okhttpVersion = '4.12.0'
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-5
@@ -5,21 +5,23 @@ plugins {
|
|||||||
android {
|
android {
|
||||||
namespace 'com.github.catvod.crawler'
|
namespace 'com.github.catvod.crawler'
|
||||||
|
|
||||||
compileSdk 35
|
compileSdk {
|
||||||
|
version = release(36)
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk 21
|
minSdk 24
|
||||||
targetSdk 28
|
targetSdk 28
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'androidx.annotation:annotation:1.6.0'
|
api 'androidx.annotation:annotation:1.9.1'
|
||||||
api 'androidx.preference:preference:1.2.1'
|
api 'androidx.preference:preference:1.2.1'
|
||||||
api 'com.google.code.gson:gson:' + gsonVersion
|
api 'com.google.code.gson:gson:' + gsonVersion
|
||||||
api 'com.google.net.cronet:cronet-okhttp:0.1.0'
|
api 'com.google.net.cronet:cronet-okhttp:0.1.0'
|
||||||
|
|||||||
@@ -1,18 +1,124 @@
|
|||||||
package com.github.catvod.utils;
|
package com.github.catvod.utils;
|
||||||
|
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
|
import com.github.catvod.net.OkHttp;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
public class Github {
|
public class Github {
|
||||||
|
|
||||||
public static final String URL = "https://github.com/Tosencen/XMBOX";
|
public static final String URL = "https://raw.githubusercontent.com/Tosencen/XMBOX-Release/main";
|
||||||
|
|
||||||
|
// 国内镜像地址 - 使用Gitee作为镜像
|
||||||
|
public static final String CN_URL = "https://gitee.com/ochenoktochen/XMBOX-Release/raw/main";
|
||||||
|
|
||||||
|
// 存储测速结果
|
||||||
|
private static Boolean useCnMirror = null;
|
||||||
|
private static long lastCheckTime = 0;
|
||||||
|
private static final long CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24小时
|
||||||
|
|
||||||
private static String getUrl(String path, String name) {
|
private static String getUrl(String path, String name) {
|
||||||
return URL + "/" + path + "/" + name;
|
return URL + "/" + path + "/" + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getCnUrl(String path, String name) {
|
||||||
|
return CN_URL + "/" + path + "/" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getJson(boolean dev, String name) {
|
public static String getJson(boolean dev, String name) {
|
||||||
return getUrl("apk/" + (dev ? "dev" : "release"), name + ".json");
|
if (useCnMirror()) {
|
||||||
|
return getCnUrl("apk/" + (dev ? "dev" : "release"), name + ".json");
|
||||||
|
} else {
|
||||||
|
return getUrl("apk/" + (dev ? "dev" : "release"), name + ".json");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getApk(boolean dev, String name) {
|
public static String getApk(boolean dev, String name) {
|
||||||
return getUrl("apk/" + (dev ? "dev" : "release"), name + ".apk");
|
if (useCnMirror()) {
|
||||||
|
return getCnUrl("apk/" + (dev ? "dev" : "release"), name + ".apk");
|
||||||
|
} else {
|
||||||
|
return getUrl("apk/" + (dev ? "dev" : "release"), name + ".apk");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智能检测是否使用国内镜像
|
||||||
|
public static boolean useCnMirror() {
|
||||||
|
// 如果已经测试过并且在24小时内,直接返回上次的结果
|
||||||
|
long currentTime = SystemClock.elapsedRealtime();
|
||||||
|
if (useCnMirror != null && (currentTime - lastCheckTime < CHECK_INTERVAL)) {
|
||||||
|
return useCnMirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进行网络测速
|
||||||
|
useCnMirror = testMirrorSpeed();
|
||||||
|
lastCheckTime = currentTime;
|
||||||
|
return useCnMirror;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试镜像速度
|
||||||
|
private static boolean testMirrorSpeed() {
|
||||||
|
try {
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(5, TimeUnit.SECONDS) // 增加超时时间
|
||||||
|
.readTimeout(5, TimeUnit.SECONDS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 测试国际源
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
boolean intlSuccess = testUrl(client, URL + "/README.md");
|
||||||
|
long intlTime = System.currentTimeMillis() - startTime;
|
||||||
|
Logger.d("Github: International mirror test - success: " + intlSuccess + ", time: " + intlTime + "ms");
|
||||||
|
|
||||||
|
// 测试国内源
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
boolean cnSuccess = testUrl(client, CN_URL + "/README.md");
|
||||||
|
long cnTime = System.currentTimeMillis() - startTime;
|
||||||
|
Logger.d("Github: Chinese mirror test - success: " + cnSuccess + ", time: " + cnTime + "ms");
|
||||||
|
|
||||||
|
// 如果两个都成功,选择更快的
|
||||||
|
if (intlSuccess && cnSuccess) {
|
||||||
|
boolean useCn = cnTime < intlTime;
|
||||||
|
Logger.d("Github: Both mirrors work, choosing " + (useCn ? "Chinese" : "International") + " mirror");
|
||||||
|
return useCn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果只有一个成功,选择成功的那个
|
||||||
|
if (intlSuccess) {
|
||||||
|
Logger.d("Github: Only international mirror works, using it");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cnSuccess) {
|
||||||
|
Logger.d("Github: Only Chinese mirror works, using it");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果都失败,默认国际源
|
||||||
|
Logger.e("Github: Both mirrors failed, defaulting to international");
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("Github: Mirror test exception: " + e.getMessage());
|
||||||
|
return false; // 出错时默认使用国际源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean testUrl(OkHttpClient client, String url) {
|
||||||
|
Request request = new Request.Builder().url(url).build();
|
||||||
|
Call call = client.newCall(request);
|
||||||
|
try {
|
||||||
|
Response response = call.execute();
|
||||||
|
boolean success = response.isSuccessful();
|
||||||
|
response.close();
|
||||||
|
return success;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
package com.github.catvod.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|||||||
@@ -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')
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
lxml
|
||||||
|
ujson
|
||||||
|
pyquery
|
||||||
|
requests
|
||||||
|
jsonpath
|
||||||
|
cachetools
|
||||||
|
pycryptodome
|
||||||
|
beautifulsoup4
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# GitHub CLI 创建 Release 脚本
|
||||||
|
# 使用前请先运行: gh auth login
|
||||||
|
|
||||||
|
echo "创建 XMBOX v3.0.8 Release..."
|
||||||
|
|
||||||
|
gh release create v3.0.8 \
|
||||||
|
--title "XMBOX v3.0.8 - UI交互体验全面优化" \
|
||||||
|
--notes-file RELEASE_NOTES_v3.0.8.md \
|
||||||
|
--draft \
|
||||||
|
~/Desktop/mobile-arm64_v8a-v3.0.8.apk \
|
||||||
|
~/Desktop/mobile-armeabi_v7a-v3.0.8.apk \
|
||||||
|
~/Desktop/leanback-arm64_v8a-v3.0.8.apk \
|
||||||
|
~/Desktop/leanback-armeabi_v7a-v3.0.8.apk
|
||||||
|
|
||||||
|
echo "Release 创建完成(草稿状态)"
|
||||||
|
echo "请在 GitHub 上检查并发布"
|
||||||
Executable
+498
@@ -0,0 +1,498 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 设置颜色输出
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${GREEN}开始修复项目依赖...${NC}"
|
||||||
|
|
||||||
|
# 修复 app/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 app 模块依赖...${NC}"
|
||||||
|
cat > app/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.application'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.fongmi.android.tv'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "com.fongmi.android.tv"
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
ndk {
|
||||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
coreLibraryDesugaringEnabled true
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding true
|
||||||
|
}
|
||||||
|
|
||||||
|
flavorDimensions "mode", "abi"
|
||||||
|
|
||||||
|
productFlavors {
|
||||||
|
mobile {
|
||||||
|
dimension "mode"
|
||||||
|
applicationIdSuffix ".mobile"
|
||||||
|
}
|
||||||
|
|
||||||
|
leanback {
|
||||||
|
dimension "mode"
|
||||||
|
applicationIdSuffix ".leanback"
|
||||||
|
}
|
||||||
|
|
||||||
|
all32 {
|
||||||
|
dimension "abi"
|
||||||
|
ndk {
|
||||||
|
abiFilters 'armeabi-v7a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
all64 {
|
||||||
|
dimension "abi"
|
||||||
|
ndk {
|
||||||
|
abiFilters 'arm64-v8a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
checkReleaseBuilds false
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: "libs", include: ["*.aar"])
|
||||||
|
implementation project(':catvod')
|
||||||
|
implementation project(':quickjs')
|
||||||
|
implementation project(':forcetech')
|
||||||
|
implementation project(':hook')
|
||||||
|
implementation project(':jianpian')
|
||||||
|
implementation project(':thunder')
|
||||||
|
implementation project(':tvbus')
|
||||||
|
implementation project(':zlive')
|
||||||
|
|
||||||
|
// AndroidX
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
implementation 'androidx.core:core:1.10.1'
|
||||||
|
implementation 'androidx.preference:preference:1.2.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
||||||
|
|
||||||
|
// Google
|
||||||
|
implementation 'com.google.android.material:material:1.7.0'
|
||||||
|
|
||||||
|
// OkHttp
|
||||||
|
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
|
||||||
|
|
||||||
|
// Room
|
||||||
|
implementation 'androidx.room:room-runtime:2.5.2'
|
||||||
|
annotationProcessor 'androidx.room:room-compiler:2.5.2'
|
||||||
|
|
||||||
|
// Jsoup
|
||||||
|
implementation 'org.jsoup:jsoup:1.15.4'
|
||||||
|
|
||||||
|
// Coil
|
||||||
|
implementation 'io.coil-kt:coil:2.2.2'
|
||||||
|
|
||||||
|
// Other
|
||||||
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修复 catvod/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 catvod 模块依赖...${NC}"
|
||||||
|
cat > catvod/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.github.catvod'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
|
||||||
|
// OkHttp
|
||||||
|
api 'com.squareup.okhttp3:okhttp:4.11.0'
|
||||||
|
api 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.11.0'
|
||||||
|
api 'com.squareup.okhttp3:logging-interceptor:4.11.0'
|
||||||
|
|
||||||
|
// Gson
|
||||||
|
api 'com.google.code.gson:gson:2.10.1'
|
||||||
|
|
||||||
|
// Guava
|
||||||
|
api 'com.google.guava:guava:31.1-android'
|
||||||
|
|
||||||
|
// Logger
|
||||||
|
api 'com.orhanobut:logger:2.2.0'
|
||||||
|
|
||||||
|
// JSoup
|
||||||
|
api 'org.jsoup:jsoup:1.15.4'
|
||||||
|
|
||||||
|
// Other
|
||||||
|
api 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修复 quickjs/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 quickjs 模块依赖...${NC}"
|
||||||
|
cat > quickjs/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.fongmi.quickjs'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
implementation project(':catvod')
|
||||||
|
|
||||||
|
// QuickJS
|
||||||
|
implementation 'io.github.taoweiji.quickjs:quickjs-android:0.9.0'
|
||||||
|
implementation 'com.github.whl1729:quickjs-android:3.2.0'
|
||||||
|
|
||||||
|
// Concurrent
|
||||||
|
implementation 'net.sourceforge.streamsupport:streamsupport:1.7.4'
|
||||||
|
implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.4'
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修复 thunder/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 thunder 模块依赖...${NC}"
|
||||||
|
cat > thunder/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.xunlei.downloadlib'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
implementation project(':catvod')
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修复 forcetech/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 forcetech 模块依赖...${NC}"
|
||||||
|
cat > forcetech/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.forcetech'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
implementation project(':catvod')
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修复 hook/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 hook 模块依赖...${NC}"
|
||||||
|
cat > hook/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.fongmi.hook'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修复 jianpian/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 jianpian 模块依赖...${NC}"
|
||||||
|
cat > jianpian/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.p2p.jianpian'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
implementation project(':catvod')
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修复 tvbus/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 tvbus 模块依赖...${NC}"
|
||||||
|
cat > tvbus/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.tvbus'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
implementation project(':catvod')
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修复 zlive/build.gradle
|
||||||
|
echo -e "${YELLOW}修复 zlive 模块依赖...${NC}"
|
||||||
|
cat > zlive/build.gradle << 'EOF'
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.zlive'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 33
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
implementation project(':catvod')
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 修改settings.gradle添加jitpack仓库
|
||||||
|
echo -e "${YELLOW}修改 settings.gradle 添加 jitpack 仓库...${NC}"
|
||||||
|
cat > settings.gradle << 'EOF'
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/google' }
|
||||||
|
maven { url "https://jitpack.io" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/google' }
|
||||||
|
maven { url "https://jitpack.io" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include ':app'
|
||||||
|
include ':catvod'
|
||||||
|
include ':quickjs'
|
||||||
|
include ':forcetech'
|
||||||
|
include ':hook'
|
||||||
|
include ':jianpian'
|
||||||
|
include ':thunder'
|
||||||
|
include ':tvbus'
|
||||||
|
include ':zlive'
|
||||||
|
|
||||||
|
rootProject.name = "TV"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo -e "${GREEN}依赖修复完成!${NC}"
|
||||||
|
echo -e "${YELLOW}现在您可以尝试构建项目:./gradlew clean${NC}"
|
||||||
+5
-1
@@ -6,7 +6,7 @@
|
|||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
@@ -22,3 +22,7 @@ android.enableJetifier=true
|
|||||||
android.useFullClasspathForDexingTransform=true
|
android.useFullClasspathForDexingTransform=true
|
||||||
android.nonTransitiveRClass=false
|
android.nonTransitiveRClass=false
|
||||||
android.nonFinalResIds=false
|
android.nonFinalResIds=false
|
||||||
|
|
||||||
|
# Library versions
|
||||||
|
media3Version=1.3.0
|
||||||
|
okhttpVersion=4.11.0
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# 配置Java路径,使用Java 17而不是默认的Java 21
|
||||||
|
org.gradle.java.home=/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
|
||||||
|
|
||||||
|
# 增加构建内存
|
||||||
|
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
|
||||||
|
# 启用并行构建
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.caching=true
|
||||||
|
|
||||||
|
# 配置网络设置
|
||||||
|
systemProp.https.protocols=TLSv1.2,TLSv1.3
|
||||||
|
systemProp.https.proxyPort=0
|
||||||
|
systemProp.https.nonProxyHosts=localhost
|
||||||
|
|
||||||
|
# Android相关配置
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
|
android.jetifier.ignorelist=bcprov-jdk15on,annotation-experimental-1.4.1.aar,activity-1.8.0.aar,nextlib-media3ext-0.8.4.aar,sardine-android-0.9.aar,bcprov-jdk18on-1.79.jar
|
||||||
|
|
||||||
|
# 允许高版本的SDK
|
||||||
|
android.suppressUnsupportedCompileSdk=35
|
||||||
|
|
||||||
|
# 禁用增量编译以解决某些兼容性问题
|
||||||
|
android.enableBuildIncremental=false
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#Wed Mar 29 12:54:35 CST 2023
|
#Wed Mar 29 12:54:35 CST 2023
|
||||||
distributionBase=GRADLE_USER_HOME
|
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
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -499,6 +499,6 @@ public class Hook extends PackageManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canRequestPackageInstalls() {
|
public boolean canRequestPackageInstalls() {
|
||||||
return false;
|
return true; // 允许请求安装包权限
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
gradle.beforeProject { project ->
|
||||||
|
File localProperties = new File(project.projectDir, 'gradle.properties.local')
|
||||||
|
if (localProperties.exists()) {
|
||||||
|
Properties props = new Properties()
|
||||||
|
localProperties.withInputStream { props.load(it) }
|
||||||
|
props.each { key, val -> project.ext[key] = val }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
// 强制使用阿里云镜像
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/google' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||||
|
maven { url 'https://maven.aliyun.com/repository/google' }
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-10
@@ -5,26 +5,24 @@ plugins {
|
|||||||
android {
|
android {
|
||||||
namespace 'com.fongmi.android.tv.quickjs'
|
namespace 'com.fongmi.android.tv.quickjs'
|
||||||
|
|
||||||
compileSdk 35
|
compileSdk {
|
||||||
|
version = release(36)
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk 21
|
minSdk 24
|
||||||
targetSdk 28
|
targetSdk 28
|
||||||
}
|
}
|
||||||
|
|
||||||
lint {
|
|
||||||
disable 'UnsafeOptInUsageError'
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':catvod')
|
implementation project(':catvod')
|
||||||
implementation 'wang.harlon.quickjs:wrapper-java:3.2.0'
|
implementation 'wang.harlon.quickjs:wrapper-java:3.2.3'
|
||||||
implementation 'wang.harlon.quickjs:wrapper-android:3.2.0'
|
implementation 'wang.harlon.quickjs:wrapper-android:3.2.3'
|
||||||
implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.4'
|
implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.4'
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user