mirror of
https://github.com/lizongying/my-tv.git
synced 2026-10-26 22:26:29 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acff1d41fc | ||
|
|
ab71075c85 | ||
|
|
5d4353249f | ||
|
|
a8944e75b1 | ||
|
|
d1eae8947e | ||
|
|
fccd64c07b | ||
|
|
eac355a1c1 | ||
|
|
6c6736fd0a | ||
|
|
f4c1db33b3 | ||
|
|
e55584c910 | ||
|
|
cf8d7f00a8 | ||
|
|
bcfca9e28c | ||
|
|
31ec13eed7 | ||
|
|
5a78913407 | ||
|
|
bb38cf5033 | ||
|
|
51d9c4c2a7 | ||
|
|
70da1d8241 | ||
|
|
ef17427f64 | ||
|
|
c1143afb4e | ||
|
|
68f158e556 | ||
|
|
4ef95d60c4 | ||
|
|
b782d1f841 | ||
|
|
7e57aa8c03 | ||
|
|
c16a4bcb9c | ||
|
|
0286d118ed | ||
|
|
fc83defe31 | ||
|
|
6fc73efd47 | ||
|
|
b157f1eb28 | ||
|
|
2b172ae615 | ||
|
|
39e5cc7c1f | ||
|
|
df5b006024 | ||
|
|
fe888c36ac | ||
|
|
5aa08f51a1 | ||
|
|
fe92fa5853 | ||
|
|
3d8ab65955 | ||
|
|
5485a686bc | ||
|
|
716efb5a83 | ||
|
|
ca82d5409f | ||
|
|
3936629af8 | ||
|
|
bb88e754a5 | ||
|
|
49e96d18f4 | ||
|
|
43343f8faa | ||
|
|
0335f58a12 | ||
|
|
9450de2c91 | ||
|
|
d03de91c1e | ||
|
|
7057e4a8b4 | ||
|
|
188431a29f | ||
|
|
5c0152e36c | ||
|
|
a63463559f | ||
|
|
d37ada1268 | ||
|
|
00e6c8d66e | ||
|
|
0ebc166acc | ||
|
|
2975a61e14 | ||
|
|
86b9f5159a | ||
|
|
10f261be8b | ||
|
|
52d356817a | ||
|
|
6ede7f76ef | ||
|
|
a4da01adf4 | ||
|
|
8b33c7c94c | ||
|
|
84a6c0f78e | ||
|
|
1417bbf57b | ||
|
|
ce678b8741 | ||
|
|
353dc0570f | ||
|
|
d7a60531ab | ||
|
|
764f2a1549 | ||
|
|
aa4d52681f | ||
|
|
8137515605 | ||
|
|
6c16408b90 | ||
|
|
44ab3a6669 | ||
|
|
917b245bbe | ||
|
|
dd4550cf4f | ||
|
|
4bdc625718 | ||
|
|
cda19995b2 | ||
|
|
1eb2f257b5 | ||
|
|
9feb0dc4bc | ||
|
|
46311036d6 |
@@ -0,0 +1,83 @@
|
||||
name: Bug 反馈
|
||||
description: 反馈一个 Bug
|
||||
labels: [ "bug" ]
|
||||
title: "[BUG] "
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: 检查清单
|
||||
description: 确保我们的错误报告表单适合您。
|
||||
options:
|
||||
- label: 之前没有人提交过类似或相同的 bug report。
|
||||
required: true
|
||||
- label: 我正在使用本软件的最新版本。
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: my-ty 版本
|
||||
description: 请选择正在使用的版本
|
||||
options:
|
||||
- 通用版
|
||||
- 专用版
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bug
|
||||
attributes:
|
||||
label: Bug 描述
|
||||
description: 请描述 bug 详情
|
||||
placeholder: |
|
||||
e.g. Crashed when generating snapshot.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: 预期行为
|
||||
description: 你预期会发生什么?
|
||||
placeholder: |
|
||||
e.g. A New snapshot!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: actual
|
||||
attributes:
|
||||
label: 实际行为
|
||||
description: 反而发生了什么?
|
||||
placeholder: |
|
||||
e.g. Crashed.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: 复现步骤
|
||||
description: 如何复现这个 bug。
|
||||
placeholder: |
|
||||
1. Open the app
|
||||
2. Crashed
|
||||
|
||||
What an app.
|
||||
- type: input
|
||||
id: ui
|
||||
attributes:
|
||||
label: UI / OS
|
||||
description: 你的电视系统 UI 或 OS 或 品牌
|
||||
placeholder: TCL / XIAOMI / PHONE / etc.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: android
|
||||
attributes:
|
||||
label: Android 版本
|
||||
description: 你的 Android 版本
|
||||
placeholder: "12"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: 额外信息
|
||||
description: 任何你觉得值得说的。
|
||||
@@ -0,0 +1,36 @@
|
||||
name: 功能(新频道)请求
|
||||
description: 提出一个建议
|
||||
labels: [ "enhancement" ]
|
||||
title: "[FR] "
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: 检查清单
|
||||
description: 确保我们的错误报告表单适合您。
|
||||
options:
|
||||
- label: 之前没有人提交过类似或相同的功能请求。
|
||||
required: true
|
||||
- label: 这个建议不会背离 my-tv 的初衷。
|
||||
required: true
|
||||
- type: textarea
|
||||
id: propose
|
||||
attributes:
|
||||
label: 改进目的
|
||||
description: 改进有什么用
|
||||
placeholder: |
|
||||
Show your idea here.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: 解决方案
|
||||
description: 你会怎么完成这个改进?
|
||||
placeholder: |
|
||||
How to do it on your opinion? Or left this blank
|
||||
- type: textarea
|
||||
id: addition
|
||||
attributes:
|
||||
label: 额外信息
|
||||
description: 任何你觉得值得说的。
|
||||
@@ -37,6 +37,13 @@ jobs:
|
||||
# override default build-tools version (29.0.3) -- optional
|
||||
BUILD_TOOLS_VERSION: "34.0.0"
|
||||
|
||||
- name: Get History
|
||||
id: get_history
|
||||
run: |
|
||||
chmod +x history.sh
|
||||
output=$(./history.sh)
|
||||
echo "$output" > history.md
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
@@ -47,6 +54,7 @@ jobs:
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
body_path: history.md
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
@@ -56,4 +64,19 @@ jobs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ${{ steps.sign_app.outputs.signedReleaseFile }}
|
||||
asset_name: my-tv-${{ github.ref_name }}.apk
|
||||
asset_content_type: application/vnd.android.package-archive
|
||||
asset_content_type: application/vnd.android.package-archive
|
||||
|
||||
- name: Gitee Create Release
|
||||
run: |
|
||||
latest_commit=$(git rev-parse HEAD)
|
||||
history=$(cat history.md)
|
||||
curl -v POST https://gitee.com/api/v5/repos/${{ github.repository }}/releases \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"access_token": "${{ secrets.GITEE_ACCESS_TOKEN}}",
|
||||
"tag_name": "${{ github.ref_name }}",
|
||||
"name": "Release ${{ github.ref_name }}",
|
||||
"body": "'"$history"'",
|
||||
"prerelease": false,
|
||||
"target_commitish": "'"$latest_commit"'"
|
||||
}'
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
## 更新日誌
|
||||
|
||||
### v2.1.8
|
||||
|
||||
* 支持x86設備
|
||||
|
||||
### v2.1.7
|
||||
|
||||
*
|
||||
|
||||
### v2.1.4
|
||||
|
||||
* 解決切換頻道時黑屏問題
|
||||
|
||||
### v2.1.1
|
||||
|
||||
* 平衡聲音
|
||||
|
||||
### v2.1.0
|
||||
|
||||
* 恢復播放
|
||||
|
||||
### 以下已不可用
|
||||
|
||||
### v2.0.9
|
||||
|
||||
* 修復部分設備閃退的問題
|
||||
* 修復設置頁可能顯示不全的問題
|
||||
|
||||
### v2.0.5
|
||||
|
||||
* 設置頁增加恢復默認按鈕,目前只用於同步系統時間
|
||||
|
||||
### v2.0.4
|
||||
|
||||
* 修復不能播放的問題
|
||||
* 包含國際頻道
|
||||
|
||||
### v2.0.3
|
||||
|
||||
* 修復不能播放的問題
|
||||
* 不包含國際頻道
|
||||
|
||||
### v2.0.0
|
||||
|
||||
* 解決卡頓問題
|
||||
* 解決網格樣式時,點擊空白處不退出的問題
|
||||
* 解決頻道列表樣式切換可能不生效的問題
|
||||
|
||||
### v1.9.8(通用)
|
||||
|
||||
* 優化在線升級
|
||||
* 優化可能狀態錯誤的問題
|
||||
|
||||
### v1.9.6(通用)
|
||||
|
||||
* 優化在線升級
|
||||
* 優化可能狀態錯誤的問題
|
||||
|
||||
### v1.9.4(通用)
|
||||
|
||||
* 修復默認時間不顯示問題
|
||||
* 設置頁居中
|
||||
* 頻道號和時間對齊
|
||||
|
||||
### v1.9.2(通用)
|
||||
|
||||
* 修復一個重試的錯誤
|
||||
|
||||
### v1.9.0(通用)
|
||||
|
||||
* 減少視頻播放失敗情況
|
||||
* 全面屏手機居中顯示
|
||||
|
||||
### v1.8.8(通用)
|
||||
|
||||
* 樣式優化
|
||||
|
||||
### v1.8.6(通用)
|
||||
|
||||
* 增加錯誤顯示
|
||||
* 節目預告優化
|
||||
* 頻道列表頁單擊切換頻道
|
||||
|
||||
### v1.8.4(通用)
|
||||
|
||||
* 解決部分情況下打開後黑屏問題
|
||||
* 延長頻道信息顯示時間,增加信息顯示長度
|
||||
* 調整頻道列表間距
|
||||
* 其他樣式優化
|
||||
|
||||
### v1.8.2(通用)
|
||||
|
||||
* 修復無法播放問題
|
||||
|
||||
### v1.8.0(通用)
|
||||
|
||||
* 修復返回鍵無法退出問題
|
||||
* 設置中增加退出按鈕
|
||||
* 修改非電視上的樣式
|
||||
* 修復低版本閃退問題
|
||||
|
||||
### v1.7.8(通用)
|
||||
|
||||
* 修復播放過程中的卡頓問題
|
||||
* 增加時間顯示
|
||||
* 頻道號優化
|
||||
* 遙控器左鍵打開頻道列表
|
||||
* 遙控器右鍵打開設置菜單
|
||||
* 頻道列表空白處點擊隱藏頻道列表
|
||||
|
||||
### v1.7.6(通用)
|
||||
|
||||
* 臨時去掉部分頻道,提高頻道的可用性
|
||||
* 部分頻道增加節目單
|
||||
|
||||
### v1.7.5(安卓5及以上專用)
|
||||
|
||||
* 修復播放過程中的卡頓問題
|
||||
* 增加時間顯示
|
||||
* 頻道號優化
|
||||
* 設置中增加退出按鈕
|
||||
|
||||
### v1.7.4(通用)
|
||||
|
||||
* 修復368
|
||||
|
||||
### v1.7.3(安卓5及以上專用)
|
||||
|
||||
* 臨時去掉部分頻道,提高頻道的可用性
|
||||
* 部分頻道增加節目單
|
||||
* 遙控器左鍵進入節目列表
|
||||
* 遙控器右鍵進入菜單
|
||||
|
||||
### v1.7.2(通用)
|
||||
|
||||
* 支持節目列表網格樣式和行樣式切換,軟件重啟後生效
|
||||
* 節目列表樣式變更
|
||||
|
||||
### v1.7.1(安卓5及以上專用)
|
||||
|
||||
* 解決設置頁更新閃退的問題
|
||||
* 鳳凰衛視回歸
|
||||
* 解決368問題
|
||||
|
||||
### v1.7.0(通用)
|
||||
|
||||
* 網絡請求優化
|
||||
|
||||
### v1.6.9(安卓5及以上專用)
|
||||
|
||||
* 去掉港澳台和國際頻道
|
||||
* 解決部分情況下3、6、8等頻道無法播放的問題
|
||||
* 解決部分情況下啟動後黑屏問題
|
||||
|
||||
### v1.6.8(通用)
|
||||
|
||||
* 修復部分設備崩潰的問題
|
||||
* 修復部分設備鳳凰衛視無法播放的問題
|
||||
|
||||
### v1.6.7(安卓5及以上專用)
|
||||
|
||||
* 手機雙擊打開配置
|
||||
* 自動更新
|
||||
|
||||
### v1.6.6(通用)
|
||||
|
||||
* 更新重慶衛視圖標
|
||||
* 鳳凰衛視增強畫質
|
||||
* 鳳凰衛視增加EPG
|
||||
|
||||
### v1.6.5(安卓5及以上專用)
|
||||
|
||||
* 增加CETV1圖標
|
||||
* 穩定性提升
|
||||
|
||||
### v1.6.4(通用)
|
||||
|
||||
* 增加CETV1
|
||||
* 增加鳳凰衛視
|
||||
* 默認關閉開機啟動
|
||||
|
||||
### v1.6.3(安卓5及以上專用)
|
||||
|
||||
* 增加CETV1
|
||||
* 鳳凰衛視增強畫質
|
||||
* 默認關閉開機啟動
|
||||
* 延遲菜單自動關閉時間
|
||||
* 解決一些可能導致首次打開時黑屏的問題
|
||||
|
||||
### v1.6.2(通用)
|
||||
|
||||
* 修復按鍵無效的問題
|
||||
* 新的頻道列表樣式
|
||||
|
||||
### v1.6.1(安卓5及以上專用)
|
||||
|
||||
* 增加鳳凰衛視
|
||||
|
||||
### v1.6.0(通用)
|
||||
|
||||
* 通用(春晚緊急修復)
|
||||
|
||||
### v1.5.9(安卓5及以上專用)
|
||||
|
||||
* 解決天貓魔盒閃退問題
|
||||
|
||||
### v1.5.8(通用)
|
||||
|
||||
* 修復央視6畫質差的問題
|
||||
* 增加兵團衛視
|
||||
* 播放失敗重試
|
||||
|
||||
### v1.5.7(安卓5及以上專用)
|
||||
|
||||
* 修復播放失敗的問題
|
||||
|
||||
### v1.5.6(通用)
|
||||
|
||||
* 解決部分設備系統時間不對導致播放失敗的問題
|
||||
|
||||
### v1.5.5(安卓5及以上專用)
|
||||
|
||||
* 修復播放失敗的問題
|
||||
* 修復APP恢復後頻道號、頻道列表不自動消失的問題
|
||||
|
||||
### v1.5.4(通用)
|
||||
|
||||
* 修復播放失敗的問題
|
||||
|
||||
### v1.5.3(安卓5及以上專用)
|
||||
|
||||
* 修復部分情況下APP切換後無法繼續播放的問題
|
||||
* 優化重試邏輯
|
||||
|
||||
### v1.5.2(通用)
|
||||
|
||||
* 修復APP恢復後頻道號、頻道列表不自動消失的問題
|
||||
|
||||
### v1.5.1(安卓5及以上專用)
|
||||
|
||||
* 性能優化
|
||||
|
||||
### v1.5.0(通用)
|
||||
|
||||
* 修復部分情況下APP切換後無法繼續播放的問題
|
||||
|
||||
### v1.4.9(安卓5及以上專用)
|
||||
|
||||
* 同步v1.4.8
|
||||
|
||||
### v1.4.8(通用)
|
||||
|
||||
* 頻道號從1開始,CCTV5+為18
|
||||
* 提高CCTV6清晰度
|
||||
* 增加天津衛視、新疆衛視
|
||||
|
||||
### v1.4.7(安卓5及以上專用)
|
||||
|
||||
* 修復部分用戶CCTV13播放過程中卡住的問題
|
||||
* 調整CCTV的頻道順序
|
||||
|
||||
### v1.4.6(通用)
|
||||
|
||||
* 10以下頻道不再需要先按0
|
||||
|
||||
### v1.4.5(安卓5及以上專用)
|
||||
|
||||
* 數字選台配置
|
||||
|
||||
### v1.4.4(通用)
|
||||
|
||||
* 優化圖標顯示
|
||||
* 增加換台反轉
|
||||
|
||||
### v1.4.3(安卓5及以上專用)
|
||||
|
||||
* 支持頻道反轉配置
|
||||
|
||||
### v1.4.2(通用)
|
||||
|
||||
* 支持安卓4
|
||||
|
||||
### v1.4.1
|
||||
|
||||
* 解決部分用戶無法打開菜單的問題
|
||||
|
||||
### v1.4.0
|
||||
|
||||
* 解決極個別高版本機型黑屏問題
|
||||
|
||||
### v1.3.9
|
||||
|
||||
* 提高穩定性
|
||||
* 提高連接速度
|
||||
|
||||
### v1.3.4
|
||||
|
||||
* 部分錯誤會提示用戶
|
||||
* 菜單3秒鐘後自動關閉
|
||||
|
||||
### v1.3.3
|
||||
|
||||
* 部分錯誤會提示用戶
|
||||
* 菜單3秒鐘後自動關閉
|
||||
|
||||
### v1.3.2
|
||||
|
||||
* 增加重試,減少因網絡問題導致的播放失敗
|
||||
* 優化橫幅banner
|
||||
|
||||
### v1.3.1
|
||||
|
||||
* 增加CCTV 8K 超高清頻道
|
||||
* 增加國際頻道欄目
|
||||
|
||||
### v1.3.0
|
||||
|
||||
* 處理368可能失敗的情形
|
||||
|
||||
### v1.2.9
|
||||
|
||||
* 支持3、6、8
|
||||
|
||||
### v1.2.8
|
||||
|
||||
* 兼容16:10等分辨率
|
||||
|
||||
### v1.2.7
|
||||
|
||||
* 此版本是為了測試安卓6.0以下版本,如之前可以正常運行,請勿安裝。
|
||||
|
||||
### v1.2.6
|
||||
|
||||
* 支持安卓4.2
|
||||
* 解決部分頻道無法播放的問題
|
||||
* 修復切換時有時沒有恢復播放的問題
|
||||
* 左右鍵不再切換源
|
||||
* 增大頻道信息標題尺寸,縮小與節目信息的間隔
|
||||
|
||||
### v1.2.5
|
||||
|
||||
* 美化頻道信息顯示
|
||||
* 優化節目單獲取
|
||||
|
||||
### v1.2.4
|
||||
|
||||
* 改變換台滑動方向,上一個頻道下滑,下一個頻道上滑
|
||||
* 軟件退出時,退出播放器
|
||||
* 播放相同的頻道,不再重複加載
|
||||
* 暫時移除部分頻道
|
||||
|
||||
### v1.2.3
|
||||
|
||||
* 固定視頻比例為16:9
|
||||
* 隱藏全面屏底部的小橫條
|
||||
* 移除移動專區
|
||||
* 修復一個閃退問題
|
||||
* 更換圖標和應用名
|
||||
@@ -1,6 +1,6 @@
|
||||
.PHONY: all
|
||||
|
||||
all: info
|
||||
all: info gen-version
|
||||
|
||||
branch := $(shell git rev-parse --abbrev-ref HEAD)
|
||||
commit := $(shell git rev-parse --short HEAD)
|
||||
@@ -9,3 +9,11 @@ info:
|
||||
@echo 'SHELL='$(SHELL)
|
||||
@echo 'branch='$(branch)
|
||||
@echo 'commit='$(commit)
|
||||
|
||||
gen-version:
|
||||
git describe --tags --always
|
||||
git describe --tags --always | sed 's/v/ /g' | sed 's/\./ /g' | sed 's/-/ /g' | awk '{print ($$1*16777216)+($$2*65536)+($$3*256)+$$4}'
|
||||
|
||||
#make gen v=v2.0.0
|
||||
gen:
|
||||
echo $(v) | sed 's/v/ /g' | sed 's/\./ /g' | sed 's/-/ /g' | awk '{print "{\"version_code\": " ($$1*16777216)+($$2*65536)+($$3*256)+$$4 ", \"version_name\": \"" "v$(v)" "\"}"}' > version.json
|
||||
@@ -1,12 +1,19 @@
|
||||
# 我的电视
|
||||
|
||||
安卓电视直播软件,内置直播源
|
||||
电视直播软件,安装即可使用
|
||||
|
||||
## 使用
|
||||
|
||||
下载安装 [releases](https://github.com/lizongying/my-tv/releases/)
|
||||
|
||||
其他地址 [my-tv](https://lyrics.run/my-tv.html)
|
||||
1. 下载
|
||||
* [github](https://github.com/lizongying/my-tv/releases/)
|
||||
* [gitee](https://gitee.com/lizongying/my-tv/releases/)
|
||||
2. 安裝
|
||||
* U盘安装
|
||||
* 小米电视可以使用小米电视助手进行安装
|
||||
* 如电视可以启用ADB,也可以通过ADB进行安装
|
||||
```shell
|
||||
adb install my-tv.apk
|
||||
```
|
||||
|
||||

|
||||

|
||||
@@ -14,45 +21,28 @@
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v1.3.3
|
||||
[更新日志](./HISTORY.md)
|
||||
|
||||
* 部分错误会提示用户
|
||||
* 菜单3秒钟后自动关闭
|
||||
## TODO
|
||||
|
||||
### v1.3.2
|
||||
* 音量不同
|
||||
* 大湾区卫视、广东4k超高清、广东珠江、三沙卫视、翡翠
|
||||
* CHC高清三个电影频道
|
||||
* 地方频道
|
||||
* 收藏夹
|
||||
* 海外
|
||||
* 隐藏频道
|
||||
* 亮度调节
|
||||
* 音量调节
|
||||
* 軟解
|
||||
|
||||
* 增加重试,减少因网络问题导致的播放失败
|
||||
* 优化横幅banner
|
||||
## 版权说明
|
||||
|
||||
### v1.2.6
|
||||
本项目仅供学习研究,禁止用于商业用途,请于下载二十四小时内删除。
|
||||
|
||||
* 支持安卓4.2
|
||||
* 解决部分频道无法播放的问题
|
||||
* 修复切换时有时没有恢复播放的问题
|
||||
* 左右键不再切换源
|
||||
* 增大频道信息标题尺寸,缩小与节目信息的间隔
|
||||
本项目可能随时终止,请大家谨慎使用,建议使用官方渠道进行观看。
|
||||
|
||||
### v1.2.5
|
||||
|
||||
* 美化频道信息显示
|
||||
* 优化节目单获取
|
||||
|
||||
### v1.2.4
|
||||
|
||||
* 改变换台滑动方向,上一个频道下滑,下一个频道上滑
|
||||
* 软件退出时,退出播放器
|
||||
* 播放相同的频道,不再重复加载
|
||||
* 暂时移除部分频道
|
||||
|
||||
## 其他
|
||||
|
||||
小米电视可以使用小米电视助手进行安装
|
||||
|
||||
如电视可以启用ADB,也可以通过ADB进行安装:
|
||||
|
||||
```shell
|
||||
adb install my-tv.apk
|
||||
```
|
||||
本项目使用的部分代码、图片、文字等资源来源于网络,如有侵权,请联系删除。
|
||||
|
||||
## 赞赏
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
/CMakeFiles/
|
||||
/Makefile
|
||||
/src/main/cpp/native.c
|
||||
/src/main/cpp/aes.c
|
||||
|
||||
+3
-1
@@ -21,7 +21,9 @@ if (IS_SO_BUILD)
|
||||
SHARED
|
||||
|
||||
# Provides a relative path to your source file(s).
|
||||
src/main/cpp/native.c)
|
||||
src/main/cpp/native.c
|
||||
src/main/cpp/aes.c
|
||||
)
|
||||
|
||||
# 设置编译输出路径
|
||||
set_target_properties(
|
||||
|
||||
+37
-32
@@ -5,7 +5,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace 'com.lizongying.mytv'
|
||||
compileSdk 33
|
||||
compileSdk 34
|
||||
|
||||
viewBinding {
|
||||
enabled = true
|
||||
@@ -26,7 +26,8 @@ android {
|
||||
cmake {
|
||||
arguments "-DIS_SO_BUILD=${project.hasProperty('IS_SO_BUILD') ? project.IS_SO_BUILD : true}"
|
||||
|
||||
abiFilters "armeabi-v7a", "arm64-v8a"
|
||||
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
||||
// abiFilters "armeabi-v7a", "arm64-v8a"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +37,8 @@ android {
|
||||
ndk {
|
||||
// Specifies the ABI configurations of your native
|
||||
// libraries Gradle should build and package with your app.
|
||||
abiFilters "armeabi-v7a", "arm64-v8a"
|
||||
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
||||
// abiFilters "armeabi-v7a", "arm64-v8a"
|
||||
}
|
||||
multiDexEnabled true
|
||||
}
|
||||
@@ -54,7 +56,7 @@ android {
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget=17
|
||||
jvmTarget = 17
|
||||
}
|
||||
|
||||
// Encapsulates your external native build configurations.
|
||||
@@ -71,46 +73,49 @@ android {
|
||||
|
||||
static def VersionCode() {
|
||||
try {
|
||||
def process = 'git rev-list --count HEAD'.execute()
|
||||
def p = "git describe --tags --always"
|
||||
def process = p.execute()
|
||||
process.waitFor()
|
||||
return process.text.toInteger()
|
||||
def replace = [v: "", ".": " ", "-": " "]
|
||||
def arr = (process.text.trim().replace(replace) + " 0").split(" ")
|
||||
def versionCode = arr[0].toInteger() * 16777216 + arr[1].toInteger() * 65536 + arr[2].toInteger() * 256 + arr[3].toInteger()
|
||||
return versionCode
|
||||
} catch (ignored) {
|
||||
return 0
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
static def VersionName() {
|
||||
try {
|
||||
def process = 'git describe --tags --always'.execute()
|
||||
def process = "git describe --tags --always".execute()
|
||||
process.waitFor()
|
||||
return process.text.trim() - "v"
|
||||
def versionName = process.text.trim() - "v"
|
||||
if (versionName.isEmpty()) {
|
||||
versionName = "1.0.0"
|
||||
}
|
||||
return versionName
|
||||
} catch (ignored) {
|
||||
return "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def media3_version = "1.1.1"
|
||||
|
||||
implementation "androidx.media3:media3-ui:$media3_version"
|
||||
|
||||
// For media playback using ExoPlayer
|
||||
implementation "androidx.media3:media3-exoplayer:$media3_version"
|
||||
|
||||
// For HLS playback support with ExoPlayer
|
||||
implementation "androidx.media3:media3-exoplayer-hls:$media3_version"
|
||||
|
||||
// 21:2.9.0 17:2.6.4
|
||||
def retrofit2_version = "2.6.4"
|
||||
|
||||
implementation 'com.google.protobuf:protobuf-kotlin:3.25.1'
|
||||
implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version"
|
||||
implementation "com.squareup.retrofit2:converter-protobuf:$retrofit2_version"
|
||||
implementation "com.squareup.retrofit2:retrofit:$retrofit2_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2"
|
||||
implementation 'androidx.core:core-ktx:1.11.0-beta02'
|
||||
implementation 'androidx.leanback:leanback:1.2.0-alpha02'
|
||||
implementation 'com.github.bumptech.glide:glide:4.11.0'
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC")
|
||||
implementation(libs.media3.ui)
|
||||
implementation(libs.media3.exoplayer)
|
||||
implementation(libs.media3.exoplayer.hls)
|
||||
implementation(libs.protobuf.kotlin)
|
||||
implementation(libs.gson)
|
||||
implementation(libs.converter.gson)
|
||||
implementation(libs.converter.protobuf)
|
||||
implementation(libs.retrofit)
|
||||
implementation(libs.glide)
|
||||
implementation(libs.work.runtime.ktx)
|
||||
implementation(libs.core.ktx)
|
||||
implementation(libs.multidex)
|
||||
implementation(libs.leanback)
|
||||
implementation(libs.lifecycle.runtime.ktx)
|
||||
implementation(libs.constraintlayout)
|
||||
implementation(libs.serialization.json)
|
||||
implementation(libs.coroutines.android)
|
||||
implementation(libs.javax.annotation.api)
|
||||
}
|
||||
@@ -1,31 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.software.leanback"
|
||||
android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:name=".MyTVApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:banner="@drawable/banner"
|
||||
android:icon="@drawable/logo"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:logo="@drawable/logo"
|
||||
android:networkSecurityConfig="@xml/network"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MyTV">
|
||||
android:theme="@style/Theme.MyTV"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:keepScreenOn="true"
|
||||
android:name=".MainActivity"
|
||||
android:banner="@drawable/banner"
|
||||
android:exported="true"
|
||||
android:icon="@drawable/logo"
|
||||
android:logo="@drawable/logo"
|
||||
android:keepScreenOn="true"
|
||||
android:screenOrientation="landscape">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.windowLayoutInDisplayCutoutMode"
|
||||
android:value="shortEdges" />
|
||||
</activity>
|
||||
|
||||
<receiver
|
||||
android:name=".BootReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".NetworkChangeReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<provider
|
||||
android:name=".InitializerProvider"
|
||||
android:authorities="${applicationId}.InitializerProvider"
|
||||
android:exported="false" />
|
||||
|
||||
<meta-data android:name="android.max_aspect" android:value="2.5" />
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
#ifndef AES_H
|
||||
#define AES_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void init();
|
||||
void encode(const char *in, size_t size, char *out);
|
||||
|
||||
#endif
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,18 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (SP.bootStartup) {
|
||||
context.startActivity(
|
||||
Intent(context, MainActivity::class.java)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +1,74 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.graphics.Color
|
||||
import android.util.Log
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.ScaleAnimation
|
||||
import android.widget.ImageView
|
||||
import androidx.leanback.widget.ImageCardView
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.lizongying.mytv.databinding.CardBinding
|
||||
import com.lizongying.mytv.models.TVListViewModel
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
class CardAdapter(private val owner: LifecycleOwner, private var tvListViewModel: TVListViewModel) :
|
||||
class CardAdapter(
|
||||
private val recyclerView: RecyclerView,
|
||||
private val mainFragment: MainFragment,
|
||||
private var tvListViewModel: TVListViewModel,
|
||||
) :
|
||||
RecyclerView.Adapter<CardAdapter.ViewHolder>() {
|
||||
|
||||
private var listener: ItemListener? = null
|
||||
private var focused: View? = null
|
||||
|
||||
var focusable = false
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val cardView = object :
|
||||
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {}
|
||||
startScaleAnimation(cardView, 1.0f, 0.9f)
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val binding = CardBinding.inflate(inflater, parent, false)
|
||||
|
||||
val application = mainFragment.requireActivity().applicationContext as MyTVApplication
|
||||
|
||||
binding.card.layoutParams.width = application.px2Px(binding.card.layoutParams.width)
|
||||
binding.card.layoutParams.height = application.px2Px(binding.card.layoutParams.height)
|
||||
binding.icon.layoutParams.height = application.px2Px(binding.icon.layoutParams.height)
|
||||
binding.icon.setPadding(application.px2Px(binding.icon.paddingTop))
|
||||
binding.main.layoutParams.height = application.px2Px(binding.main.layoutParams.height)
|
||||
binding.main.setPadding(application.px2Px(binding.main.paddingTop))
|
||||
binding.title.textSize = application.px2PxFont(binding.title.textSize)
|
||||
binding.desc.textSize = application.px2PxFont(binding.desc.textSize)
|
||||
val layoutParams = binding.desc.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.topMargin = application.px2Px(binding.desc.marginTop)
|
||||
binding.desc.layoutParams = layoutParams
|
||||
|
||||
val cardView = binding.root
|
||||
cardView.isClickable = true
|
||||
cardView.isFocusable = true
|
||||
cardView.isFocusableInTouchMode = true
|
||||
|
||||
return ViewHolder(cardView)
|
||||
return ViewHolder(cardView, binding)
|
||||
}
|
||||
|
||||
private fun startScaleAnimation(view: View, fromScale: Float, toScale: Float) {
|
||||
fun clear() {
|
||||
focused?.clearFocus()
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
|
||||
private fun startScaleAnimation(view: View) {
|
||||
val scaleAnimation = ScaleAnimation(
|
||||
fromScale, toScale,
|
||||
fromScale, toScale,
|
||||
0.9f, 1.0f,
|
||||
0.9f, 1.0f,
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f
|
||||
)
|
||||
scaleAnimation.duration = 200
|
||||
scaleAnimation.fillAfter = true
|
||||
|
||||
scaleAnimation.fillAfter = false
|
||||
view.startAnimation(scaleAnimation)
|
||||
}
|
||||
|
||||
@@ -47,58 +76,134 @@ class CardAdapter(private val owner: LifecycleOwner, private var tvListViewModel
|
||||
val item = tvListViewModel.getTVViewModel(position)
|
||||
|
||||
val tvViewModel = item as TVViewModel
|
||||
val cardView = viewHolder.view as ImageCardView
|
||||
val cardView = viewHolder.view
|
||||
cardView.tag = tvViewModel
|
||||
|
||||
cardView.setOnFocusChangeListener { _, hasFocus ->
|
||||
listener?.onItemFocusChange(item, hasFocus)
|
||||
val onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus ->
|
||||
if (hasFocus) {
|
||||
startScaleAnimation(cardView, 0.9f, 1.0f)
|
||||
listener?.onItemHasFocus(item)
|
||||
focused = cardView
|
||||
|
||||
if (focusable) {
|
||||
startScaleAnimation(cardView)
|
||||
viewHolder.focus(true)
|
||||
}
|
||||
} else {
|
||||
startScaleAnimation(cardView, 1.0f, 0.9f)
|
||||
viewHolder.focus(false)
|
||||
}
|
||||
}
|
||||
|
||||
cardView.onFocusChangeListener = onFocusChangeListener
|
||||
|
||||
cardView.setOnClickListener { _ ->
|
||||
listener?.onItemClicked(item)
|
||||
}
|
||||
|
||||
cardView.titleText = tvViewModel.title.value
|
||||
cardView.tag = tvViewModel.videoUrl.value
|
||||
var downX = 0f
|
||||
var downY = 0f
|
||||
val touchSlop = ViewConfiguration.get(cardView.context).scaledTouchSlop
|
||||
|
||||
if (tvViewModel.logo.value != null) {
|
||||
if (tvViewModel.title.value == "CCTV8K 超高清") {
|
||||
Glide.with(viewHolder.view.context)
|
||||
.load(R.drawable.cctv8k)
|
||||
.centerInside()
|
||||
.into(cardView.mainImageView)
|
||||
} else {
|
||||
Glide.with(viewHolder.view.context)
|
||||
.load(tvViewModel.logo.value)
|
||||
.centerInside()
|
||||
.into(cardView.mainImageView)
|
||||
cardView.setOnTouchListener { _, event ->
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
downX = event.x
|
||||
downY = event.y
|
||||
true
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
val upX = event.x
|
||||
val upY = event.y
|
||||
val deltaX = abs(upX - downX)
|
||||
val deltaY = abs(upY - downY)
|
||||
if (deltaX < touchSlop && deltaY < touchSlop) {
|
||||
// 如果位移量小于阈值,则执行点击操作
|
||||
cardView.performClick()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
||||
cardView.mainImageView.setBackgroundColor(Color.WHITE)
|
||||
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
|
||||
}
|
||||
|
||||
tvViewModel.program.observe(owner) { _ ->
|
||||
val program = tvViewModel.getProgramOne()
|
||||
if (program != null) {
|
||||
cardView.contentText = program.name
|
||||
cardView.setOnKeyListener { _, keyCode, event: KeyEvent? ->
|
||||
if (event?.action == KeyEvent.ACTION_DOWN) {
|
||||
return@setOnKeyListener listener?.onKey(keyCode) ?: false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
viewHolder.binding.title.text = tvViewModel.getTV().title
|
||||
|
||||
viewHolder.binding.icon.let {
|
||||
Glide.with(viewHolder.view.context)
|
||||
.load(tvViewModel.getTV().logo)
|
||||
.centerInside()
|
||||
.into(it)
|
||||
}
|
||||
|
||||
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
|
||||
if (!epg.isNullOrEmpty()) {
|
||||
viewHolder.binding.desc.text = epg.last().title
|
||||
} else {
|
||||
viewHolder.binding.desc.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = tvListViewModel.size()
|
||||
|
||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
class ViewHolder(itemView: View, var binding: CardBinding) : RecyclerView.ViewHolder(itemView) {
|
||||
val view = itemView
|
||||
|
||||
fun focus(hasFocus: Boolean) {
|
||||
if (hasFocus) {
|
||||
binding.main.setBackgroundResource(R.drawable.rounded_light_bottom)
|
||||
} else {
|
||||
binding.main.setBackgroundResource(R.drawable.rounded_dark_bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateEPG() {
|
||||
for (i in 0 until recyclerView.childCount) {
|
||||
val childView = recyclerView.getChildAt(i)
|
||||
|
||||
val viewHolder = recyclerView.getChildViewHolder(childView) as ViewHolder
|
||||
|
||||
if (viewHolder.view.tag != null && viewHolder.view.tag is TVViewModel) {
|
||||
val tvViewModel = viewHolder.view.tag as TVViewModel
|
||||
|
||||
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
|
||||
|
||||
if (!epg.isNullOrEmpty()) {
|
||||
val title = epg.last().title
|
||||
if (viewHolder.binding.desc.text != title) {
|
||||
viewHolder.binding.desc.text = title
|
||||
Log.i(TAG, "updateEPG $title")
|
||||
}
|
||||
} else {
|
||||
if (viewHolder.binding.desc.text != "") {
|
||||
viewHolder.binding.desc.text = ""
|
||||
Log.i(TAG, "updateEPG")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun toPosition(position: Int) {
|
||||
recyclerView.post {
|
||||
recyclerView.scrollToPosition(position)
|
||||
recyclerView.getChildAt(position)?.isSelected
|
||||
recyclerView.getChildAt(position)?.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
interface ItemListener {
|
||||
fun onItemFocusChange(tvViewModel: TVViewModel, hasFocus: Boolean)
|
||||
fun onItemHasFocus(tvViewModel: TVViewModel)
|
||||
fun onItemClicked(tvViewModel: TVViewModel)
|
||||
fun onKey(keyCode: Int): Boolean
|
||||
}
|
||||
|
||||
fun setItemListener(listener: ItemListener) {
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.leanback.widget.ImageCardView
|
||||
import androidx.leanback.widget.Presenter
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.bumptech.glide.Glide
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
|
||||
class CardPresenter(
|
||||
private val owner: LifecycleOwner,
|
||||
) : Presenter() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
|
||||
val cardView = object :
|
||||
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {}
|
||||
|
||||
cardView.isFocusable = true
|
||||
cardView.isFocusableInTouchMode = true
|
||||
return ViewHolder(cardView)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) {
|
||||
val tvViewModel = item as TVViewModel
|
||||
val cardView = viewHolder.view as ImageCardView
|
||||
|
||||
cardView.titleText = tvViewModel.title.value
|
||||
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
|
||||
cardView.tag = tvViewModel.videoUrl.value
|
||||
|
||||
if (tvViewModel.logo.value != null) {
|
||||
if (tvViewModel.title.value == "CCTV8K 超高清") {
|
||||
Glide.with(viewHolder.view.context)
|
||||
.load(R.drawable.cctv8k)
|
||||
.centerInside()
|
||||
.into(cardView.mainImageView)
|
||||
} else {
|
||||
Glide.with(viewHolder.view.context)
|
||||
.load(tvViewModel.logo.value)
|
||||
.centerInside()
|
||||
.into(cardView.mainImageView)
|
||||
}
|
||||
|
||||
cardView.setBackgroundColor(Color.WHITE)
|
||||
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
|
||||
}
|
||||
|
||||
tvViewModel.program.observe(owner) { _ ->
|
||||
val program = tvViewModel.getProgramOne()
|
||||
if (program != null) {
|
||||
cardView.contentText = program.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUnbindViewHolder(viewHolder: ViewHolder) {
|
||||
val cardView = viewHolder.view as ImageCardView
|
||||
cardView.mainImage = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CardPresenter"
|
||||
private const val CARD_WIDTH = 300
|
||||
private const val CARD_HEIGHT = 101
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.marginEnd
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.lizongying.mytv.databinding.ChannelBinding
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
|
||||
class ChannelFragment : Fragment() {
|
||||
private var _binding: ChannelBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val handler = Handler()
|
||||
private val delay: Long = 5000
|
||||
private var channel = 0
|
||||
private var channelCount = 0
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = ChannelBinding.inflate(inflater, container, false)
|
||||
_binding!!.root.visibility = View.GONE
|
||||
|
||||
val application = requireActivity().applicationContext as MyTVApplication
|
||||
|
||||
binding.channel.layoutParams.width = application.px2Px(binding.channel.layoutParams.width)
|
||||
binding.channel.layoutParams.height = application.px2Px(binding.channel.layoutParams.height)
|
||||
|
||||
val layoutParams = binding.channel.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.topMargin = application.px2Px(binding.channel.marginTop)
|
||||
layoutParams.marginEnd = application.px2Px(binding.channel.marginEnd)
|
||||
binding.channel.layoutParams = layoutParams
|
||||
|
||||
binding.content.textSize = application.px2PxFont(binding.content.textSize)
|
||||
binding.time.textSize = application.px2PxFont(binding.time.textSize)
|
||||
|
||||
binding.main.layoutParams.width = application.shouldWidthPx()
|
||||
binding.main.layoutParams.height = application.shouldHeightPx()
|
||||
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
fun show(tvViewModel: TVViewModel) {
|
||||
handler.removeCallbacks(hideRunnable)
|
||||
handler.removeCallbacks(playRunnable)
|
||||
binding.content.text = (tvViewModel.getTV().id.plus(1)).toString()
|
||||
view?.visibility = View.VISIBLE
|
||||
handler.postDelayed(hideRunnable, delay)
|
||||
}
|
||||
|
||||
fun show(channel: String) {
|
||||
if (channelCount > 1) {
|
||||
return
|
||||
}
|
||||
channelCount++
|
||||
this.channel = "${this.channel}$channel".toInt()
|
||||
handler.removeCallbacks(hideRunnable)
|
||||
handler.removeCallbacks(playRunnable)
|
||||
if (channelCount < 2) {
|
||||
binding.content.text = "${this.channel}"
|
||||
view?.visibility = View.VISIBLE
|
||||
handler.postDelayed(playRunnable, delay)
|
||||
} else {
|
||||
handler.postDelayed(playRunnable, 0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (view?.visibility == View.VISIBLE) {
|
||||
handler.postDelayed(hideRunnable, delay)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
handler.removeCallbacks(hideRunnable)
|
||||
handler.removeCallbacks(playRunnable)
|
||||
}
|
||||
|
||||
private val hideRunnable = Runnable {
|
||||
binding.content.text = ""
|
||||
view?.visibility = View.GONE
|
||||
channel = 0
|
||||
channelCount = 0
|
||||
}
|
||||
|
||||
private val playRunnable = Runnable {
|
||||
(activity as MainActivity).play(channel - 1)
|
||||
binding.content.text = ""
|
||||
view?.visibility = View.GONE
|
||||
channel = 0
|
||||
channelCount = 0
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ChannelFragment"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.lizongying.mytv.databinding.ConfirmationBinding
|
||||
|
||||
class ConfirmationFragment(
|
||||
private val listener: ConfirmationListener,
|
||||
private val message: String,
|
||||
private val update: Boolean
|
||||
) : DialogFragment() {
|
||||
//
|
||||
// private var _binding: ConfirmationBinding? = null
|
||||
// private val binding get() = _binding!!
|
||||
//
|
||||
// override fun onCreateView(
|
||||
// inflater: LayoutInflater, container: ViewGroup?,
|
||||
// savedInstanceState: Bundle?
|
||||
// ): View {
|
||||
// _binding = ConfirmationBinding.inflate(inflater, container, false)
|
||||
// return binding.root
|
||||
// }
|
||||
//
|
||||
// fun updateProgress(progress: Int) {
|
||||
// binding.progressBar.progress = progress
|
||||
// }
|
||||
//
|
||||
// override fun onDestroyView() {
|
||||
// super.onDestroyView()
|
||||
// _binding = null
|
||||
// }
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
val builder = AlertDialog.Builder(it)
|
||||
builder.setTitle(message)
|
||||
if (update) {
|
||||
builder.setMessage("确定更新吗?")
|
||||
.setPositiveButton(
|
||||
"确定"
|
||||
) { _, _ ->
|
||||
listener.onConfirm()
|
||||
}
|
||||
.setNegativeButton(
|
||||
"取消"
|
||||
) { _, _ ->
|
||||
listener.onCancel()
|
||||
}
|
||||
} else {
|
||||
builder.setMessage("")
|
||||
.setNegativeButton(
|
||||
"好的"
|
||||
) { _, _ ->
|
||||
}
|
||||
}
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
|
||||
interface ConfirmationListener {
|
||||
fun onConfirm()
|
||||
fun onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.marginStart
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.lizongying.mytv.databinding.ErrorBinding
|
||||
|
||||
class ErrorFragment : Fragment() {
|
||||
private var _binding: ErrorBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = ErrorBinding.inflate(inflater, container, false)
|
||||
|
||||
val application = requireActivity().applicationContext as MyTVApplication
|
||||
|
||||
binding.logo.layoutParams.width = application.px2Px(binding.logo.layoutParams.width)
|
||||
binding.logo.layoutParams.height = application.px2Px(binding.logo.layoutParams.height)
|
||||
|
||||
val layoutParams = binding.msg.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.topMargin = application.px2Px(binding.msg.marginTop)
|
||||
binding.msg.layoutParams = layoutParams
|
||||
|
||||
binding.msg.textSize = application.px2PxFont(binding.msg.textSize)
|
||||
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
fun show(msg: String) {
|
||||
binding.msg.text = msg
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ErrorFragment"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.Signature
|
||||
import android.content.pm.SigningInfo
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import java.security.MessageDigest
|
||||
|
||||
private const val TAG = "Extensions"
|
||||
|
||||
private val Context.packageInfo: PackageInfo
|
||||
get() {
|
||||
val flag = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
PackageManager.GET_SIGNATURES
|
||||
} else {
|
||||
PackageManager.GET_SIGNING_CERTIFICATES
|
||||
}
|
||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
packageManager.getPackageInfo(packageName, flag)
|
||||
} else {
|
||||
packageManager.getPackageInfo(
|
||||
packageName,
|
||||
PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version code of the app which is defined in build.gradle.
|
||||
* eg:100
|
||||
*/
|
||||
val Context.appVersionCode: Long
|
||||
get() {
|
||||
val packageInfo = this.packageInfo
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
packageInfo.longVersionCode
|
||||
} else {
|
||||
packageInfo.versionCode.toLong()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the version name of the app which is defined in build.gradle.
|
||||
* eg:1.0.0
|
||||
*/
|
||||
val Context.appVersionName: String get() = packageInfo.versionName
|
||||
|
||||
val Context.appSignature: String
|
||||
get() {
|
||||
val packageInfo = this.packageInfo
|
||||
var sign: Signature? = null
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
val signatures: Array<out Signature>? = packageInfo.signatures
|
||||
if (signatures != null) {
|
||||
sign = signatures[0]
|
||||
}
|
||||
} else {
|
||||
val signingInfo: SigningInfo? = packageInfo.signingInfo
|
||||
if (signingInfo != null) {
|
||||
sign = signingInfo.apkContentsSigners[0]
|
||||
}
|
||||
}
|
||||
if (sign == null) {
|
||||
return ""
|
||||
}
|
||||
return hashSignature(sign)
|
||||
}
|
||||
|
||||
|
||||
val Context.isNetworkConnected: Boolean
|
||||
get() {
|
||||
val connectivityManager =
|
||||
this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val network = connectivityManager.activeNetwork
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
|
||||
return networkCapabilities != null &&
|
||||
(networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
|
||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET))
|
||||
} else {
|
||||
val networkInfo = connectivityManager.activeNetworkInfo
|
||||
return networkInfo != null && networkInfo.isConnectedOrConnecting
|
||||
}
|
||||
}
|
||||
|
||||
private fun hashSignature(signature: Signature): String {
|
||||
return try {
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
md.update(signature.toByteArray())
|
||||
val digest = md.digest()
|
||||
digest.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error hashing signature", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.marginBottom
|
||||
import androidx.core.view.marginStart
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.bumptech.glide.Glide
|
||||
import com.lizongying.mytv.databinding.InfoBinding
|
||||
@@ -16,39 +21,64 @@ class InfoFragment : Fragment() {
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val handler = Handler()
|
||||
private val delay: Long = 3000
|
||||
private val delay: Long = 5000
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = InfoBinding.inflate(inflater, container, false)
|
||||
(activity as MainActivity).fragmentReady()
|
||||
|
||||
val application = requireActivity().applicationContext as MyTVApplication
|
||||
|
||||
binding.info.layoutParams.width = application.px2Px(binding.info.layoutParams.width)
|
||||
binding.info.layoutParams.height = application.px2Px(binding.info.layoutParams.height)
|
||||
|
||||
val layoutParams = binding.info.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.bottomMargin = application.px2Px(binding.info.marginBottom)
|
||||
binding.info.layoutParams = layoutParams
|
||||
|
||||
binding.logo.layoutParams.width = application.px2Px(binding.logo.layoutParams.width)
|
||||
binding.logo.setPadding(application.px2Px(binding.logo.paddingTop))
|
||||
binding.main.layoutParams.width = application.px2Px(binding.main.layoutParams.width)
|
||||
binding.main.setPadding(application.px2Px(binding.main.paddingTop))
|
||||
|
||||
val layoutParamsMain = binding.main.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsMain.marginStart = application.px2Px(binding.main.marginStart)
|
||||
binding.main.layoutParams = layoutParamsMain
|
||||
|
||||
val layoutParamsDesc = binding.desc.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsDesc.topMargin = application.px2Px(binding.desc.marginTop)
|
||||
binding.desc.layoutParams = layoutParamsDesc
|
||||
|
||||
binding.title.textSize = application.px2PxFont(binding.title.textSize)
|
||||
binding.desc.textSize = application.px2PxFont(binding.desc.textSize)
|
||||
|
||||
binding.container.layoutParams.width = application.shouldWidthPx()
|
||||
binding.container.layoutParams.height = application.shouldHeightPx()
|
||||
|
||||
_binding!!.root.visibility = View.GONE
|
||||
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
// Use a Handler to delay the fragment transaction
|
||||
handler.postDelayed(removeRunnable, delay)
|
||||
}
|
||||
|
||||
fun show(tvViewModel: TVViewModel) {
|
||||
binding.textView.text = tvViewModel.title.value
|
||||
if (tvViewModel.title.value == "CCTV8K 超高清") {
|
||||
Glide.with(this)
|
||||
.load(R.drawable.cctv8k)
|
||||
.into(binding.infoLogo)
|
||||
} else {
|
||||
Glide.with(this)
|
||||
.load(tvViewModel.logo.value)
|
||||
.into(binding.infoLogo)
|
||||
if (_binding == null) {
|
||||
return
|
||||
}
|
||||
binding.title.text = tvViewModel.getTV().title
|
||||
|
||||
val program = tvViewModel.getProgramOne()
|
||||
if (program != null) {
|
||||
binding.infoDesc.text = program.name
|
||||
Glide.with(this)
|
||||
.load(tvViewModel.getTV().logo)
|
||||
.into(binding.logo)
|
||||
|
||||
Log.i(TAG, "${tvViewModel.getTV().title} ${tvViewModel.epg.value}")
|
||||
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
|
||||
if (!epg.isNullOrEmpty()) {
|
||||
binding.desc.text = epg.last().title
|
||||
} else {
|
||||
binding.desc.text = ""
|
||||
}
|
||||
|
||||
handler.removeCallbacks(removeRunnable)
|
||||
@@ -56,10 +86,13 @@ class InfoFragment : Fragment() {
|
||||
handler.postDelayed(removeRunnable, delay)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
handler.postDelayed(removeRunnable, delay)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
Log.i(TAG, "onPause")
|
||||
// Cancel the delayed task when the fragment is paused
|
||||
handler.removeCallbacks(removeRunnable)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.content.ContentValues
|
||||
import android.net.Uri
|
||||
|
||||
internal class InitializerProvider : ContentProvider() {
|
||||
|
||||
// Happens before Application#onCreate.It's fine to init something here
|
||||
override fun onCreate(): Boolean {
|
||||
SP.init(context!!)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun query(
|
||||
uri: Uri,
|
||||
projection: Array<out String>?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<out String>?,
|
||||
sortOrder: String?,
|
||||
) = unsupported()
|
||||
|
||||
override fun getType(uri: Uri) = unsupported()
|
||||
|
||||
override fun insert(uri: Uri, values: ContentValues?) = unsupported()
|
||||
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) =
|
||||
unsupported()
|
||||
|
||||
override fun update(
|
||||
uri: Uri,
|
||||
values: ContentValues?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<out String>?,
|
||||
) = unsupported()
|
||||
|
||||
private fun unsupported(errorMessage: String? = null): Nothing =
|
||||
throw UnsupportedOperationException(errorMessage)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class ItemDecoration(context: Context) : RecyclerView.ItemDecoration() {
|
||||
|
||||
private var application: MyTVApplication
|
||||
|
||||
init {
|
||||
application = context.applicationContext as MyTVApplication
|
||||
}
|
||||
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
super.getItemOffsets(outRect, view, parent, state)
|
||||
val space = application.dp2Px(5)
|
||||
outRect.top = space
|
||||
outRect.bottom = space
|
||||
outRect.left = space
|
||||
outRect.right = space
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.lizongying.mytv.databinding.LoadingBinding
|
||||
|
||||
class LoadingFragment : Fragment() {
|
||||
private var _binding: LoadingBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = LoadingBinding.inflate(inflater, container, false)
|
||||
|
||||
val application = requireActivity().applicationContext as MyTVApplication
|
||||
|
||||
binding.bar.layoutParams.width = application.px2Px(binding.bar.layoutParams.width)
|
||||
binding.bar.layoutParams.height = application.px2Px(binding.bar.layoutParams.height)
|
||||
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "LoadingFragment"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,5 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.Signature
|
||||
import android.content.pm.SigningInfo
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
@@ -14,59 +8,210 @@ import android.util.Log
|
||||
import android.view.GestureDetector
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
import android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
import android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
import android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
import java.security.MessageDigest
|
||||
import com.lizongying.mytv.requests.Request
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class MainActivity : FragmentActivity() {
|
||||
class MainActivity : FragmentActivity(), Request.RequestListener, OnSharedPreferenceChangeListener {
|
||||
|
||||
var playerFragment = PlayerFragment()
|
||||
private val mainFragment = MainFragment2()
|
||||
private var ready = 0
|
||||
private val playerFragment = PlayerFragment()
|
||||
private val errorFragment = ErrorFragment()
|
||||
|
||||
private val loadingFragment = LoadingFragment()
|
||||
private val mainFragment = MainFragment()
|
||||
private val infoFragment = InfoFragment()
|
||||
private val channelFragment = ChannelFragment()
|
||||
private var timeFragment = TimeFragment()
|
||||
private val settingFragment = SettingFragment()
|
||||
|
||||
private var doubleBackToExitPressedOnce = false
|
||||
|
||||
private lateinit var gestureDetector: GestureDetector
|
||||
|
||||
private val handler = Handler()
|
||||
private val delay: Long = 4000
|
||||
private val delayHideMain: Long = 10000
|
||||
private val delayHideSetting: Long = 15000
|
||||
|
||||
init {
|
||||
Utils.setRequestListener(this)
|
||||
}
|
||||
|
||||
fun syncTime() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() }
|
||||
utilsJob.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
val lp = window.attributes
|
||||
lp.layoutInDisplayCutoutMode =
|
||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||
window.setAttributes(lp)
|
||||
}
|
||||
|
||||
|
||||
window.decorView.apply {
|
||||
systemUiVisibility =
|
||||
SYSTEM_UI_FLAG_FULLSCREEN or
|
||||
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
|
||||
SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
||||
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE
|
||||
}
|
||||
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
window.decorView.systemUiVisibility = SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
window.decorView.systemUiVisibility =
|
||||
SYSTEM_UI_FLAG_FULLSCREEN or
|
||||
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
||||
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
// window.statusBarColor = Color.TRANSPARENT
|
||||
// window.navigationBarColor = Color.TRANSPARENT
|
||||
// }
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
Request.setRequestListener(this)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
val transaction = supportFragmentManager.beginTransaction()
|
||||
.add(R.id.main_browse_fragment, playerFragment)
|
||||
.add(R.id.main_browse_fragment, errorFragment)
|
||||
// .add(R.id.main_browse_fragment, loadingFragment)
|
||||
.add(R.id.main_browse_fragment, timeFragment)
|
||||
.add(R.id.main_browse_fragment, infoFragment)
|
||||
.add(R.id.main_browse_fragment, channelFragment)
|
||||
.add(R.id.main_browse_fragment, mainFragment)
|
||||
.hide(infoFragment)
|
||||
.hide(mainFragment)
|
||||
.commit()
|
||||
mainFragment.view?.requestFocus()
|
||||
.hide(errorFragment)
|
||||
|
||||
if (!SP.time) {
|
||||
transaction.hide(timeFragment)
|
||||
} else {
|
||||
transaction.show(timeFragment)
|
||||
}
|
||||
|
||||
transaction.commitNow()
|
||||
}
|
||||
gestureDetector = GestureDetector(this, GestureListener())
|
||||
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
// val connectivityManager =
|
||||
// getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
// connectivityManager.registerDefaultNetworkCallback(object :
|
||||
// ConnectivityManager.NetworkCallback() {
|
||||
// override fun onAvailable(network: Network) {
|
||||
// super.onAvailable(network)
|
||||
// Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
|
||||
// if (this@MainActivity.isNetworkConnected) {
|
||||
// Log.i(TAG, "net isNetworkConnected")
|
||||
// ready++
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
// Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
|
||||
// ready++
|
||||
// }
|
||||
|
||||
SP.setOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
fun showInfoFragment(tvViewModel: TVViewModel) {
|
||||
infoFragment.show(tvViewModel)
|
||||
if (SP.channelNum) {
|
||||
channelFragment.show(tvViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun showErrorFragment(msg: String) {
|
||||
errorFragment.show(msg)
|
||||
if (errorFragment.isVisible) {
|
||||
return
|
||||
}
|
||||
supportFragmentManager.beginTransaction()
|
||||
.show(errorFragment)
|
||||
.commit()
|
||||
}
|
||||
|
||||
fun hideErrorFragment() {
|
||||
errorFragment.show("")
|
||||
if (!errorFragment.isVisible) {
|
||||
return
|
||||
}
|
||||
supportFragmentManager.beginTransaction()
|
||||
.hide(errorFragment)
|
||||
.commit()
|
||||
}
|
||||
|
||||
fun showLoadingFragment() {
|
||||
showFragment(loadingFragment)
|
||||
}
|
||||
|
||||
fun hideLoadingFragment() {
|
||||
hideFragment(loadingFragment)
|
||||
}
|
||||
|
||||
fun showPlayerFragment() {
|
||||
if (playerFragment.isVisible) {
|
||||
return
|
||||
}
|
||||
supportFragmentManager.beginTransaction()
|
||||
.show(playerFragment)
|
||||
.commit()
|
||||
}
|
||||
|
||||
fun hidePlayerFragment() {
|
||||
if (!playerFragment.isVisible) {
|
||||
return
|
||||
}
|
||||
supportFragmentManager.beginTransaction()
|
||||
.hide(playerFragment)
|
||||
.commit()
|
||||
}
|
||||
|
||||
private fun showChannel(channel: String) {
|
||||
if (!mainFragment.isHidden) {
|
||||
return
|
||||
}
|
||||
|
||||
if (settingFragment.isVisible) {
|
||||
return
|
||||
}
|
||||
|
||||
if (SP.channelNum) {
|
||||
channelFragment.show(channel)
|
||||
}
|
||||
}
|
||||
|
||||
fun play(tvViewModel: TVViewModel) {
|
||||
playerFragment.play(tvViewModel)
|
||||
mainFragment.view?.requestFocus()
|
||||
}
|
||||
|
||||
fun play(itemPosition: Int) {
|
||||
mainFragment.play(itemPosition)
|
||||
}
|
||||
|
||||
fun prev() {
|
||||
@@ -77,21 +222,13 @@ class MainActivity : FragmentActivity() {
|
||||
mainFragment.next()
|
||||
}
|
||||
|
||||
private fun prevSource() {
|
||||
// mainFragment.prevSource()
|
||||
}
|
||||
|
||||
private fun nextSource() {
|
||||
// mainFragment.nextSource()
|
||||
}
|
||||
|
||||
fun switchMainFragment() {
|
||||
val transaction = supportFragmentManager.beginTransaction()
|
||||
|
||||
if (mainFragment.isHidden) {
|
||||
mainFragment.setPosition()
|
||||
transaction.show(mainFragment)
|
||||
keepRunnable()
|
||||
mainActive()
|
||||
} else {
|
||||
transaction.hide(mainFragment)
|
||||
}
|
||||
@@ -99,13 +236,24 @@ class MainActivity : FragmentActivity() {
|
||||
transaction.commit()
|
||||
}
|
||||
|
||||
fun keepRunnable() {
|
||||
handler.removeCallbacks(hideRunnable)
|
||||
handler.postDelayed(hideRunnable, delay)
|
||||
fun mainActive() {
|
||||
handler.removeCallbacks(hideMain)
|
||||
handler.postDelayed(hideMain, delayHideMain)
|
||||
}
|
||||
|
||||
private val hideRunnable = Runnable {
|
||||
supportFragmentManager.beginTransaction().hide(mainFragment).commit()
|
||||
fun settingDelayHide() {
|
||||
handler.removeCallbacks(hideSetting)
|
||||
handler.postDelayed(hideSetting, delayHideSetting)
|
||||
}
|
||||
|
||||
fun settingNeverHide() {
|
||||
handler.removeCallbacks(hideSetting)
|
||||
}
|
||||
|
||||
private val hideMain = Runnable {
|
||||
if (!mainFragment.isHidden) {
|
||||
supportFragmentManager.beginTransaction().hide(mainFragment).commit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun mainFragmentIsHidden(): Boolean {
|
||||
@@ -120,8 +268,40 @@ class MainActivity : FragmentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
fun fragmentReady() {
|
||||
mainFragment.fragmentReady()
|
||||
fun fragmentReady(tag: String) {
|
||||
ready++
|
||||
Log.i(TAG, "ready $tag $ready ")
|
||||
if (ready == 7) {
|
||||
mainFragment.fragmentReady()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showTime() {
|
||||
if (SP.time) {
|
||||
showFragment(timeFragment)
|
||||
} else {
|
||||
hideFragment(timeFragment)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFragment(fragment: Fragment) {
|
||||
if (!fragment.isHidden) {
|
||||
return
|
||||
}
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
.show(fragment)
|
||||
.commitNow()
|
||||
}
|
||||
|
||||
private fun hideFragment(fragment: Fragment) {
|
||||
if (fragment.isHidden) {
|
||||
return
|
||||
}
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
.hide(fragment)
|
||||
.commitNow()
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||
@@ -139,8 +319,13 @@ class MainActivity : FragmentActivity() {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
showSetting()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent,
|
||||
e1: MotionEvent?,
|
||||
e2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
@@ -172,42 +357,35 @@ class MainActivity : FragmentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHelp() {
|
||||
val versionName = getPackageInfo().versionName
|
||||
private fun showSetting() {
|
||||
if (!mainFragment.isHidden) {
|
||||
return
|
||||
}
|
||||
|
||||
val textView = TextView(this)
|
||||
textView.text =
|
||||
"当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/"
|
||||
textView.setBackgroundColor(0xFF263238.toInt())
|
||||
textView.setPadding(20, 50, 20, 20)
|
||||
if (settingFragment.isVisible) {
|
||||
return
|
||||
}
|
||||
|
||||
val imageView = ImageView(this)
|
||||
val drawable = ContextCompat.getDrawable(this, R.drawable.appreciate)
|
||||
imageView.setImageDrawable(drawable)
|
||||
imageView.setBackgroundColor(Color.WHITE)
|
||||
settingFragment.show(supportFragmentManager, TAG)
|
||||
settingDelayHide()
|
||||
}
|
||||
|
||||
val linearLayout = LinearLayout(this)
|
||||
linearLayout.orientation = LinearLayout.VERTICAL
|
||||
linearLayout.addView(textView)
|
||||
linearLayout.addView(imageView)
|
||||
private val hideSetting = Runnable {
|
||||
if (settingFragment.isVisible) {
|
||||
settingFragment.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
val layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
imageView.layoutParams = layoutParams
|
||||
textView.layoutParams = layoutParams
|
||||
|
||||
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
|
||||
builder
|
||||
.setView(linearLayout)
|
||||
|
||||
val dialog: AlertDialog = builder.create()
|
||||
dialog.show()
|
||||
private fun hideSettingFragment() {
|
||||
handler.postDelayed(hideSetting, 0)
|
||||
}
|
||||
|
||||
private fun channelUp() {
|
||||
if (mainFragment.isHidden) {
|
||||
if (SP.channelReversal) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
prev()
|
||||
} else {
|
||||
// if (mainFragment.selectedPosition == 0) {
|
||||
@@ -221,6 +399,10 @@ class MainActivity : FragmentActivity() {
|
||||
|
||||
private fun channelDown() {
|
||||
if (mainFragment.isHidden) {
|
||||
if (SP.channelReversal) {
|
||||
prev()
|
||||
return
|
||||
}
|
||||
next()
|
||||
} else {
|
||||
// if (mainFragment.selectedPosition == mainFragment.tvListViewModel.maxNum.size - 1) {
|
||||
@@ -237,6 +419,11 @@ class MainActivity : FragmentActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
if (settingFragment.isVisible) {
|
||||
hideSettingFragment()
|
||||
return
|
||||
}
|
||||
|
||||
if (doubleBackToExitPressedOnce) {
|
||||
super.onBackPressed()
|
||||
return
|
||||
@@ -250,137 +437,200 @@ class MainActivity : FragmentActivity() {
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
// Toast.makeText(this, "keyCode ${keyCodeToString(keyCode)}", Toast.LENGTH_SHORT).show()
|
||||
private fun active() {
|
||||
if (settingFragment.isVisible) {
|
||||
settingDelayHide()
|
||||
}
|
||||
}
|
||||
|
||||
fun onKey(keyCode: Int): Boolean {
|
||||
Log.i(TAG, "keyCode $keyCode")
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_0 -> {
|
||||
showChannel("0")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_1 -> {
|
||||
showChannel("1")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_2 -> {
|
||||
showChannel("2")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_3 -> {
|
||||
showChannel("3")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_4 -> {
|
||||
showChannel("4")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_5 -> {
|
||||
showChannel("5")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_6 -> {
|
||||
showChannel("6")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_7 -> {
|
||||
showChannel("7")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_8 -> {
|
||||
showChannel("8")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_9 -> {
|
||||
showChannel("9")
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_ESCAPE -> {
|
||||
back()
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_BACK -> {
|
||||
back()
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_BOOKMARK -> {
|
||||
showSetting()
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_UNKNOWN -> {
|
||||
showHelp()
|
||||
// showSetting()
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_HELP -> {
|
||||
showHelp()
|
||||
showSetting()
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_SETTINGS -> {
|
||||
showHelp()
|
||||
showSetting()
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_MENU -> {
|
||||
showHelp()
|
||||
showSetting()
|
||||
return true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_ENTER -> {
|
||||
active()
|
||||
switchMainFragment()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||
switchMainFragment()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||
active()
|
||||
channelUp()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_CHANNEL_UP -> {
|
||||
channelUp()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||
active()
|
||||
channelDown()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_CHANNEL_DOWN -> {
|
||||
channelDown()
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
// if (mainFragment.isHidden) {
|
||||
// prevSource()
|
||||
// } else {
|
||||
//// if (mainFragment.tvListViewModel.getTVViewModelCurrent()
|
||||
//// ?.getItemPosition() == 0
|
||||
//// ) {
|
||||
////// mainFragment.toLastPosition()
|
||||
//// hideMainFragment()
|
||||
//// }
|
||||
// }
|
||||
active()
|
||||
if (mainFragment.isHidden && !settingFragment.isVisible) {
|
||||
switchMainFragment()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
// if (mainFragment.isHidden) {
|
||||
// nextSource()
|
||||
// } else {
|
||||
//// if (mainFragment.tvListViewModel.getTVViewModelCurrent()
|
||||
//// ?.getItemPosition() == mainFragment.tvListViewModel.maxNum[mainFragment.selectedPosition] - 1
|
||||
//// ) {
|
||||
//// mainFragment.toFirstPosition()
|
||||
//// }
|
||||
// }
|
||||
active()
|
||||
if (mainFragment.isHidden && !settingFragment.isVisible) {
|
||||
showSetting()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (onKey(keyCode)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
private fun getPackageInfo(): PackageInfo {
|
||||
val flag = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
PackageManager.GET_SIGNATURES
|
||||
} else {
|
||||
PackageManager.GET_SIGNING_CERTIFICATES
|
||||
}
|
||||
private fun getAppSignature() = this.appSignature
|
||||
|
||||
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
packageManager.getPackageInfo(packageName, flag)
|
||||
} else {
|
||||
packageManager.getPackageInfo(
|
||||
packageName,
|
||||
PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong())
|
||||
)
|
||||
override fun onStart() {
|
||||
Log.i(TAG, "onStart MainActivity")
|
||||
syncTime()
|
||||
super.onStart()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
Log.i(TAG, "onResume")
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onResumeFragments() {
|
||||
super.onResumeFragments()
|
||||
mainFragment.changeMenu()
|
||||
|
||||
if (!mainFragment.isHidden) {
|
||||
handler.postDelayed(hideMain, delayHideMain)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAppSignature(): String {
|
||||
val packageInfo = getPackageInfo()
|
||||
|
||||
var sign: Signature? = null
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
val signatures: Array<out Signature>? = packageInfo.signatures
|
||||
if (signatures != null) {
|
||||
sign = signatures[0]
|
||||
}
|
||||
} else {
|
||||
val signingInfo: SigningInfo? = packageInfo.signingInfo
|
||||
if (signingInfo != null) {
|
||||
sign = signingInfo.apkContentsSigners[0]
|
||||
}
|
||||
}
|
||||
if (sign == null) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return hashSignature(sign)
|
||||
override fun onPause() {
|
||||
Log.i(TAG, "onPause")
|
||||
super.onPause()
|
||||
handler.removeCallbacks(hideMain)
|
||||
}
|
||||
|
||||
private fun hashSignature(signature: Signature): String {
|
||||
return try {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
md.update(signature.toByteArray())
|
||||
val digest = md.digest()
|
||||
digest.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error hashing signature", e)
|
||||
""
|
||||
}
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Request.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
override fun onRequestFinished(message: String?) {
|
||||
fragmentReady("request")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TAG = "MainActivity"
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(key: String) {
|
||||
Log.i(TAG, "$key changed")
|
||||
when (key) {
|
||||
SP.KEY_GRID -> mainFragment.changeMenu()
|
||||
SP.KEY_TIME -> showTime()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +1,338 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.GestureDetector
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.leanback.app.BrowseSupportFragment
|
||||
import androidx.leanback.widget.ArrayObjectAdapter
|
||||
import androidx.leanback.widget.HeaderItem
|
||||
import androidx.leanback.widget.ListRow
|
||||
import androidx.leanback.widget.ListRowPresenter
|
||||
import androidx.leanback.widget.ListRowPresenter.SelectItemViewHolderTask
|
||||
import androidx.leanback.widget.OnItemViewClickedListener
|
||||
import androidx.leanback.widget.OnItemViewSelectedListener
|
||||
import androidx.leanback.widget.Presenter
|
||||
import androidx.leanback.widget.Row
|
||||
import androidx.leanback.widget.RowPresenter
|
||||
import androidx.core.view.marginBottom
|
||||
import androidx.core.view.marginStart
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.lizongying.mytv.Utils.getDateTimestamp
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lizongying.mytv.api.YSP
|
||||
import com.lizongying.mytv.databinding.MenuBinding
|
||||
import com.lizongying.mytv.databinding.RowBinding
|
||||
import com.lizongying.mytv.models.ProgramType
|
||||
import com.lizongying.mytv.models.TVList
|
||||
import com.lizongying.mytv.models.TVListViewModel
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
import com.lizongying.mytv.requests.Request
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class MainFragment : BrowseSupportFragment() {
|
||||
var itemPosition: Int = 0
|
||||
class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
|
||||
private var request: Request = Request()
|
||||
private var itemPosition = 0
|
||||
|
||||
private var rowsAdapter: ArrayObjectAdapter? = null
|
||||
private var rowList: MutableList<View> = mutableListOf()
|
||||
|
||||
private var _binding: MenuBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
var tvListViewModel = TVListViewModel()
|
||||
|
||||
private var sharedPref: SharedPreferences? = null
|
||||
private var lastVideoUrl = ""
|
||||
|
||||
private var lastVideoUrl: String = ""
|
||||
private lateinit var application: MyTVApplication
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private lateinit var mUpdateProgramRunnable: UpdateProgramRunnable
|
||||
private lateinit var gestureDetector: GestureDetector
|
||||
|
||||
private var ready = 0
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val context = requireContext()
|
||||
_binding = MenuBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
headersState = HEADERS_DISABLED
|
||||
application = requireActivity().applicationContext as MyTVApplication
|
||||
|
||||
binding.menu.layoutParams.width = application.shouldWidthPx()
|
||||
binding.menu.layoutParams.height = application.shouldHeightPx()
|
||||
|
||||
binding.container.setOnClickListener {
|
||||
hideSelf()
|
||||
}
|
||||
|
||||
gestureDetector = GestureDetector(context, GestureListener())
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
Log.i(TAG, "onSingleTapConfirmed")
|
||||
hideSelf()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideSelf() {
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.hide(this)
|
||||
.commit()
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
activity?.let { request.initYSP(it) }
|
||||
activity?.let { YSP.init(it) }
|
||||
|
||||
sharedPref = activity?.getPreferences(Context.MODE_PRIVATE)
|
||||
itemPosition = SP.itemPosition
|
||||
|
||||
loadRows()
|
||||
view?.post {
|
||||
val content = binding.content
|
||||
|
||||
setupEventListeners()
|
||||
var idx: Long = 0
|
||||
for ((k, v) in TVList.list) {
|
||||
val itemBinding: RowBinding =
|
||||
RowBinding.inflate(layoutInflater, content, false)
|
||||
|
||||
mUpdateProgramRunnable = UpdateProgramRunnable()
|
||||
handler.post(mUpdateProgramRunnable)
|
||||
|
||||
tvListViewModel.getTVListViewModel().value?.forEach { tvViewModel ->
|
||||
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
|
||||
if (tvViewModel.errInfo.value != null
|
||||
&& tvViewModel.id.value == itemPosition
|
||||
) {
|
||||
Toast.makeText(context, tvViewModel.errInfo.value, Toast.LENGTH_SHORT).show()
|
||||
val tvListViewModelCurrent = TVListViewModel()
|
||||
for ((idx2, v1) in v.withIndex()) {
|
||||
val tvViewModel = TVViewModel(v1)
|
||||
tvViewModel.setRowPosition(idx.toInt())
|
||||
tvViewModel.setItemPosition(idx2)
|
||||
tvListViewModelCurrent.addTVViewModel(tvViewModel)
|
||||
tvListViewModel.addTVViewModel(tvViewModel)
|
||||
}
|
||||
}
|
||||
tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
|
||||
tvListViewModel.maxNum.add(v.size)
|
||||
|
||||
// not first time && channel not change
|
||||
if (tvViewModel.ready.value != null
|
||||
&& tvViewModel.id.value == itemPosition
|
||||
&& check(tvViewModel)
|
||||
) {
|
||||
Log.i(TAG, "ready ${tvViewModel.title.value}")
|
||||
(activity as? MainActivity)?.play(tvViewModel)
|
||||
val adapter =
|
||||
CardAdapter(
|
||||
itemBinding.items,
|
||||
this,
|
||||
tvListViewModelCurrent,
|
||||
)
|
||||
rowList.add(itemBinding.items)
|
||||
|
||||
adapter.setItemListener(this)
|
||||
|
||||
itemBinding.header.text = k
|
||||
itemBinding.items.tag = idx.toInt()
|
||||
itemBinding.items.adapter = adapter
|
||||
|
||||
itemBinding.items.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
(activity as MainActivity).mainActive()
|
||||
}
|
||||
})
|
||||
|
||||
val itemDecoration = context?.let { ItemDecoration(it) }
|
||||
if (itemDecoration != null) {
|
||||
itemBinding.items.addItemDecoration(itemDecoration)
|
||||
}
|
||||
|
||||
if (SP.grid) {
|
||||
itemBinding.items.layoutManager =
|
||||
GridLayoutManager(context, 6)
|
||||
itemBinding.items.layoutParams.height =
|
||||
application.dp2Px(110 * ((tvListViewModelCurrent.size() + 6 - 1) / 6) + 5)
|
||||
} else {
|
||||
itemBinding.items.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
val layoutParams = itemBinding.row.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.topMargin = application.dp2Px(11)
|
||||
itemBinding.row.layoutParams = layoutParams
|
||||
itemBinding.row.setOnClickListener {
|
||||
hideSelf()
|
||||
}
|
||||
|
||||
itemBinding.items.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
|
||||
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
||||
gestureDetector.onTouchEvent(e)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
})
|
||||
|
||||
val layoutParamsHeader =
|
||||
itemBinding.header.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsHeader.topMargin = application.px2Px(itemBinding.header.marginTop)
|
||||
layoutParamsHeader.bottomMargin = application.px2Px(itemBinding.header.marginBottom)
|
||||
layoutParamsHeader.marginStart = application.px2Px(itemBinding.header.marginStart)
|
||||
itemBinding.header.layoutParams = layoutParamsHeader
|
||||
itemBinding.header.textSize = application.px2PxFont(itemBinding.header.textSize)
|
||||
|
||||
content.addView(itemBinding.row)
|
||||
|
||||
idx++
|
||||
}
|
||||
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
|
||||
if (tvViewModel.change.value != null) {
|
||||
val title = tvViewModel.title.value
|
||||
Log.i(TAG, "switch $title")
|
||||
if (tvViewModel.pid.value != "") {
|
||||
Log.i(TAG, "request $title ${tvViewModel.pid.value}")
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
tvViewModel.let { request.fetchData(it) }
|
||||
|
||||
if (itemPosition >= tvListViewModel.size()) {
|
||||
itemPosition = 0
|
||||
}
|
||||
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
|
||||
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
|
||||
if (tvViewModel.errInfo.value != null
|
||||
&& tvViewModel.getTV().id == itemPosition
|
||||
) {
|
||||
if (tvViewModel.errInfo.value == "") {
|
||||
(activity as? MainActivity)?.showPlayerFragment()
|
||||
(activity as? MainActivity)?.hideErrorFragment()
|
||||
(activity as? MainActivity)?.hideLoadingFragment()
|
||||
} else {
|
||||
(activity as? MainActivity)?.hidePlayerFragment()
|
||||
(activity as? MainActivity)?.hideLoadingFragment()
|
||||
(activity as? MainActivity)?.showErrorFragment(tvViewModel.errInfo.value.toString())
|
||||
}
|
||||
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
|
||||
setSelectedPosition(
|
||||
tvViewModel.getRowPosition(), true,
|
||||
SelectItemViewHolderTask(tvViewModel.getItemPosition())
|
||||
)
|
||||
} else {
|
||||
if (check(tvViewModel)) {
|
||||
(activity as? MainActivity)?.play(tvViewModel)
|
||||
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
|
||||
}
|
||||
}
|
||||
tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
|
||||
|
||||
setSelectedPosition(
|
||||
tvViewModel.getRowPosition(), true,
|
||||
SelectItemViewHolderTask(tvViewModel.getItemPosition())
|
||||
// not first time && channel not change
|
||||
if (tvViewModel.ready.value != null
|
||||
&& tvViewModel.getTV().id == itemPosition
|
||||
&& check(tvViewModel)
|
||||
) {
|
||||
Log.i(TAG, "ready ${tvViewModel.getTV().title}")
|
||||
(activity as? MainActivity)?.play(tvViewModel)
|
||||
}
|
||||
}
|
||||
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
|
||||
if (tvViewModel.change.value != null) {
|
||||
val title = tvViewModel.getTV().title
|
||||
Log.i(TAG, "switch $title")
|
||||
if (tvViewModel.getTV().pid != "") {
|
||||
Log.i(TAG, "request $title")
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
tvViewModel.let { Request.fetchData(it) }
|
||||
}
|
||||
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
|
||||
setPosition(
|
||||
tvViewModel.getRowPosition(), tvViewModel.getItemPosition()
|
||||
)
|
||||
} else {
|
||||
if (check(tvViewModel)) {
|
||||
// TODO lastVideoUrl
|
||||
(activity as? MainActivity)?.play(tvViewModel)
|
||||
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
|
||||
setPosition(
|
||||
tvViewModel.getRowPosition(), tvViewModel.getItemPosition()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragmentReady()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
handler.removeCallbacks(mUpdateProgramRunnable)
|
||||
with(sharedPref!!.edit()) {
|
||||
putInt("position", itemPosition)
|
||||
apply()
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateProgram(tvViewModel: TVViewModel) {
|
||||
val timestamp = getDateTimestamp()
|
||||
if (timestamp - tvViewModel.programUpdateTime > 60) {
|
||||
if (tvViewModel.program.value!!.isEmpty()) {
|
||||
tvViewModel.programUpdateTime = timestamp
|
||||
request.fetchProgram(tvViewModel)
|
||||
} else {
|
||||
if (timestamp - tvViewModel.program.value!!.last().et < 600) {
|
||||
tvViewModel.programUpdateTime = timestamp
|
||||
request.fetchProgram(tvViewModel)
|
||||
fun changeMenu() {
|
||||
if (SP.grid) {
|
||||
for (i in rowList) {
|
||||
if (i is RecyclerView) {
|
||||
i.layoutManager = GridLayoutManager(context, 6)
|
||||
i.layoutParams.height =
|
||||
application.dp2Px(110 * (((i.adapter as CardAdapter).getItemCount() + 6 - 1) / 6) + 5)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i in rowList) {
|
||||
if (i is RecyclerView) {
|
||||
i.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
i.layoutParams.height = application.dp2Px(110 + 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class UpdateProgramRunnable : Runnable {
|
||||
override fun run() {
|
||||
tvListViewModel.getTVListViewModel().value?.filter { it.programId.value != null }
|
||||
?.forEach { tvViewModel ->
|
||||
updateProgram(
|
||||
tvViewModel
|
||||
)
|
||||
override fun onKey(keyCode: Int): Boolean {
|
||||
if (this.isHidden) {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||
(activity as MainActivity).onKey(keyCode)
|
||||
return true
|
||||
}
|
||||
handler.postDelayed(this, 60000)
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||
(activity as MainActivity).onKey(keyCode)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onItemHasFocus(tvViewModel: TVViewModel) {
|
||||
val row = tvViewModel.getRowPosition()
|
||||
|
||||
for (i in rowList) {
|
||||
if (i.tag as Int != row) {
|
||||
((i as RecyclerView).adapter as CardAdapter).focusable = false
|
||||
(i.adapter as CardAdapter).clear()
|
||||
} else {
|
||||
((i as RecyclerView).adapter as CardAdapter).focusable = true
|
||||
}
|
||||
}
|
||||
|
||||
(activity as MainActivity).mainActive()
|
||||
}
|
||||
|
||||
override fun onItemClicked(tvViewModel: TVViewModel) {
|
||||
if (this.isHidden) {
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
return
|
||||
}
|
||||
|
||||
if (itemPosition != tvViewModel.getTV().id) {
|
||||
itemPosition = tvViewModel.getTV().id
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("menu")
|
||||
Log.i(TAG, "onItemClicked ${tvViewModel.getTV().id}")
|
||||
}
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
}
|
||||
|
||||
fun setPosition() {
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) ?: return
|
||||
val rowPosition = tvViewModel.getRowPosition()
|
||||
val itemPosition = tvViewModel.getItemPosition()
|
||||
setPosition(rowPosition, itemPosition)
|
||||
}
|
||||
|
||||
fun setPosition(rowPosition: Int, itemPosition: Int) {
|
||||
rowList[rowPosition].post {
|
||||
when (val layoutManager = (rowList[rowPosition] as RecyclerView).layoutManager) {
|
||||
is GridLayoutManager -> {
|
||||
layoutManager.findViewByPosition(
|
||||
itemPosition
|
||||
)?.requestFocus()
|
||||
}
|
||||
|
||||
is LinearLayoutManager -> {
|
||||
layoutManager.findViewByPosition(
|
||||
itemPosition
|
||||
)?.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun check(tvViewModel: TVViewModel): Boolean {
|
||||
val title = tvViewModel.title.value
|
||||
val title = tvViewModel.getTV().title
|
||||
val videoUrl = tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
|
||||
if (videoUrl == null || videoUrl == "") {
|
||||
Log.e(TAG, "$title videoUrl is empty")
|
||||
@@ -166,62 +347,23 @@ class MainFragment : BrowseSupportFragment() {
|
||||
return true
|
||||
}
|
||||
|
||||
fun toLastPosition() {
|
||||
setSelectedPosition(
|
||||
selectedPosition, false,
|
||||
SelectItemViewHolderTask(tvListViewModel.maxNum[selectedPosition] - 1)
|
||||
)
|
||||
}
|
||||
|
||||
fun toFirstPosition() {
|
||||
setSelectedPosition(
|
||||
selectedPosition, false,
|
||||
SelectItemViewHolderTask(0)
|
||||
)
|
||||
}
|
||||
|
||||
override fun startHeadersTransition(withHeaders: Boolean) {
|
||||
}
|
||||
|
||||
private fun loadRows() {
|
||||
rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
|
||||
|
||||
val cardPresenter = CardPresenter(viewLifecycleOwner)
|
||||
|
||||
var idx: Long = 0
|
||||
for ((k, v) in TVList.list) {
|
||||
val listRowAdapter = ArrayObjectAdapter(cardPresenter)
|
||||
for ((idx2, v1) in v.withIndex()) {
|
||||
val tvViewModel = TVViewModel(v1)
|
||||
tvViewModel.setRowPosition(idx.toInt())
|
||||
tvViewModel.setItemPosition(idx2)
|
||||
tvListViewModel.addTVViewModel(tvViewModel)
|
||||
listRowAdapter.add(tvViewModel)
|
||||
}
|
||||
tvListViewModel.maxNum.add(v.size)
|
||||
val header = HeaderItem(idx, k)
|
||||
rowsAdapter!!.add(ListRow(header, listRowAdapter))
|
||||
idx++
|
||||
}
|
||||
|
||||
adapter = rowsAdapter
|
||||
|
||||
itemPosition = sharedPref?.getInt("position", 0)!!
|
||||
if (itemPosition >= tvListViewModel.size()) {
|
||||
itemPosition = 0
|
||||
}
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
}
|
||||
|
||||
fun fragmentReady() {
|
||||
ready++
|
||||
Log.i(TAG, "ready $ready")
|
||||
if (ready == 3) {
|
||||
// request.fetchPage()
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
tvViewModel?.changed()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("init")
|
||||
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
|
||||
updateEPG(tvViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun play(itemPosition: Int) {
|
||||
view?.post {
|
||||
if (itemPosition > -1 && itemPosition < tvListViewModel.size()) {
|
||||
this.itemPosition = itemPosition
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("num")
|
||||
} else {
|
||||
Toast.makeText(context, "频道不存在", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,9 +374,7 @@ class MainFragment : BrowseSupportFragment() {
|
||||
itemPosition = tvListViewModel.size() - 1
|
||||
}
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
tvViewModel?.changed()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("prev")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,85 +385,73 @@ class MainFragment : BrowseSupportFragment() {
|
||||
itemPosition = 0
|
||||
}
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
tvViewModel?.changed()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("next")
|
||||
}
|
||||
}
|
||||
|
||||
fun prevSource() {
|
||||
view?.post {
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
if (tvViewModel != null) {
|
||||
if (tvViewModel.videoUrl.value!!.size > 1) {
|
||||
val videoIndex = tvViewModel.videoIndex.value?.minus(1)
|
||||
if (videoIndex == -1) {
|
||||
tvViewModel.setVideoIndex(tvViewModel.videoUrl.value!!.size - 1)
|
||||
}
|
||||
tvViewModel.changed()
|
||||
}
|
||||
private fun updateEPG(tvViewModel: TVViewModel) {
|
||||
when (tvViewModel.getTV().programType) {
|
||||
ProgramType.Y_PROTO -> {
|
||||
Request.fetchYProtoEPG(tvViewModel)
|
||||
}
|
||||
|
||||
ProgramType.Y_JCE -> {
|
||||
Request.fetchYJceEPG(tvViewModel)
|
||||
}
|
||||
|
||||
ProgramType.F -> {
|
||||
Request.fetchFEPG(tvViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun nextSource() {
|
||||
view?.post {
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
if (tvViewModel != null) {
|
||||
if (tvViewModel.videoUrl.value!!.size > 1) {
|
||||
val videoIndex = tvViewModel.videoIndex.value?.plus(1)
|
||||
if (videoIndex == tvViewModel.videoUrl.value!!.size) {
|
||||
tvViewModel.setVideoIndex(0)
|
||||
}
|
||||
tvViewModel.changed()
|
||||
fun shouldHasFocus(tvModel: TVViewModel): Boolean {
|
||||
return tvModel == tvListViewModel.getTVViewModel(itemPosition)
|
||||
}
|
||||
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
Log.i(TAG, "onHiddenChanged $hidden")
|
||||
super.onHiddenChanged(hidden)
|
||||
if (!hidden) {
|
||||
val tvModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
val rowPosition = tvModel?.getRowPosition()
|
||||
val itemPosition = tvModel?.getItemPosition()
|
||||
Log.i(TAG, "toPosition $rowPosition $itemPosition")
|
||||
for (i in rowList) {
|
||||
if (i.tag as Int == rowPosition) {
|
||||
((i as RecyclerView).adapter as CardAdapter).updateEPG()
|
||||
(i.adapter as CardAdapter).focusable = true
|
||||
(i.adapter as CardAdapter).toPosition(itemPosition!!)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupEventListeners() {
|
||||
onItemViewClickedListener = ItemViewClickedListener()
|
||||
onItemViewSelectedListener = ItemViewSelectedListener()
|
||||
}
|
||||
|
||||
private inner class ItemViewClickedListener : OnItemViewClickedListener {
|
||||
override fun onItemClicked(
|
||||
itemViewHolder: Presenter.ViewHolder,
|
||||
item: Any,
|
||||
rowViewHolder: RowPresenter.ViewHolder,
|
||||
row: Row
|
||||
) {
|
||||
if (item is TVViewModel) {
|
||||
if (itemPosition != item.id.value!!) {
|
||||
itemPosition = item.id.value!!
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
tvViewModel?.changed()
|
||||
} else {
|
||||
view?.post {
|
||||
for (i in rowList) {
|
||||
((i as RecyclerView).adapter as CardAdapter).focusable = false
|
||||
}
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ItemViewSelectedListener : OnItemViewSelectedListener {
|
||||
override fun onItemSelected(
|
||||
itemViewHolder: Presenter.ViewHolder?, item: Any?,
|
||||
rowViewHolder: RowPresenter.ViewHolder, row: Row
|
||||
) {
|
||||
if (item is TVViewModel) {
|
||||
tvListViewModel.setItemPositionCurrent(item.id.value!!)
|
||||
(activity as MainActivity).keepRunnable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
Log.i(TAG, "onResume")
|
||||
super.onResume()
|
||||
view!!.requestFocus()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.i(TAG, "onDestroy")
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
Log.i(TAG, "onDestroyView")
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainFragment"
|
||||
private const val POSITION = "position"
|
||||
}
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lizongying.mytv.Utils.dpToPx
|
||||
import com.lizongying.mytv.databinding.RowBinding
|
||||
import com.lizongying.mytv.databinding.ShowBinding
|
||||
import com.lizongying.mytv.models.TVListViewModel
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainFragment2 : Fragment(), CardAdapter.ItemListener {
|
||||
|
||||
private var itemPosition = 0
|
||||
|
||||
private var rowList: MutableList<View> = mutableListOf()
|
||||
|
||||
private var _binding: ShowBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var request: Request = Request()
|
||||
|
||||
var tvListViewModel = TVListViewModel()
|
||||
|
||||
private var sharedPref: SharedPreferences? = null
|
||||
|
||||
private var lastVideoUrl: String = ""
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private lateinit var mUpdateProgramRunnable: UpdateProgramRunnable
|
||||
|
||||
private var ready = 0
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = ShowBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
activity?.let { request.initYSP(it) }
|
||||
sharedPref = activity?.getPreferences(Context.MODE_PRIVATE)
|
||||
|
||||
view?.post {
|
||||
val content = binding.content
|
||||
|
||||
var idx: Long = 0
|
||||
for ((k, v) in TVList.list) {
|
||||
val itemBinding: RowBinding =
|
||||
RowBinding.inflate(layoutInflater, content, false)
|
||||
|
||||
val tvListViewModelCurrent = TVListViewModel()
|
||||
for ((idx2, v1) in v.withIndex()) {
|
||||
val tvViewModel = TVViewModel(v1)
|
||||
tvViewModel.setRowPosition(idx.toInt())
|
||||
tvViewModel.setItemPosition(idx2)
|
||||
tvListViewModelCurrent.addTVViewModel(tvViewModel)
|
||||
tvListViewModel.addTVViewModel(tvViewModel)
|
||||
}
|
||||
tvListViewModel.maxNum.add(v.size)
|
||||
|
||||
val adapter = CardAdapter(viewLifecycleOwner, tvListViewModelCurrent)
|
||||
rowList.add(itemBinding.rowItems)
|
||||
|
||||
adapter.setItemListener(this)
|
||||
|
||||
itemBinding.rowHeader.text = k
|
||||
itemBinding.rowItems.adapter = adapter
|
||||
itemBinding.rowItems.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
|
||||
val layoutParams = itemBinding.row.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.topMargin = dpToPx(11F)
|
||||
itemBinding.row.layoutParams = layoutParams
|
||||
content.addView(itemBinding.row)
|
||||
|
||||
idx++
|
||||
}
|
||||
|
||||
mUpdateProgramRunnable = UpdateProgramRunnable()
|
||||
handler.post(mUpdateProgramRunnable)
|
||||
|
||||
itemPosition = sharedPref?.getInt("position", 0)!!
|
||||
if (itemPosition >= tvListViewModel.size()) {
|
||||
itemPosition = 0
|
||||
}
|
||||
setPosition()
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVListViewModel().value?.forEach { tvViewModel ->
|
||||
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
|
||||
if (tvViewModel.errInfo.value != null
|
||||
&& tvViewModel.id.value == itemPosition
|
||||
) {
|
||||
Toast.makeText(context, tvViewModel.errInfo.value, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
|
||||
|
||||
// not first time && channel not change
|
||||
if (tvViewModel.ready.value != null
|
||||
&& tvViewModel.id.value == itemPosition
|
||||
&& check(tvViewModel)
|
||||
) {
|
||||
Log.i(TAG, "ready ${tvViewModel.title.value}")
|
||||
(activity as? MainActivity)?.play(tvViewModel)
|
||||
}
|
||||
}
|
||||
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
|
||||
if (tvViewModel.change.value != null) {
|
||||
val title = tvViewModel.title.value
|
||||
Log.i(TAG, "switch $title")
|
||||
if (tvViewModel.pid.value != "") {
|
||||
Log.i(TAG, "request $title ${tvViewModel.pid.value}")
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
tvViewModel.let { request.fetchData(it) }
|
||||
}
|
||||
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
|
||||
setPosition(
|
||||
tvViewModel.getRowPosition(), tvViewModel.getItemPosition()
|
||||
)
|
||||
} else {
|
||||
if (check(tvViewModel)) {
|
||||
(activity as? MainActivity)?.play(tvViewModel)
|
||||
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
|
||||
setPosition(
|
||||
tvViewModel.getRowPosition(), tvViewModel.getItemPosition()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragmentReady()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onItemFocusChange(tvViewModel: TVViewModel, hasFocus: Boolean) {
|
||||
if (hasFocus) {
|
||||
tvListViewModel.setItemPositionCurrent(tvViewModel.id.value!!)
|
||||
(activity as MainActivity).keepRunnable()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemClicked(tvViewModel: TVViewModel) {
|
||||
if (this.isHidden) {
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
return
|
||||
}
|
||||
|
||||
if (itemPosition != tvViewModel.id.value!!) {
|
||||
itemPosition = tvViewModel.id.value!!
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
}
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
}
|
||||
|
||||
fun setPosition() {
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
rowList[tvViewModel!!.getRowPosition()].post {
|
||||
((rowList[tvViewModel.getRowPosition()] as RecyclerView).layoutManager as LinearLayoutManager).findViewByPosition(
|
||||
tvViewModel.getItemPosition()
|
||||
)?.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
fun setPosition(rowPosition: Int, itemPosition: Int) {
|
||||
rowList[rowPosition].post {
|
||||
((rowList[rowPosition] as RecyclerView).layoutManager as LinearLayoutManager).findViewByPosition(
|
||||
itemPosition
|
||||
)?.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
fun check(tvViewModel: TVViewModel): Boolean {
|
||||
val title = tvViewModel.title.value
|
||||
val videoUrl = tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
|
||||
if (videoUrl == null || videoUrl == "") {
|
||||
Log.e(TAG, "$title videoUrl is empty")
|
||||
return false
|
||||
}
|
||||
|
||||
if (videoUrl == lastVideoUrl) {
|
||||
Log.e(TAG, "$title videoUrl is duplication")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun fragmentReady() {
|
||||
ready++
|
||||
Log.i(TAG, "ready $ready")
|
||||
if (ready == 3) {
|
||||
// request.fetchPage()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
}
|
||||
}
|
||||
|
||||
fun prev() {
|
||||
view?.post {
|
||||
itemPosition--
|
||||
if (itemPosition == -1) {
|
||||
itemPosition = tvListViewModel.size() - 1
|
||||
}
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
}
|
||||
}
|
||||
|
||||
fun next() {
|
||||
view?.post {
|
||||
itemPosition++
|
||||
if (itemPosition == tvListViewModel.size()) {
|
||||
itemPosition = 0
|
||||
}
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateProgram(tvViewModel: TVViewModel) {
|
||||
val timestamp = Utils.getDateTimestamp()
|
||||
if (timestamp - tvViewModel.programUpdateTime > 60) {
|
||||
if (tvViewModel.program.value!!.isEmpty()) {
|
||||
tvViewModel.programUpdateTime = timestamp
|
||||
request.fetchProgram(tvViewModel)
|
||||
} else {
|
||||
if (timestamp - tvViewModel.program.value!!.last().et < 600) {
|
||||
tvViewModel.programUpdateTime = timestamp
|
||||
request.fetchProgram(tvViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class UpdateProgramRunnable : Runnable {
|
||||
override fun run() {
|
||||
tvListViewModel.getTVListViewModel().value?.filter { it.programId.value != null }
|
||||
?.forEach { tvViewModel ->
|
||||
updateProgram(
|
||||
tvViewModel
|
||||
)
|
||||
}
|
||||
handler.postDelayed(this, 60000)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
handler.removeCallbacks(mUpdateProgramRunnable)
|
||||
with(sharedPref!!.edit()) {
|
||||
putInt("position", itemPosition)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
view!!.requestFocus()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainFragment"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.WindowManager
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.multidex.MultiDex
|
||||
import com.lizongying.mytv.models.MyViewModel
|
||||
|
||||
|
||||
class MyTVApplication : Application() {
|
||||
private lateinit var displayMetrics: DisplayMetrics
|
||||
private lateinit var realDisplayMetrics: DisplayMetrics
|
||||
|
||||
private lateinit var windowManager: WindowManager
|
||||
|
||||
private var width = 0
|
||||
private var height = 0
|
||||
private var shouldWidth = 0
|
||||
private var shouldHeight = 0
|
||||
private var deviation = 0
|
||||
private var ratio = 1.0
|
||||
private var density = 2.0f
|
||||
private var scale = 1.0f
|
||||
|
||||
lateinit var myViewModel: MyViewModel
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
displayMetrics = DisplayMetrics()
|
||||
realDisplayMetrics = DisplayMetrics()
|
||||
windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
windowManager.defaultDisplay.getRealMetrics(realDisplayMetrics)
|
||||
|
||||
if (realDisplayMetrics.heightPixels > realDisplayMetrics.widthPixels) {
|
||||
width = realDisplayMetrics.heightPixels
|
||||
height = realDisplayMetrics.widthPixels
|
||||
} else {
|
||||
width = realDisplayMetrics.widthPixels
|
||||
height = realDisplayMetrics.heightPixels
|
||||
}
|
||||
|
||||
density = displayMetrics.density
|
||||
|
||||
ratio = if ((width.toDouble() / height) < (16.0 / 9)) {
|
||||
width * 2 / 1920.0 / density
|
||||
} else {
|
||||
height * 2 / 1080.0 / density
|
||||
}
|
||||
|
||||
if ((width.toDouble() / height) < (16.0 / 9.0)) {
|
||||
ratio = width * 2 / 1920.0 / density
|
||||
shouldWidth = width
|
||||
shouldHeight = (width * 9.0 / 16.0).toInt()
|
||||
} else {
|
||||
ratio = height * 2 / 1080.0 / density
|
||||
shouldHeight = height
|
||||
shouldWidth = (height * 16.0 / 9.0).toInt()
|
||||
}
|
||||
|
||||
deviation = width - Resources.getSystem().displayMetrics.widthPixels
|
||||
scale = Resources.getSystem().displayMetrics.scaledDensity
|
||||
|
||||
myViewModel = ViewModelProvider(
|
||||
ViewModelStore(),
|
||||
ViewModelProvider.NewInstanceFactory()
|
||||
)[MyViewModel::class.java]
|
||||
}
|
||||
|
||||
fun getDisplayMetrics(): DisplayMetrics {
|
||||
return displayMetrics
|
||||
}
|
||||
|
||||
fun getRealDisplayMetrics(): DisplayMetrics {
|
||||
return realDisplayMetrics
|
||||
}
|
||||
|
||||
fun shouldWidthPx(): Int {
|
||||
return shouldWidth
|
||||
}
|
||||
|
||||
fun shouldHeightPx(): Int {
|
||||
return shouldHeight
|
||||
}
|
||||
|
||||
fun getWidth(): Int {
|
||||
return width
|
||||
}
|
||||
|
||||
fun getHeight(): Int {
|
||||
return height
|
||||
}
|
||||
|
||||
fun getDeviation(): Int {
|
||||
return deviation
|
||||
}
|
||||
|
||||
fun getRatio(): Double {
|
||||
return ratio
|
||||
}
|
||||
|
||||
fun dp2Px(dp: Int): Int {
|
||||
return (dp * ratio * density + 0.5f).toInt()
|
||||
}
|
||||
|
||||
fun px2Px(px: Int): Int {
|
||||
return (px * ratio + 0.5f).toInt()
|
||||
}
|
||||
|
||||
fun px2PxFont(px: Float): Float {
|
||||
return (px * ratio / scale).toFloat()
|
||||
}
|
||||
|
||||
fun sp2Px(sp: Float): Float {
|
||||
return (sp * ratio * scale).toFloat()
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
super.attachBaseContext(base)
|
||||
MultiDex.install(base)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
|
||||
class NetworkChangeReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
|
||||
val isConnected = context?.isNetworkConnected
|
||||
if (isConnected == true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
|
||||
interface OnSharedPreferenceChangeListener {
|
||||
fun onSharedPreferenceChanged(key: String)
|
||||
}
|
||||
@@ -31,14 +31,21 @@ class PlayerFragment : Fragment() {
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = PlayerBinding.inflate(inflater, container, false)
|
||||
|
||||
playerView = _binding!!.playerView
|
||||
(activity as MainActivity).playerFragment = this
|
||||
|
||||
playerView?.viewTreeObserver?.addOnGlobalLayoutListener(object :
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
@OptIn(UnstableApi::class)
|
||||
override fun onGlobalLayout() {
|
||||
playerView!!.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
|
||||
// val renderersFactory = context?.let { DefaultRenderersFactory(it) }
|
||||
// renderersFactory?.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
|
||||
|
||||
playerView!!.player = activity?.let {
|
||||
ExoPlayer.Builder(it)
|
||||
// .setRenderersFactory(renderersFactory!!)
|
||||
.build()
|
||||
}
|
||||
playerView!!.player?.playWhenReady = true
|
||||
@@ -61,37 +68,53 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
override fun onPlayerError(error: PlaybackException) {
|
||||
super.onPlayerError(error)
|
||||
|
||||
Log.e(TAG, "PlaybackException $error")
|
||||
val err = "播放错误"
|
||||
tvViewModel?.setErrInfo(err)
|
||||
tvViewModel?.changed("retry")
|
||||
}
|
||||
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
super.onIsPlayingChanged(isPlaying)
|
||||
if (isPlaying) {
|
||||
tvViewModel?.setErrInfo("")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
(activity as MainActivity).fragmentReady()
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
return _binding!!.root
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
fun play(tvViewModel: TVViewModel) {
|
||||
this.tvViewModel = tvViewModel
|
||||
val videoUrlCurrent =
|
||||
tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
|
||||
playerView?.player?.run {
|
||||
videoUrlCurrent?.let { setMediaItem(MediaItem.fromUri(it)) }
|
||||
setMediaItem(MediaItem.fromUri(tvViewModel.getVideoUrlCurrent()))
|
||||
prepare()
|
||||
volume = tvViewModel.getTV().volume
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
Log.i(TAG, "onStart")
|
||||
super.onStart()
|
||||
if (playerView != null) {
|
||||
if (playerView != null && playerView!!.player?.isPlaying == false) {
|
||||
Log.i(TAG, "replay")
|
||||
playerView!!.player?.prepare()
|
||||
playerView!!.player?.play()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (playerView != null) {
|
||||
override fun onResume() {
|
||||
Log.i(TAG, "onResume")
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (playerView != null && playerView!!.player?.isPlaying == true) {
|
||||
playerView!!.player?.stop()
|
||||
}
|
||||
}
|
||||
@@ -103,7 +126,12 @@ class PlayerFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PlaybackVideoFragment"
|
||||
private const val TAG = "PlayerFragment"
|
||||
}
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.lizongying.mytv.Utils.getDateFormat
|
||||
import com.lizongying.mytv.api.ApiClient
|
||||
import com.lizongying.mytv.api.Info
|
||||
import com.lizongying.mytv.api.LiveInfo
|
||||
import com.lizongying.mytv.api.LiveInfoRequest
|
||||
import com.lizongying.mytv.api.YSP
|
||||
import com.lizongying.mytv.api.YSPApiService
|
||||
import com.lizongying.mytv.api.YSPBtraceService
|
||||
import com.lizongying.mytv.api.YSPProtoService
|
||||
import com.lizongying.mytv.api.YSPTokenService
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel
|
||||
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.epgProgramModel
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
|
||||
class Request {
|
||||
private var yspTokenService: YSPTokenService = ApiClient().yspTokenService
|
||||
private var yspApiService: YSPApiService = ApiClient().yspApiService
|
||||
private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService
|
||||
private var yspProtoService: YSPProtoService = ApiClient().yspProtoService
|
||||
private var ysp: YSP? = null
|
||||
|
||||
// TODO onDestroy
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private lateinit var myRunnable: MyRunnable
|
||||
|
||||
private var mapping = mapOf(
|
||||
"CCTV4K" to "CCTV4K 超高清",
|
||||
"CCTV1" to "CCTV1 综合",
|
||||
"CCTV2" to "CCTV2 财经",
|
||||
"CCTV4" to "CCTV4 中文国际",
|
||||
"CCTV5" to "CCTV5 体育",
|
||||
"CCTV5+" to "CCTV5+ 体育赛事",
|
||||
"CCTV7" to "CCTV7 国防军事",
|
||||
"CCTV9" to "CCTV9 记录",
|
||||
"CCTV10" to "CCTV10 科教",
|
||||
"CCTV11" to "CCTV11 戏曲",
|
||||
"CCTV12" to "CCTV12 社会与法",
|
||||
"CCTV13" to "CCTV13 新闻",
|
||||
"CCTV14" to "CCTV14 少儿",
|
||||
"CCTV15" to "CCTV15 音乐",
|
||||
"CCTV16-HD" to "CCTV16 奥林匹克",
|
||||
"CCTV17" to "CCTV17 农业农村",
|
||||
"CGTN" to "CGTN",
|
||||
"CGTN法语频道" to "CGTN 法语频道",
|
||||
"CGTN俄语频道" to "CGTN 俄语频道",
|
||||
"CGTN阿拉伯语频道" to "CGTN 阿拉伯语频道",
|
||||
"CGTN西班牙语频道" to "CGTN 西班牙语频道",
|
||||
"CGTN外语纪录频道" to "CGTN 纪录频道",
|
||||
"东方卫视" to "东方卫视",
|
||||
"湖南卫视" to "湖南卫视",
|
||||
"湖北卫视" to "湖北卫视",
|
||||
"辽宁卫视" to "辽宁卫视",
|
||||
"江苏卫视" to "江苏卫视",
|
||||
"江西卫视" to "江西卫视",
|
||||
"山东卫视" to "山东卫视",
|
||||
"广东卫视" to "广东卫视",
|
||||
"广西卫视" to "广西卫视",
|
||||
"重庆卫视" to "重庆卫视",
|
||||
"河南卫视" to "河南卫视",
|
||||
"河北卫视" to "河北卫视",
|
||||
"贵州卫视" to "贵州卫视",
|
||||
"北京卫视" to "北京卫视",
|
||||
"黑龙江卫视" to "黑龙江卫视",
|
||||
"浙江卫视" to "浙江卫视",
|
||||
"安徽卫视" to "安徽卫视",
|
||||
"深圳卫视" to "深圳卫视",
|
||||
"四川卫视" to "四川卫视",
|
||||
"福建东南卫视" to "东南卫视",
|
||||
"海南卫视" to "海南卫视",
|
||||
)
|
||||
|
||||
fun initYSP(context: Context) {
|
||||
ysp = YSP(context)
|
||||
}
|
||||
|
||||
var call: Call<LiveInfo>? = null
|
||||
|
||||
fun fetchVideo(tvModel: TVViewModel, cookie: String) {
|
||||
call?.cancel()
|
||||
if (::myRunnable.isInitialized) {
|
||||
handler.removeCallbacks(myRunnable)
|
||||
}
|
||||
|
||||
val title = tvModel.title.value
|
||||
|
||||
tvModel.seq = 0
|
||||
val data = ysp?.switch(tvModel)
|
||||
val request = data?.let { LiveInfoRequest(it) }
|
||||
call = request?.let { yspApiService.getLiveInfo("guid=${ysp?.getGuid()}; $cookie", it) }
|
||||
|
||||
call?.enqueue(object : Callback<LiveInfo> {
|
||||
override fun onResponse(call: Call<LiveInfo>, response: Response<LiveInfo>) {
|
||||
if (response.isSuccessful) {
|
||||
val liveInfo = response.body()
|
||||
if (liveInfo?.data?.playurl != null) {
|
||||
val chanll = liveInfo.data.chanll
|
||||
val decodedBytes = Base64.decode(
|
||||
chanll.substring(9, chanll.length - 3),
|
||||
Base64.DEFAULT
|
||||
)
|
||||
val decodedString = String(decodedBytes)
|
||||
val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
|
||||
val matchResult = regex.find(decodedString)
|
||||
if (matchResult != null) {
|
||||
val (key, iv) = matchResult.destructured
|
||||
val keyBytes = Base64.decode(key, Base64.DEFAULT)
|
||||
val ivBytes = Base64.decode(iv, Base64.DEFAULT)
|
||||
val url = liveInfo.data.playurl + "&revoi=" + encryptTripleDES(
|
||||
keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0),
|
||||
ivBytes
|
||||
).uppercase()
|
||||
Log.i(TAG, "$title url $url")
|
||||
tvModel.addVideoUrl(url)
|
||||
tvModel.allReady()
|
||||
tvModel.retryTimes = 0
|
||||
myRunnable = MyRunnable(tvModel)
|
||||
handler.post(myRunnable)
|
||||
} else {
|
||||
Log.e(TAG, "$title key error")
|
||||
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
|
||||
tvModel.retryTimes++
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (liveInfo?.data?.errinfo != null && liveInfo.data.errinfo == "应版权方要求,暂停提供直播信号,请点击观看其他精彩节目") {
|
||||
Log.e(TAG, "$title error ${liveInfo.data.errinfo}")
|
||||
tvModel.setErrInfo(liveInfo.data.errinfo)
|
||||
} else {
|
||||
Log.e(TAG, "$title url error $request $liveInfo")
|
||||
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
|
||||
tvModel.retryTimes++
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "$title status error")
|
||||
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
|
||||
tvModel.retryTimes++
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<LiveInfo>, t: Throwable) {
|
||||
Log.e(TAG, "$title request error")
|
||||
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
|
||||
tvModel.retryTimes++
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun fetchVideo(tvModel: TVViewModel) {
|
||||
yspTokenService.getInfo()
|
||||
.enqueue(object : Callback<Info> {
|
||||
override fun onResponse(call: Call<Info>, response: Response<Info>) {
|
||||
if (response.isSuccessful) {
|
||||
val token = response.body()?.data?.token
|
||||
Log.i(TAG, "info success $token")
|
||||
val cookie =
|
||||
"vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token"
|
||||
fetchVideo(tvModel, cookie)
|
||||
} else {
|
||||
Log.e(TAG, "info status error")
|
||||
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
|
||||
tvModel.retryTimes++
|
||||
fetchVideo(tvModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Info>, t: Throwable) {
|
||||
Log.e(TAG, "info request error $t")
|
||||
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
|
||||
tvModel.retryTimes++
|
||||
fetchVideo(tvModel)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun fetchData(tvModel: TVViewModel) {
|
||||
if (tvModel.needToken) {
|
||||
fetchVideo(tvModel)
|
||||
} else {
|
||||
val cookie = "vplatform=109"
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
}
|
||||
|
||||
inner class MyRunnable(private val tvModel: TVViewModel) : Runnable {
|
||||
override fun run() {
|
||||
fetchBtrace(tvModel)
|
||||
handler.postDelayed(this, 60000)
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchBtrace(tvModel: TVViewModel) {
|
||||
val title = tvModel.title.value
|
||||
|
||||
val guid = ysp?.getGuid()!!
|
||||
val pid = tvModel.pid.value!!
|
||||
val sid = tvModel.sid.value!!
|
||||
yspBtraceService.kvcollect(
|
||||
c_timestamp = ysp?.generateGuid()!!,
|
||||
guid = guid,
|
||||
c_guid = guid,
|
||||
prog = sid,
|
||||
viewid = sid,
|
||||
fpid = pid,
|
||||
livepid = pid,
|
||||
sUrl = "https://www.yangshipin.cn/#/tv/home?pid=$pid",
|
||||
playno = ysp?.getRand()!!,
|
||||
ftime = getDateFormat("yyyy-MM-dd HH:mm:ss"),
|
||||
seq = tvModel.seq.toString(),
|
||||
)
|
||||
.enqueue(object : Callback<Void> {
|
||||
override fun onResponse(call: Call<Void>, response: Response<Void>) {
|
||||
if (response.isSuccessful) {
|
||||
// Log.d(TAG, "$title kvcollect success")
|
||||
} else {
|
||||
Log.e(TAG, "$title kvcollect status error")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Void>, t: Throwable) {
|
||||
Log.e(TAG, "$title kvcollect request error")
|
||||
}
|
||||
})
|
||||
tvModel.seq++
|
||||
}
|
||||
|
||||
fun fetchPage() {
|
||||
yspProtoService.getPage().enqueue(object : Callback<pageModel.Response> {
|
||||
override fun onResponse(
|
||||
call: Call<pageModel.Response>,
|
||||
response: Response<pageModel.Response>
|
||||
) {
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body()
|
||||
|
||||
if (body?.data?.feedModuleListCount == 1) {
|
||||
for (item in body.data?.feedModuleListList!![0]?.dataTvChannelListList!!) {
|
||||
if (item.isVip && !item.isLimitedFree) {
|
||||
continue
|
||||
}
|
||||
Log.i(
|
||||
TAG,
|
||||
"${item.channelName},${item.pid},${item.streamId}"
|
||||
)
|
||||
var channelType = "央视频道"
|
||||
if (item?.channelType === "weishi") {
|
||||
channelType = "地方频道"
|
||||
}
|
||||
if (!mapping.containsKey(item.channelName)) {
|
||||
continue
|
||||
}
|
||||
val tv =
|
||||
TVList.list[channelType]?.find { it.title == mapping[item.channelName] }
|
||||
if (tv != null) {
|
||||
tv.logo = item.tvLogo
|
||||
tv.pid = item.pid
|
||||
tv.sid = item.streamId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<pageModel.Response>, t: Throwable) {
|
||||
Log.e(TAG, "Page request failed", t)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun fetchProgram(tvViewModel: TVViewModel) {
|
||||
val title = tvViewModel.title.value
|
||||
yspProtoService.getProgram(tvViewModel.programId.value!!, getDateFormat("yyyyMMdd"))
|
||||
.enqueue(object : Callback<epgProgramModel.Response> {
|
||||
override fun onResponse(
|
||||
call: Call<epgProgramModel.Response>,
|
||||
response: Response<epgProgramModel.Response>
|
||||
) {
|
||||
if (response.isSuccessful) {
|
||||
val program = response.body()
|
||||
if (program != null) {
|
||||
tvViewModel.addProgram(program.dataListList)
|
||||
Log.i(TAG, "$title program ${program.dataListList.size}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<epgProgramModel.Response>, t: Throwable) {
|
||||
Log.e(TAG, "$title program request failed $t")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun encryptTripleDES(key: ByteArray, iv: ByteArray): String {
|
||||
val plaintext =
|
||||
"""{"mver":"1","subver":"1.2","host":"www.yangshipin.cn/#/tv/home?pid=","referer":"","canvas":"YSPANGLE(Apple,AppleM1Pro,OpenGL4.1)"}"""
|
||||
return try {
|
||||
val keySpec = SecretKeySpec(key, "DESede")
|
||||
val ivSpec = IvParameterSpec(iv)
|
||||
val cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding")
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
|
||||
val encryptedBytes = cipher.doFinal(plaintext.toByteArray())
|
||||
return encryptedBytes.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "Request"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
object SP {
|
||||
// Name of the sp file TODO Should use a meaningful name and do migrations
|
||||
private const val SP_FILE_NAME = "MainActivity"
|
||||
|
||||
// If Change channel with up and down in reversed order or not
|
||||
private const val KEY_CHANNEL_REVERSAL = "channel_reversal"
|
||||
|
||||
// If use channel num to select channel or not
|
||||
private const val KEY_CHANNEL_NUM = "channel_num"
|
||||
|
||||
const val KEY_TIME = "time"
|
||||
|
||||
// If start app on device boot or not
|
||||
private const val KEY_BOOT_STARTUP = "boot_startup"
|
||||
|
||||
const val KEY_GRID = "grid"
|
||||
|
||||
// Position in list of the selected channel item
|
||||
private const val KEY_POSITION = "position"
|
||||
|
||||
// guid
|
||||
private const val KEY_GUID = "guid"
|
||||
|
||||
private lateinit var sp: SharedPreferences
|
||||
|
||||
private var listener: OnSharedPreferenceChangeListener? = null
|
||||
|
||||
/**
|
||||
* The method must be invoked as early as possible(At least before using the keys)
|
||||
*/
|
||||
fun init(context: Context) {
|
||||
sp = context.getSharedPreferences(SP_FILE_NAME, Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
fun setOnSharedPreferenceChangeListener(listener: OnSharedPreferenceChangeListener) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
var channelReversal: Boolean
|
||||
get() = sp.getBoolean(KEY_CHANNEL_REVERSAL, false)
|
||||
set(value) = sp.edit().putBoolean(KEY_CHANNEL_REVERSAL, value).apply()
|
||||
|
||||
var channelNum: Boolean
|
||||
get() = sp.getBoolean(KEY_CHANNEL_NUM, true)
|
||||
set(value) = sp.edit().putBoolean(KEY_CHANNEL_NUM, value).apply()
|
||||
|
||||
var time: Boolean
|
||||
get() = sp.getBoolean(KEY_TIME, true)
|
||||
set(value) {
|
||||
if (value != this.time) {
|
||||
sp.edit().putBoolean(KEY_TIME, value).apply()
|
||||
listener?.onSharedPreferenceChanged(KEY_TIME)
|
||||
}
|
||||
}
|
||||
|
||||
var bootStartup: Boolean
|
||||
get() = sp.getBoolean(KEY_BOOT_STARTUP, false)
|
||||
set(value) = sp.edit().putBoolean(KEY_BOOT_STARTUP, value).apply()
|
||||
|
||||
var grid: Boolean
|
||||
get() = sp.getBoolean(KEY_GRID, false)
|
||||
set(value) {
|
||||
if (value != this.grid) {
|
||||
sp.edit().putBoolean(KEY_GRID, value).apply()
|
||||
listener?.onSharedPreferenceChanged(KEY_GRID)
|
||||
}
|
||||
}
|
||||
|
||||
var itemPosition: Int
|
||||
get() = sp.getInt(KEY_POSITION, 0)
|
||||
set(value) = sp.edit().putInt(KEY_POSITION, value).apply()
|
||||
|
||||
var guid: String
|
||||
get() = sp.getString(KEY_GUID, "") ?: ""
|
||||
set(value) = sp.edit().putString(KEY_GUID, value).apply()
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.marginEnd
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.lizongying.mytv.api.YSP
|
||||
import com.lizongying.mytv.databinding.SettingBinding
|
||||
|
||||
|
||||
class SettingFragment : DialogFragment() {
|
||||
|
||||
private var _binding: SettingBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var updateManager: UpdateManager
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
dialog?.window?.apply {
|
||||
addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
attributes.layoutInDisplayCutoutMode =
|
||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||
setAttributes(attributes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val context = requireContext() // It‘s safe to get context here.
|
||||
_binding = SettingBinding.inflate(inflater, container, false)
|
||||
binding.versionName.text = "当前版本: v${context.appVersionName}"
|
||||
binding.version.text = "https://github.com/lizongying/my-tv"
|
||||
|
||||
binding.switchChannelReversal.run {
|
||||
isChecked = SP.channelReversal
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
SP.channelReversal = isChecked
|
||||
(activity as MainActivity).settingDelayHide()
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchChannelNum.run {
|
||||
isChecked = SP.channelNum
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
SP.channelNum = isChecked
|
||||
(activity as MainActivity).settingDelayHide()
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchTime.run {
|
||||
isChecked = SP.time
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
SP.time = isChecked
|
||||
(activity as MainActivity).settingDelayHide()
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchBootStartup.run {
|
||||
isChecked = SP.bootStartup
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
SP.bootStartup = isChecked
|
||||
(activity as MainActivity).settingDelayHide()
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchGrid.run {
|
||||
isChecked = SP.grid
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
SP.grid = isChecked
|
||||
(activity as MainActivity).settingDelayHide()
|
||||
}
|
||||
}
|
||||
|
||||
binding.checkVersion.setOnClickListener {
|
||||
(activity as MainActivity).settingDelayHide()
|
||||
requestInstallPermissions()
|
||||
}
|
||||
|
||||
binding.clear.setOnClickListener {
|
||||
(requireActivity() as MainActivity).syncTime()
|
||||
SP.guid = ""
|
||||
YSP.getGuid()
|
||||
}
|
||||
|
||||
val application = requireActivity().applicationContext as MyTVApplication
|
||||
val textSize = application.px2PxFont(binding.switchChannelReversal.textSize)
|
||||
|
||||
binding.content.layoutParams.width =
|
||||
application.px2Px(binding.content.layoutParams.width)
|
||||
binding.content.setPadding(
|
||||
application.px2Px(binding.content.paddingLeft),
|
||||
application.px2Px(binding.content.paddingTop),
|
||||
application.px2Px(binding.content.paddingRight),
|
||||
application.px2Px(binding.content.paddingBottom)
|
||||
)
|
||||
binding.name.textSize = application.px2PxFont(binding.name.textSize)
|
||||
binding.version.textSize = textSize
|
||||
val layoutParamsVersion = binding.version.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsVersion.topMargin = application.px2Px(binding.version.marginTop)
|
||||
binding.version.layoutParams = layoutParamsVersion
|
||||
|
||||
binding.checkVersion.textSize = textSize
|
||||
val layoutParamsCheckVersion =
|
||||
binding.checkVersion.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsCheckVersion.marginEnd = application.px2Px(binding.checkVersion.marginEnd)
|
||||
binding.checkVersion.layoutParams = layoutParamsCheckVersion
|
||||
|
||||
binding.versionName.textSize = textSize
|
||||
|
||||
binding.clear.textSize = textSize
|
||||
binding.exit.textSize = textSize
|
||||
|
||||
val layoutParamsChannelSwitch =
|
||||
binding.switchChannelReversal.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsChannelSwitch.topMargin =
|
||||
application.px2Px(binding.switchChannelReversal.marginTop)
|
||||
|
||||
binding.switchChannelReversal.textSize = textSize
|
||||
binding.switchChannelReversal.layoutParams = layoutParamsChannelSwitch
|
||||
|
||||
binding.switchChannelNum.textSize = textSize
|
||||
binding.switchChannelNum.layoutParams = layoutParamsChannelSwitch
|
||||
|
||||
binding.switchTime.textSize = textSize
|
||||
binding.switchTime.layoutParams = layoutParamsChannelSwitch
|
||||
|
||||
binding.switchBootStartup.textSize = textSize
|
||||
binding.switchBootStartup.layoutParams = layoutParamsChannelSwitch
|
||||
|
||||
binding.switchGrid.textSize = textSize
|
||||
binding.switchGrid.layoutParams = layoutParamsChannelSwitch
|
||||
|
||||
binding.appreciate.layoutParams.width =
|
||||
application.px2Px(binding.appreciate.layoutParams.width)
|
||||
|
||||
binding.exit.setOnClickListener {
|
||||
requireActivity().finishAffinity()
|
||||
}
|
||||
|
||||
// val myViewModel = application.myViewModel
|
||||
// application.myViewModel.downloadProgress.observe(viewLifecycleOwner) { _ ->
|
||||
// val downloadProgress = myViewModel.downloadProgress.value
|
||||
// if (downloadProgress != null) {
|
||||
// if (downloadProgress == 100) {
|
||||
// binding.progressBar.visibility = GONE
|
||||
// } else {
|
||||
// if (!binding.progressBar.isVisible) {
|
||||
// binding.progressBar.visibility = VISIBLE
|
||||
// }
|
||||
// binding.progressBar.progress = downloadProgress
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
updateManager = UpdateManager(context, context.appVersionCode)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun requestInstallPermissions() {
|
||||
val context = requireContext()
|
||||
val permissionsList: MutableList<String> = ArrayList()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !context.packageManager.canRequestPackageInstalls()) {
|
||||
permissionsList.add(Manifest.permission.REQUEST_INSTALL_PACKAGES)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
ContextCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
permissionsList.add(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
ContextCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
permissionsList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
|
||||
if (permissionsList.isNotEmpty()) {
|
||||
ActivityCompat.requestPermissions(
|
||||
requireActivity(),
|
||||
permissionsList.toTypedArray<String>(),
|
||||
PERMISSIONS_REQUEST_CODE
|
||||
)
|
||||
} else {
|
||||
updateManager.checkAndUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == PERMISSIONS_REQUEST_CODE) {
|
||||
var allPermissionsGranted = true
|
||||
for (result in grantResults) {
|
||||
if (result != PackageManager.PERMISSION_GRANTED) {
|
||||
allPermissionsGranted = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (allPermissionsGranted) {
|
||||
updateManager.checkAndUpdate()
|
||||
} else {
|
||||
Toast.makeText(context, "权限授权失败", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "SettingFragment"
|
||||
const val PERMISSIONS_REQUEST_CODE = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class TV(
|
||||
var id: Int = 0,
|
||||
var title: String,
|
||||
var videoUrl: List<String>,
|
||||
var videoIndex: Int = 0,
|
||||
var logo: String = "",
|
||||
var pid: String = "",
|
||||
var sid: String = "",
|
||||
var programId: String = "",
|
||||
) : Serializable {
|
||||
|
||||
override fun toString(): String {
|
||||
return "TV{" +
|
||||
"id=" + id +
|
||||
", title='" + title + '\'' +
|
||||
", videoUrl='" + videoUrl + '\'' +
|
||||
", videoIndex='" + videoIndex + '\'' +
|
||||
", logo='" + logo + '\'' +
|
||||
", pid='" + pid + '\'' +
|
||||
", sid='" + sid + '\'' +
|
||||
", programId='" + programId + '\'' +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
object TVList {
|
||||
val list: Map<String, List<TV>> by lazy {
|
||||
setupTV()
|
||||
}
|
||||
|
||||
private var mappingLogo = mapOf(
|
||||
"CCTV4K 超高清" to "https://resources.yangshipin.cn/assets/oms/image/202306/3e9d06fd7244d950df5838750f1c6ac3456e172b51caca2c16d2282125b111e8.png?imageMogr2/format/webp",
|
||||
"CCTV8K 超高清" to "https://static.wikia.nocookie.net/logos/images/6/69/CCTV8K.png/revision/latest/scale-to-width-down/120?cb=20230104110835&path-prefix=vi",
|
||||
"CCTV1 综合" to "https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp",
|
||||
"CCTV2 财经" to "https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp",
|
||||
"CCTV3 综艺" to "https://resources.yangshipin.cn/assets/oms/image/202306/7b7a65c712450da3deb6ca66fbacf4f9aee00d3f20bd80eafb5ada01ec63eb3a.png?imageMogr2/format/webp",
|
||||
"CCTV4 中文国际" to "https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp",
|
||||
"CCTV5 体育" to "https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp",
|
||||
"CCTV5+ 体育赛事" to "https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp",
|
||||
"CCTV6 电影" to "https://resources.yangshipin.cn/assets/oms/image/202306/741515efda91f03f455df8a7da4ee11fa9329139c276435cf0a9e2af398d5bf2.png?imageMogr2/format/webp",
|
||||
"CCTV7 国防军事" to "https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp",
|
||||
"CCTV8 电视剧" to "https://resources.yangshipin.cn/assets/oms/image/202306/ad51de94426a0ba039e6dd6a8534ea98ecc813a6176bde87b4f18cc34d6d7590.png?imageMogr2/format/webp",
|
||||
"CCTV9 记录" to "https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp",
|
||||
"CCTV10 科教" to "https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp",
|
||||
"CCTV11 戏曲" to "https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp",
|
||||
"CCTV12 社会与法" to "https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp",
|
||||
"CCTV13 新闻" to "https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp",
|
||||
"CCTV14 少儿" to "https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp",
|
||||
"CCTV15 音乐" to "https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp",
|
||||
"CCTV16 奥林匹克" to "https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp",
|
||||
"CCTV17 农业农村" to "https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp",
|
||||
"CGTN" to "https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp",
|
||||
"CGTN 法语频道" to "https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp",
|
||||
"CGTN 俄语频道" to "https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp",
|
||||
"CGTN 阿拉伯语频道" to "https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp",
|
||||
"CGTN 西班牙语频道" to "https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp",
|
||||
"CGTN 纪录频道" to "https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp",
|
||||
"风云剧场" to "https://resources.yangshipin.cn/assets/oms/image/202306/4d549e53e6d0f632d5a633d1945280797b153e588f919221a07faa869812cc89.png?imageMogr2/format/webp",
|
||||
"第一剧场" to "https://resources.yangshipin.cn/assets/oms/image/202306/a556bd7d93ce65e18f243a8892b5604f4faa994a4897315914216a710a706208.png?imageMogr2/format/webp",
|
||||
"怀旧剧场" to "https://resources.yangshipin.cn/assets/oms/image/202306/5661bd04fecdb6e899f801147a22ab5d3a475bf2b62e30aec2c0023190ebc9b1.png?imageMogr2/format/webp",
|
||||
"世界地理" to "https://resources.yangshipin.cn/assets/oms/image/202306/bb3c6c9e145d698137f5bb64a582021a01b51344b929003630eb769ea65832a9.png?imageMogr2/format/webp",
|
||||
"风云音乐" to "https://resources.yangshipin.cn/assets/oms/image/202306/bbf1d024c5228b8dd128b0e3cb1717d173fab4ee84c3a4c8a57b1a215362ca3b.png?imageMogr2/format/webp",
|
||||
"兵器科技" to "https://resources.yangshipin.cn/assets/oms/image/202306/4c6b6a6d3839889f34d33db3c2f80233b26b74d3489b393487635f8704e70796.png?imageMogr2/format/webp",
|
||||
"风云足球" to "https://resources.yangshipin.cn/assets/oms/image/202306/cd1e2bb52b06a991de168733e5ff0f1d85adc8042d40c8f393f723543e5dd08a.png?imageMogr2/format/webp",
|
||||
"高尔夫网球" to "https://resources.yangshipin.cn/assets/oms/image/202306/cdd1b31ede7a5ad049ed53d9a072422f829e72dd062ed2c19e077fdd01699071.png?imageMogr2/format/webp",
|
||||
"女性时尚" to "https://resources.yangshipin.cn/assets/oms/image/202306/fa28955ce8b2539d728bf4c6a13a46ff57ad76eae46627f7bcfb1ed8a613d3fc.png?imageMogr2/format/webp",
|
||||
"央视文化精品" to "https://resources.yangshipin.cn/assets/oms/image/202306/14ac5ce40482cacd3d4b37435222bfe86af2b452a2f04ecbfc1d13d76edd7c57.png?imageMogr2/format/webp",
|
||||
"央视台球" to "https://resources.yangshipin.cn/assets/oms/image/202306/10e14a92478011aa6c3c8562e62127f3b1908e29fcd78e4b2b24b9e6d3ec2fbc.png?imageMogr2/format/webp",
|
||||
"电视指南" to "https://resources.yangshipin.cn/assets/oms/image/202306/244d72c0eb1615ed7d51c2f5db5a67f306aa3f58c05bc2d34de3aa7e956dc8c9.png?imageMogr2/format/webp",
|
||||
"卫生健康" to "https://resources.yangshipin.cn/assets/oms/image/202306/54a6863656fdfd8f803be193ddf22441c5000a108833889816fd2d8911715ce8.png?imageMogr2/format/webp",
|
||||
"东方卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp",
|
||||
"湖南卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp",
|
||||
"湖北卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp",
|
||||
"辽宁卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/ac4ed6058a87c101ae7147ebc38905d0cae047fb73fd277ee5049b84f52bda36.png?imageMogr2/format/webp",
|
||||
"江苏卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/380ad685c0c1d5b2c902246b8d2df6d3f9b45e2837abcfe493075bbded597a31.png?imageMogr2/format/webp",
|
||||
"江西卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/3c760d0d00463855890e8a1864ea4a6b6dd66b90c29b4ac714a4b17c16519871.png?imageMogr2/format/webp",
|
||||
"山东卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/22d403f07a7cf5410b3ad3ddb65a11aa229a32475fac213f5344c9f0ec330ca1.png?imageMogr2/format/webp",
|
||||
"广东卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/28886880a4dc0f06fb7e0a528a1def0591d61a65870e29176ede0cc92033bbfd.png?imageMogr2/format/webp",
|
||||
"广西卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/54b7e97cb816bb223fe05f3fc44da2c7820eb66e8550c19d23100f2c414ecc38.png?imageMogr2/format/webp",
|
||||
"重庆卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/657651f411de2673d1770d9a78b44c1265704f7468cc41d4be7f51d630768494.png?imageMogr2/format/webp",
|
||||
"河南卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/74925962148a6d31c85808b6cd4e444c2a54bab393d2c5fc85e960b50e22fa86.png?imageMogr2/format/webp",
|
||||
"河北卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/d545becdc81c60197b08c7f47380705e4665ed3fe55efc8b855e486f6e655378.png?imageMogr2/format/webp",
|
||||
"贵州卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/4eb45f4781d33d872af027dc01c941559aab55667dd99cc5c22bef7037807b13.png?imageMogr2/format/webp",
|
||||
"北京卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/f4f23633c578beea49a3841d88d3490100f029ee349059fa532869db889872c5.png?imageMogr2/format/webp",
|
||||
"黑龙江卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/d8273ae9be698ce2db21f5b886ecac95a73429593f93713c60ed8c12c38bf0d3.png?imageMogr2/format/webp",
|
||||
"浙江卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/a66c836bd98ba3e41a2e9a570d4b9c50dedc6839e9de333e2e78212ad505f37e.png?imageMogr2/format/webp",
|
||||
"安徽卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/f35fa04b51b1ee4984b03578b65403570868ebca03c6c01e11b097f999a58d9b.png?imageMogr2/format/webp",
|
||||
"深圳卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/d59fec04c902e3581c617136d02d4b9b8c4cbe64272781ddd3525e80c823edb7.png?imageMogr2/format/webp",
|
||||
"四川卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp",
|
||||
"东南卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp",
|
||||
"海南卫视" to "https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp",
|
||||
)
|
||||
private var mappingEPG = mapOf(
|
||||
"CCTV4K 超高清" to "600002264",
|
||||
"CCTV8K 超高清" to "600156816",
|
||||
"CCTV1 综合" to "600001859",
|
||||
"CCTV2 财经" to "600001800",
|
||||
"CCTV3 综艺" to "600001801",
|
||||
"CCTV4 中文国际" to "600001814",
|
||||
"CCTV5 体育" to "600001818",
|
||||
"CCTV5+ 体育赛事" to "600001817",
|
||||
"CCTV6 电影" to "600001802",
|
||||
"CCTV7 国防军事" to "600004092",
|
||||
"CCTV8 电视剧" to "600001803",
|
||||
"CCTV9 记录" to "600004078",
|
||||
"CCTV10 科教" to "600001805",
|
||||
"CCTV11 戏曲" to "600001806",
|
||||
"CCTV12 社会与法" to "600001807",
|
||||
"CCTV13 新闻" to "600001811",
|
||||
"CCTV14 少儿" to "600001809",
|
||||
"CCTV15 音乐" to "600001815",
|
||||
"CCTV16 奥林匹克" to "600098637",
|
||||
"CCTV17 农业农村" to "600001810",
|
||||
"CGTN" to "600014550",
|
||||
"CGTN 法语频道" to "600084704",
|
||||
"CGTN 俄语频道" to "600084758",
|
||||
"CGTN 阿拉伯语频道" to "600084782",
|
||||
"CGTN 西班牙语频道" to "600084744",
|
||||
"CGTN 记录频道" to "600084781",
|
||||
"风云剧场" to "600099658",
|
||||
"第一剧场" to "600099655",
|
||||
"怀旧剧场" to "600099620",
|
||||
"世界地理" to "600099637",
|
||||
"风云音乐" to "600099660",
|
||||
"兵器科技" to "600099649",
|
||||
"风云足球" to "600099636",
|
||||
"高尔夫网球" to "600099659",
|
||||
"女性时尚" to "600099650",
|
||||
"央视文化精品" to "600099653",
|
||||
"央视台球" to "600099652",
|
||||
"电视指南" to "600099656",
|
||||
"卫生健康" to "600099651",
|
||||
"东方卫视" to "600002483",
|
||||
"湖南卫视" to "600002475",
|
||||
"湖北卫视" to "600002508",
|
||||
"辽宁卫视" to "600002505",
|
||||
"江苏卫视" to "600002521",
|
||||
"江西卫视" to "600002503",
|
||||
"山东卫视" to "600002513",
|
||||
"广东卫视" to "600002485",
|
||||
"广西卫视" to "600002509",
|
||||
"重庆卫视" to "600002531",
|
||||
"河南卫视" to "600002525",
|
||||
"河北卫视" to "600002493",
|
||||
"贵州卫视" to "600002490",
|
||||
"北京卫视" to "600002309",
|
||||
"黑龙江卫视" to "600002498",
|
||||
"浙江卫视" to "600002520",
|
||||
"安徽卫视" to "600002532",
|
||||
"深圳卫视" to "600002481",
|
||||
"四川卫视" to "600002516",
|
||||
"东南卫视" to "600002484",
|
||||
"海南卫视" to "600002506",
|
||||
)
|
||||
private var mappingVideo = mapOf(
|
||||
"CCTV4K 超高清" to arrayOf("600002264", "2000266303"),
|
||||
"CCTV8K 超高清" to arrayOf("600156816", "2020603421"),
|
||||
"CCTV1 综合" to arrayOf("600001859", "2000210103"),
|
||||
"CCTV2 财经" to arrayOf("600001800", "2000203603"),
|
||||
"CCTV3 综艺" to arrayOf("600001801", "2000203803"),
|
||||
"CCTV4 中文国际" to arrayOf("600001814", "2000204803"),
|
||||
"CCTV5 体育" to arrayOf("600001818", "2000205103"),
|
||||
"CCTV5+ 体育赛事" to arrayOf("600001817", "2000204503"),
|
||||
"CCTV6 电影" to arrayOf("600001802", "2000203303"),
|
||||
"CCTV7 国防军事" to arrayOf("600004092", "2000510003"),
|
||||
"CCTV8 电视剧" to arrayOf("600001803", "2000203903"),
|
||||
"CCTV9 记录" to arrayOf("600004078", "2000499403"),
|
||||
"CCTV10 科教" to arrayOf("600001805", "2000203503"),
|
||||
"CCTV11 戏曲" to arrayOf("600001806", "2000204103"),
|
||||
"CCTV12 社会与法" to arrayOf("600001807", "2000202603"),
|
||||
// "CCTV13 新闻" to arrayOf("600001811","2000204603"),
|
||||
"CCTV14 少儿" to arrayOf("600001809", "2000204403"),
|
||||
"CCTV15 音乐" to arrayOf("600001815", "2000205003"),
|
||||
"CCTV16 奥林匹克" to arrayOf("600098637", "2012375003"),
|
||||
"CCTV17 农业农村" to arrayOf("600001810", "2000204203"),
|
||||
"CGTN" to arrayOf("600014550", "2001656803"),
|
||||
"CGTN 法语频道" to arrayOf("600084704", "2010153503"),
|
||||
"CGTN 俄语频道" to arrayOf("600084758", "2010152603"),
|
||||
"CGTN 阿拉伯语频道" to arrayOf("600084782", "2010155203"),
|
||||
"CGTN 西班牙语频道" to arrayOf("600084744", "2010152503"),
|
||||
"CGTN 记录频道" to arrayOf("600084781", "2010155403"),
|
||||
"风云剧场" to arrayOf("600099658", "2012513603"),
|
||||
"第一剧场" to arrayOf("600099655", "2012514403"),
|
||||
"怀旧剧场" to arrayOf("600099620", "2012511203"),
|
||||
"世界地理" to arrayOf("600099637", "2012513303"),
|
||||
"风云音乐" to arrayOf("600099660", "2012514103"),
|
||||
"兵器科技" to arrayOf("600099649", "2012513403"),
|
||||
"风云足球" to arrayOf("600099636", "2012514203"),
|
||||
"高尔夫网球" to arrayOf("600099659", "2012512503"),
|
||||
"女性时尚" to arrayOf("600099650", "2012513903"),
|
||||
"央视文化精品" to arrayOf("600099653", "2012513803"),
|
||||
"央视台球" to arrayOf("600099652", "2012513703"),
|
||||
"电视指南" to arrayOf("600099656", "2012514003"),
|
||||
"卫生健康" to arrayOf("600099651", "2012513503"),
|
||||
"东方卫视" to arrayOf("600002483", "2000292403"),
|
||||
"湖南卫视" to arrayOf("600002475", "2000296203"),
|
||||
"湖北卫视" to arrayOf("600002508", "2000294503"),
|
||||
"辽宁卫视" to arrayOf("600002505", "2000281303"),
|
||||
"江苏卫视" to arrayOf("600002521", "2000295603"),
|
||||
"江西卫视" to arrayOf("600002503", "2000294103"),
|
||||
"山东卫视" to arrayOf("600002513", "2000294803"),
|
||||
"广东卫视" to arrayOf("600002485", "2000292703"),
|
||||
"广西卫视" to arrayOf("600002509", "2000294203"),
|
||||
"重庆卫视" to arrayOf("600002531", "2000297803"),
|
||||
"河南卫视" to arrayOf("600002525", "2000296103"),
|
||||
"河北卫视" to arrayOf("600002493", "2000293403"),
|
||||
"贵州卫视" to arrayOf("600002490", "2000293303"),
|
||||
"北京卫视" to arrayOf("600002309", "2000272103"),
|
||||
"黑龙江卫视" to arrayOf("600002498", "2000293903"),
|
||||
"浙江卫视" to arrayOf("600002520", "2000295503"),
|
||||
"安徽卫视" to arrayOf("600002532", "2000298003"),
|
||||
"深圳卫视" to arrayOf("600002481", "2000292203"),
|
||||
"四川卫视" to arrayOf("600002516", "2000295003"),
|
||||
"东南卫视" to arrayOf("600002484", "2000292503"),
|
||||
"海南卫视" to arrayOf("600002506", "2000291503"),
|
||||
)
|
||||
private var count: Int = 0
|
||||
|
||||
private fun setupTV(): Map<String, List<TV>> {
|
||||
val tvs = """
|
||||
央视频道
|
||||
CCTV4K 超高清,
|
||||
CCTV8K 超高清,
|
||||
CCTV1 综合,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226231/index.m3u8
|
||||
CCTV2 财经,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226195/index.m3u8
|
||||
CCTV3 综艺,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226397/index.m3u8
|
||||
CCTV4 中文国际,http://39.134.24.161/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226191/index.m3u8
|
||||
CCTV5 体育,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226395/index.m3u8
|
||||
CCTV5+ 体育赛事,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226221/index.m3u8
|
||||
CCTV6 电影,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226393/index.m3u8
|
||||
CCTV7 国防军事,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226192/index.m3u8
|
||||
CCTV8 电视剧,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226391/index.m3u8
|
||||
CCTV9 记录,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8
|
||||
CCTV10 科教,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226189/index.m3u8
|
||||
CCTV11 戏曲,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226240/index.m3u8
|
||||
CCTV12 社会与法,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226190/index.m3u8
|
||||
CCTV13 新闻,https://live-play.cctvnews.cctv.com/cctv/merge_cctv13.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226233/index.m3u8
|
||||
CCTV14 少儿,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226193/index.m3u8
|
||||
CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8
|
||||
CCTV16 奥林匹克,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8
|
||||
CCTV17 农业农村,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226198/index.m3u8
|
||||
风云剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8
|
||||
第一剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226959/index.m3u8
|
||||
怀旧剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226972/index.m3u8
|
||||
世界地理,
|
||||
风云音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226953/index.m3u8
|
||||
兵器科技,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226975/index.m3u8
|
||||
风云足球,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226984/index.m3u8
|
||||
高尔夫网球,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226978/index.m3u8
|
||||
女性时尚,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226969/index.m3u8
|
||||
央视文化精品,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226981/index.m3u8
|
||||
央视台球,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226956/index.m3u8
|
||||
电视指南,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226987/index.m3u8
|
||||
卫生健康,
|
||||
地方频道
|
||||
东方卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226217/index.m3u8
|
||||
湖南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226307/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226211/index.m3u8
|
||||
湖北卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226477/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226194/index.m3u8
|
||||
辽宁卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226546/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226201/index.m3u8
|
||||
江苏卫视,http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226200/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226200/index.m3u8
|
||||
江西卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226344/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225764/index.m3u8
|
||||
山东卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226209/index.m3u8
|
||||
广东卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226216/index.m3u8
|
||||
广西卫视,http://live.gxrb.com.cn/tv/gxtvlive03/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225770/index.m3u8
|
||||
重庆卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226409/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226202/index.m3u8
|
||||
河南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226480/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225767/index.m3u8
|
||||
河北卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226406/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225750/index.m3u8
|
||||
贵州卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226474/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225793/index.m3u8
|
||||
北京卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225728/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226222/index.m3u8
|
||||
黑龙江卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226327/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226215/index.m3u8
|
||||
浙江卫视,http://hw-m-l.cztv.com/channels/lantian/channel01/1080p.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226199/index.m3u8
|
||||
安徽卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226391/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226203/index.m3u8
|
||||
深圳卫视,http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226205/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226205/index.m3u8
|
||||
四川卫视,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8;http://39.134.24.166/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225768/index.m3u8
|
||||
东南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226341/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225766/index.m3u8
|
||||
海南卫视,http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226465/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225769/index.m3u8
|
||||
国际频道
|
||||
CGTN,http://live.cgtn.com/1000/prog_index.m3u8
|
||||
CGTN 法语频道,https://livefr.cgtn.com/1000f/prog_index.m3u8
|
||||
CGTN 俄语频道,http://liveru.cgtn.com/1000r/prog_index.m3u8
|
||||
CGTN 阿拉伯语频道,http://livear.cgtn.com/1000a/prog_index.m3u8
|
||||
CGTN 西班牙语频道,http://livees.cgtn.com/500e/prog_index.m3u8;http://livees.cgtn.com/1000e/prog_index.m3u8
|
||||
CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8
|
||||
""".trimIndent()
|
||||
|
||||
val map: MutableMap<String, MutableList<TV>> = mutableMapOf()
|
||||
|
||||
var channel = ""
|
||||
for (i in tvs.split("\n")) {
|
||||
if (i.trim() == "") {
|
||||
continue
|
||||
}
|
||||
if (!i.contains(",")) {
|
||||
channel = i.trim()
|
||||
if (channel == "移动专区") {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
val p = i.split(",")
|
||||
val titleMap = map[channel] ?: mutableListOf()
|
||||
|
||||
val tv = TV(
|
||||
count,
|
||||
p[0],
|
||||
p[1].split(";").map { it.trim() }
|
||||
)
|
||||
|
||||
val logo = mappingLogo[tv.title]
|
||||
if (logo != null && logo != "") {
|
||||
tv.logo = logo
|
||||
}
|
||||
val programId = mappingEPG[tv.title]
|
||||
if (programId != null && programId != "") {
|
||||
tv.programId = programId
|
||||
}
|
||||
|
||||
val video = mappingVideo[tv.title]
|
||||
if (video != null) {
|
||||
tv.pid = video[0]
|
||||
tv.sid = video[1]
|
||||
}
|
||||
|
||||
titleMap.add(tv)
|
||||
map[channel] = titleMap
|
||||
count++
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.marginEnd
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.lizongying.mytv.Utils.getDateFormat
|
||||
import com.lizongying.mytv.databinding.TimeBinding
|
||||
|
||||
class TimeFragment : Fragment() {
|
||||
private var _binding: TimeBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val handler = Handler()
|
||||
private val delay: Long = 1000
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = TimeBinding.inflate(inflater, container, false)
|
||||
|
||||
val application = requireActivity().applicationContext as MyTVApplication
|
||||
|
||||
binding.time.layoutParams.width = application.px2Px(binding.time.layoutParams.width)
|
||||
binding.time.layoutParams.height = application.px2Px(binding.time.layoutParams.height)
|
||||
|
||||
val layoutParams = binding.time.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.topMargin = application.px2Px(binding.time.marginTop)
|
||||
layoutParams.marginEnd = application.px2Px(binding.time.marginEnd)
|
||||
binding.time.layoutParams = layoutParams
|
||||
|
||||
binding.content.textSize = application.px2PxFont(binding.content.textSize)
|
||||
binding.channel.textSize = application.px2PxFont(binding.channel.textSize)
|
||||
|
||||
binding.main.layoutParams.width = application.shouldWidthPx()
|
||||
binding.main.layoutParams.height = application.shouldHeightPx()
|
||||
|
||||
if (SP.time) {
|
||||
handler.removeCallbacks(showRunnable)
|
||||
handler.postDelayed(showRunnable, 0)
|
||||
} else {
|
||||
handler.removeCallbacks(showRunnable)
|
||||
}
|
||||
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
super.onHiddenChanged(hidden)
|
||||
if (!hidden) {
|
||||
handler.removeCallbacks(showRunnable)
|
||||
handler.postDelayed(showRunnable, 0)
|
||||
} else {
|
||||
handler.removeCallbacks(showRunnable)
|
||||
}
|
||||
}
|
||||
|
||||
private val showRunnable: Runnable = Runnable {
|
||||
run {
|
||||
if (_binding == null) {
|
||||
return@Runnable
|
||||
}
|
||||
binding.content.text = getDateFormat("HH:mm")
|
||||
handler.postDelayed(showRunnable, delay)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
handler.removeCallbacks(showRunnable)
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "TimeFragment"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
import com.lizongying.mytv.api.ApiClient
|
||||
import com.lizongying.mytv.requests.ReleaseRequest
|
||||
import com.lizongying.mytv.requests.ReleaseResponse
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class UpdateManager(
|
||||
private var context: Context,
|
||||
private var versionCode: Long
|
||||
) :
|
||||
ConfirmationFragment.ConfirmationListener {
|
||||
|
||||
private var releaseRequest = ReleaseRequest()
|
||||
private var release: ReleaseResponse? = null
|
||||
|
||||
fun checkAndUpdate() {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
var text = "版本获取失败"
|
||||
var update = false
|
||||
try {
|
||||
release = releaseRequest.getRelease()
|
||||
val code = release?.version_code
|
||||
if (code != null) {
|
||||
if (code.toLong() >= versionCode) {
|
||||
text = "最新版本:${release?.version_name}"
|
||||
update = true
|
||||
} else {
|
||||
text = "已是最新版本,不需要更新"
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error occurred: ${e.message}", e)
|
||||
}
|
||||
updateUI(text, update)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUI(text: String, update: Boolean) {
|
||||
val dialog = ConfirmationFragment(this@UpdateManager, text, update)
|
||||
dialog.show((context as FragmentActivity).supportFragmentManager, TAG)
|
||||
}
|
||||
|
||||
private fun startDownload(context: Context, release: ReleaseResponse) {
|
||||
val versionName = release.version_name
|
||||
val apkName = "my-tv"
|
||||
val apkFileName = "$apkName-${release.version_name}.apk"
|
||||
val downloadUrl =
|
||||
"${ApiClient.DOWNLOAD_HOST}${release.version_name}/$apkName-${release.version_name}.apk"
|
||||
Log.i(TAG, "downloadUrl: $downloadUrl")
|
||||
val downloadRequest = OneTimeWorkRequestBuilder<UpdateWorker>()
|
||||
.setInputData(
|
||||
workDataOf(
|
||||
"VERSION_NAME" to versionName,
|
||||
"APK_FILENAME" to apkFileName,
|
||||
"DOWNLOAD_URL" to downloadUrl
|
||||
)
|
||||
)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context).enqueue(downloadRequest)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "UpdateManager"
|
||||
}
|
||||
|
||||
override fun onConfirm() {
|
||||
Log.i(TAG, "onConfirm $release")
|
||||
release?.let { startDownload(context, it) }
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.app.DownloadManager
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.lizongying.mytv.models.MyViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class UpdateWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
||||
|
||||
private lateinit var notificationManager: NotificationManager
|
||||
private lateinit var notificationBuilder: NotificationCompat.Builder
|
||||
|
||||
private lateinit var myViewModel: MyViewModel
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val downloadUrl = inputData.getString("DOWNLOAD_URL") ?: return Result.failure()
|
||||
val apkFileName = inputData.getString("APK_FILENAME") ?: return Result.failure()
|
||||
val versionName = inputData.getString("VERSION_NAME") ?: return Result.failure()
|
||||
|
||||
showNotification(applicationContext)
|
||||
|
||||
val app = applicationContext as MyTVApplication
|
||||
myViewModel = app.myViewModel
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
downloadAndInstall(applicationContext, downloadUrl, apkFileName, versionName)
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
Result.failure()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showNotification(context: Context) {
|
||||
notificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationBuilder = NotificationCompat.Builder(context, "download_channel").apply {
|
||||
setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
setContentTitle("Downloading Update")
|
||||
setContentText("Download in progress")
|
||||
priority = NotificationCompat.PRIORITY_LOW
|
||||
setOngoing(true)
|
||||
setProgress(100, 0, false)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(
|
||||
"download_channel",
|
||||
"Download Updates",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
notificationManager.notify(1, notificationBuilder.build())
|
||||
}
|
||||
|
||||
private fun downloadAndInstall(
|
||||
context: Context,
|
||||
downloadUrl: String,
|
||||
apkFileName: String,
|
||||
versionName: String
|
||||
) {
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.mkdirs()
|
||||
Log.i(TAG, "save dir ${Environment.DIRECTORY_DOWNLOADS}")
|
||||
val request = DownloadManager.Request(Uri.parse(downloadUrl)).apply {
|
||||
setTitle("${context.resources.getString(R.string.app_name)} $versionName Downloading")
|
||||
setDescription("Downloading the latest version of the app")
|
||||
setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, apkFileName)
|
||||
|
||||
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
// setAllowedOverRoaming(false)
|
||||
setMimeType("application/vnd.android.package-archive")
|
||||
}
|
||||
|
||||
val manager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val downloadId = manager.enqueue(request)
|
||||
|
||||
var downloading = true
|
||||
while (downloading) {
|
||||
val query = DownloadManager.Query().setFilterById(downloadId)
|
||||
val cursor = manager.query(query)
|
||||
if (cursor.moveToFirst()) {
|
||||
val bytesDownloaded =
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
|
||||
val bytesTotal =
|
||||
cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
|
||||
|
||||
if (bytesTotal > 0) {
|
||||
val progress = (bytesDownloaded * 100L / bytesTotal).toInt()
|
||||
Log.i(TAG, "progress $progress")
|
||||
// myViewModel.setDownloadProgress(progress)
|
||||
notificationBuilder.setProgress(100, progress, false)
|
||||
notificationManager.notify(1, notificationBuilder.build())
|
||||
}
|
||||
|
||||
when (cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))) {
|
||||
DownloadManager.STATUS_SUCCESSFUL -> downloading = false
|
||||
DownloadManager.STATUS_FAILED -> {
|
||||
downloading = false
|
||||
throw Exception("Download failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor.close()
|
||||
}
|
||||
|
||||
notificationBuilder.setContentText("Download complete")
|
||||
.setProgress(0, 0, false)
|
||||
.setOngoing(false)
|
||||
notificationManager.notify(1, notificationBuilder.build())
|
||||
|
||||
val apkFile = File(
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||
apkFileName
|
||||
)
|
||||
|
||||
if (apkFile.exists()) {
|
||||
val apkUri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
FileProvider.getUriForFile(context, context.packageName + ".fileprovider", apkFile)
|
||||
.apply {
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
}
|
||||
} else {
|
||||
Uri.parse("file://${apkFile.absolutePath}")
|
||||
// Uri.fromFile(apkFile)
|
||||
}
|
||||
// val apkUri = Uri.parse("file://$apkFile")
|
||||
Log.i(TAG, "apkUri $apkUri")
|
||||
val installIntent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(apkUri, "application/vnd.android.package-archive")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
context.startActivity(installIntent)
|
||||
} else {
|
||||
Log.e(TAG, "APK file does not exist!")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "UpdateWorker"
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,259 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import com.google.gson.Gson
|
||||
import com.lizongying.mytv.api.TimeResponse
|
||||
import com.lizongying.mytv.requests.Request
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
object Utils {
|
||||
private var between: Long = 0
|
||||
|
||||
private var a: String = ""
|
||||
var b: String = ""
|
||||
private var c: String = ""
|
||||
|
||||
private var listener: Request.RequestListener? = null
|
||||
|
||||
fun getDateFormat(format: String): String {
|
||||
return SimpleDateFormat(format, Locale.CHINA).format(Date())
|
||||
return SimpleDateFormat(
|
||||
format,
|
||||
Locale.CHINA
|
||||
).format(Date(System.currentTimeMillis() - between))
|
||||
}
|
||||
|
||||
fun getDateTimestamp(): Long {
|
||||
return Date().time / 1000
|
||||
return (System.currentTimeMillis() - between) / 1000
|
||||
}
|
||||
|
||||
fun setBetween(currentTimeMillis: Long) {
|
||||
between = System.currentTimeMillis() - currentTimeMillis
|
||||
}
|
||||
|
||||
suspend fun init() {
|
||||
try {
|
||||
val currentTimeMillis = getTimestampFromServer()
|
||||
if (currentTimeMillis > 0) {
|
||||
between = System.currentTimeMillis() - currentTimeMillis
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("Failed to retrieve timestamp from server: ${e.message}")
|
||||
}
|
||||
|
||||
var x = ""
|
||||
try {
|
||||
x = getNothing()
|
||||
} catch (e: Exception) {
|
||||
x = ""
|
||||
println("a ${e.message}")
|
||||
}
|
||||
|
||||
if (x != "") {
|
||||
try {
|
||||
x = getNothing2(x)
|
||||
} catch (e: Exception) {
|
||||
x = ""
|
||||
println("b ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// if (x != "") {
|
||||
// try {
|
||||
// getNothing3(x)
|
||||
// } catch (e: Exception) {
|
||||
// println("b ${e.message}")
|
||||
// }
|
||||
// }
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
listener?.onRequestFinished(null)
|
||||
}
|
||||
}
|
||||
|
||||
class RetryInterceptor(private val maxRetry: Int) : Interceptor {
|
||||
@Throws(IOException::class)
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
var response = chain.proceed(request)
|
||||
var tryCount = 0
|
||||
while (!response.isSuccessful && tryCount < maxRetry) {
|
||||
tryCount++
|
||||
response = chain.proceed(request)
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从服务器获取时间戳
|
||||
* @return Long 时间戳
|
||||
*/
|
||||
private suspend fun getTimestampFromServer(): Long {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val client = okhttp3.OkHttpClient.Builder()
|
||||
.addInterceptor(RetryInterceptor(3))
|
||||
.build()
|
||||
val request = okhttp3.Request.Builder()
|
||||
.url("https://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp")
|
||||
.build()
|
||||
try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
val string = response.body()?.string()
|
||||
Gson().fromJson(string, TimeResponse::class.java).data.t.toLong()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw IOException("Error during network request", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val regex = Regex("""chunk-vendors\.([^.]+)\.js""")
|
||||
|
||||
private suspend fun getNothing(): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val client = okhttp3.OkHttpClient.Builder()
|
||||
.addInterceptor(RetryInterceptor(3))
|
||||
.build()
|
||||
val request = okhttp3.Request.Builder()
|
||||
.url("https://www.yangshipin.cn")
|
||||
.build()
|
||||
try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
val string = response.body()?.string()
|
||||
|
||||
val matchResult = string?.let { regex.find(it) }
|
||||
var x = ""
|
||||
if (matchResult != null) {
|
||||
x = matchResult.groupValues[1]
|
||||
}
|
||||
x
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw IOException("Error during network request", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val regex2 = Regex(""""ysp_tx"[^:]+:"([^"]+)[^:]+:"([^"]+)""")
|
||||
|
||||
private val regex3 = Regex(""""(https[^"]+wasm([^"]+))""")
|
||||
|
||||
private suspend fun getNothing2(x: String): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val client = okhttp3.OkHttpClient.Builder()
|
||||
.addInterceptor(RetryInterceptor(3))
|
||||
.build()
|
||||
|
||||
val request = okhttp3.Request.Builder()
|
||||
.url("https://www.yangshipin.cn/js/chunk-vendors.$x.js")
|
||||
.build()
|
||||
try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
val string = response.body()?.string()
|
||||
|
||||
val matches = string?.let { regex2.findAll(it) }
|
||||
|
||||
var matchResult = matches?.lastOrNull()
|
||||
|
||||
if (matchResult != null) {
|
||||
val (aa, bb) = matchResult.destructured
|
||||
a = aa
|
||||
b = bb
|
||||
}
|
||||
|
||||
matchResult = string?.let { regex3.find(it) }
|
||||
|
||||
if (matchResult != null) {
|
||||
val (cc) = matchResult.destructured
|
||||
c = cc
|
||||
Log.i("", "ccccc $c")
|
||||
}
|
||||
c
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw IOException("Error during network request", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getNothing3(x: String): String {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val client = okhttp3.OkHttpClient.Builder()
|
||||
.addInterceptor(RetryInterceptor(3))
|
||||
.build()
|
||||
|
||||
val request = okhttp3.Request.Builder()
|
||||
.url("https://www.yangshipin.cn/js/chunk-vendors.$x.js")
|
||||
.build()
|
||||
try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
val string = response.body()?.string()
|
||||
|
||||
val matches = string?.let { regex2.findAll(it) }
|
||||
|
||||
var matchResult = matches?.lastOrNull()
|
||||
|
||||
if (matchResult != null) {
|
||||
val (aa, bb) = matchResult.destructured
|
||||
a = aa
|
||||
b = bb
|
||||
}
|
||||
|
||||
matchResult = string?.let { regex3.find(it) }
|
||||
|
||||
if (matchResult != null) {
|
||||
val (cc) = matchResult.destructured
|
||||
c = cc
|
||||
}
|
||||
c
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw IOException("Error during network request", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dpToPx(dp: Float): Int {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp,
|
||||
Resources.getSystem().displayMetrics
|
||||
TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().displayMetrics
|
||||
).toInt()
|
||||
}
|
||||
|
||||
fun dpToPx(dp: Int): Int {
|
||||
// val density = Resources.getSystem().displayMetrics.density
|
||||
// return (dp * density + 0.5f).toInt()
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp.toFloat(),
|
||||
Resources.getSystem().displayMetrics
|
||||
TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), Resources.getSystem().displayMetrics
|
||||
).toInt()
|
||||
}
|
||||
|
||||
fun pxToDp(px: Float): Int {
|
||||
val scale = Resources.getSystem().displayMetrics.density
|
||||
return (px / scale).toInt()
|
||||
}
|
||||
|
||||
fun pxToDp(px: Int): Int {
|
||||
val scale = Resources.getSystem().displayMetrics.density
|
||||
return (px / scale).toInt()
|
||||
}
|
||||
|
||||
fun isTmallDevice() = Build.MANUFACTURER.equals("Tmall", ignoreCase = true)
|
||||
|
||||
fun setRequestListener(listener: Request.RequestListener) {
|
||||
this.listener = listener
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,19 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.google.gson.Gson
|
||||
import com.lizongying.mytv.jce.JceConverterFactory
|
||||
import com.lizongying.mytv.requests.ReleaseService
|
||||
import okhttp3.ConnectionSpec
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.TlsVersion
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.converter.protobuf.ProtoConverterFactory
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManager
|
||||
import javax.net.ssl.X509TrustManager
|
||||
@@ -15,14 +24,20 @@ class ApiClient {
|
||||
private val myUrl = "https://lyrics.run/"
|
||||
private val protoUrl = "https://capi.yangshipin.cn/"
|
||||
private val traceUrl = "https://btrace.yangshipin.cn/"
|
||||
private val trace2Url = "https://aatc-api.yangshipin.cn/"
|
||||
private val tokenUrl = "https://h5access.yangshipin.cn/"
|
||||
private val trace3Url = "https://dtrace.ysp.cctv.cn/"
|
||||
private val jceUrl = "https://jacc.ysp.cctv.cn/"
|
||||
private val fUrl = "https://m.fengshows.com/"
|
||||
|
||||
private var okHttpClient = getUnsafeOkHttpClient()
|
||||
private var gson = Gson()
|
||||
|
||||
val yspApiService: YSPApiService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(yspUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build().create(YSPApiService::class.java)
|
||||
}
|
||||
|
||||
@@ -30,10 +45,18 @@ class ApiClient {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(myUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build().create(YSPTokenService::class.java)
|
||||
}
|
||||
|
||||
val releaseService: ReleaseService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(HOST)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build().create(ReleaseService::class.java)
|
||||
}
|
||||
|
||||
val yspProtoService: YSPProtoService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(protoUrl)
|
||||
@@ -46,10 +69,80 @@ class ApiClient {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(traceUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build().create(YSPBtraceService::class.java)
|
||||
}
|
||||
|
||||
val yspBtraceService2: YSPBtraceService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(trace2Url)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build().create(YSPBtraceService::class.java)
|
||||
}
|
||||
|
||||
val yspToken2Service: YSPApiService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(tokenUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build().create(YSPApiService::class.java)
|
||||
}
|
||||
|
||||
val yspBtraceService3: YSPBtraceService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(trace3Url)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build().create(YSPBtraceService::class.java)
|
||||
}
|
||||
|
||||
val yspJceService: YSPJceService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(jceUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(JceConverterFactory.create())
|
||||
.build().create(YSPJceService::class.java)
|
||||
}
|
||||
|
||||
val fAuthService: FAuthService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(fUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build().create(FAuthService::class.java)
|
||||
}
|
||||
|
||||
private fun enableTls12OnPreLollipop(client: OkHttpClient.Builder): OkHttpClient.Builder {
|
||||
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
|
||||
try {
|
||||
val sc = SSLContext.getInstance("TLSv1.2")
|
||||
|
||||
sc.init(null, null, null)
|
||||
|
||||
// a more robust version is to pass a custom X509TrustManager
|
||||
// as the second parameter and make checkServerTrusted to accept your server.
|
||||
// Credits: https://github.com/square/okhttp/issues/2372#issuecomment-1774955225
|
||||
client.sslSocketFactory(Tls12SocketFactory(sc.socketFactory))
|
||||
|
||||
val cs = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
.tlsVersions(TlsVersion.TLS_1_2)
|
||||
.build()
|
||||
|
||||
val specs: MutableList<ConnectionSpec> = ArrayList()
|
||||
specs.add(cs)
|
||||
specs.add(ConnectionSpec.COMPATIBLE_TLS)
|
||||
specs.add(ConnectionSpec.CLEARTEXT)
|
||||
|
||||
client.connectionSpecs(specs)
|
||||
} catch (exc: java.lang.Exception) {
|
||||
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
|
||||
}
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
private fun getUnsafeOkHttpClient(): OkHttpClient {
|
||||
try {
|
||||
val trustAllCerts: Array<TrustManager> = arrayOf(
|
||||
@@ -75,14 +168,23 @@ class ApiClient {
|
||||
val sslContext = SSLContext.getInstance("SSL")
|
||||
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
|
||||
|
||||
return OkHttpClient.Builder()
|
||||
val proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress("10.0.2.2", 8888))
|
||||
|
||||
val builder = OkHttpClient.Builder()
|
||||
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
|
||||
.hostnameVerifier { _, _ -> true }
|
||||
// .proxy(proxy)
|
||||
.dns(DnsCache())
|
||||
.build()
|
||||
|
||||
return enableTls12OnPreLollipop(builder).build()
|
||||
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val HOST = "https://gitee.com/lizongying/my-tv/raw/"
|
||||
const val DOWNLOAD_HOST = "https://gitee.com/lizongying/my-tv/releases/download/"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSink
|
||||
import java.io.IOException
|
||||
|
||||
data class Auth(
|
||||
val code: Int,
|
||||
val msg: String,
|
||||
val data: AuthData,
|
||||
)
|
||||
|
||||
data class AuthData(
|
||||
val token: String,
|
||||
)
|
||||
|
||||
data class AuthRequest(
|
||||
var data: String,
|
||||
) : RequestBody() {
|
||||
override fun contentType(): MediaType? {
|
||||
return MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8")
|
||||
}
|
||||
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
try {
|
||||
sink.writeUtf8(data)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "$e")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "AuthRequest"
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -1,4 +1,4 @@
|
||||
package com.lizongying.mytv
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -7,8 +7,14 @@ class Encryptor {
|
||||
|
||||
external fun encrypt(t: String, e: String, r: String, n: String, i: String): String
|
||||
|
||||
external fun encrypt2(t: String): String
|
||||
|
||||
external fun hash(data: ByteArray): ByteArray?
|
||||
|
||||
external fun hash2(data: ByteArray): ByteArray?
|
||||
|
||||
external fun hash3(data: ByteArray): ByteArray?
|
||||
|
||||
companion object {
|
||||
init {
|
||||
System.loadLibrary("native")
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
|
||||
data class FAuth(
|
||||
val data: Data,
|
||||
) {
|
||||
data class Data(
|
||||
val live_url: String,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface FAuthService {
|
||||
@GET("api/v3/hub/live/auth-url")
|
||||
fun getAuth(
|
||||
@Header("token") token: String,
|
||||
@Query("live_id") live_id: String = "",
|
||||
@Query("live_qa") live_qa: String = "",
|
||||
): Call<FAuth>
|
||||
|
||||
@GET("api/v3/live/{liveId}/resources")
|
||||
fun getEPG(
|
||||
@Path("liveId") liveId: String,
|
||||
@Query("date") date: String,
|
||||
@Query("dir") dir: String = "asc"
|
||||
): Call<List<FEPG>>
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
|
||||
data class FEPG(
|
||||
val title: String,
|
||||
val event_time: String,
|
||||
)
|
||||
@@ -3,9 +3,37 @@ package com.lizongying.mytv.api
|
||||
data class Info(
|
||||
val code: Int?,
|
||||
val msg: String?,
|
||||
val data: InfoData,
|
||||
val data: Data,
|
||||
) {
|
||||
data class Data(
|
||||
val token: String,
|
||||
)
|
||||
}
|
||||
|
||||
data class Token(
|
||||
val e: Int?,
|
||||
val t: String?,
|
||||
)
|
||||
|
||||
data class InfoData(
|
||||
val token: String,
|
||||
data class InfoV2(
|
||||
val o: String?,
|
||||
val f: String?,
|
||||
val t: String?,
|
||||
val e: Int?,
|
||||
val c: Int?,
|
||||
)
|
||||
|
||||
data class ReleaseV2(
|
||||
val n: String?,
|
||||
val u: String?,
|
||||
val d: String?,
|
||||
val c: Int?,
|
||||
)
|
||||
|
||||
data class TimeResponse(
|
||||
val data: Time
|
||||
) {
|
||||
data class Time(
|
||||
val t: String
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
data class KvcollectRequest(
|
||||
val BossId: Int = 99150,
|
||||
val Pwd: Int = 1999332929,
|
||||
val cdn: String = "waibao",
|
||||
val cmd: Int = 263,
|
||||
val defn: String = "fhd",
|
||||
val downspeed: Int = 10,
|
||||
val durl: String = "", //
|
||||
val errcode: String = "",
|
||||
val fact1: String = "ysp_pc_live_b",
|
||||
val firstreport: Int = 0,
|
||||
val fplayerver: String = "180",
|
||||
val ftime: String = "", //
|
||||
val geturltime: Int = 0,
|
||||
val guid: String = "", //
|
||||
val hc_openid: String = "",
|
||||
val hh_ua: String = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
||||
val live_type: String = "",
|
||||
val livepid: String = "", //
|
||||
val login_type: String = "",
|
||||
val open_id: String = "",
|
||||
val openid: String = "",
|
||||
val platform: String = "5910204",
|
||||
val playno: String = "", //
|
||||
val prd: Int = 60000,
|
||||
val prog: String = "", //
|
||||
val rand_str: String = "", //
|
||||
val sRef: String = "",
|
||||
val sUrl: String = "", //
|
||||
val sdtfrom: String = "ysp_pc_01",
|
||||
val seq: Int = 9, //
|
||||
var signature: String = "",
|
||||
val url: String = "", //
|
||||
val viewid: String = "", //
|
||||
val _dc: Double = 0.0,
|
||||
)
|
||||
|
||||
|
||||
data class KvcollectRequest2(
|
||||
val BossId: Int = 99150,
|
||||
val Pwd: Int = 1999332929,
|
||||
val cdn: String = "waibao",
|
||||
val cmd: Int = 263,
|
||||
val defn: String = "fhd",
|
||||
val downspeed: Int = 10,
|
||||
val durl: String = "", //
|
||||
val errcode: String = "",
|
||||
val fact1: String = "ysp_pc_live_b",
|
||||
val firstreport: Int = 0,
|
||||
val fplayerver: String = "180",
|
||||
val ftime: String = "", //
|
||||
val geturltime: Int = 0,
|
||||
val guid: String = "", //
|
||||
val hc_openid: String = "",
|
||||
val hh_ua: String = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
||||
val live_type: String = "",
|
||||
val livepid: String = "", //
|
||||
val login_type: String = "",
|
||||
val open_id: String = "",
|
||||
val openid: String = "",
|
||||
val platform: String = "5910204",
|
||||
val playno: String = "", //
|
||||
val prd: Int = 60000,
|
||||
val prog: String = "", //
|
||||
val rand_str: String = "", //
|
||||
val sRef: String = "",
|
||||
val sUrl: String = "", //
|
||||
val sdtfrom: String = "ysp_pc_01",
|
||||
val seq: Int = 0, //
|
||||
var signature: String = "",
|
||||
val url: String = "", //
|
||||
val viewid: String = "", //
|
||||
val _dc: String = "",
|
||||
)
|
||||
@@ -13,6 +13,7 @@ data class LiveInfo(
|
||||
)
|
||||
|
||||
data class LiveInfoData(
|
||||
val extended_param: String,
|
||||
val chanll: String,
|
||||
val playurl: String,
|
||||
val errinfo: String,
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.Socket
|
||||
import java.net.UnknownHostException
|
||||
import javax.net.ssl.SSLSocket
|
||||
import javax.net.ssl.SSLSocketFactory
|
||||
|
||||
|
||||
/**
|
||||
* Enables TLS v1.2 when creating SSLSockets.
|
||||
*
|
||||
*
|
||||
* For some reason, android supports TLS v1.2 from API 16, but enables it by
|
||||
* default only from API 20.
|
||||
* @link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
|
||||
* @see SSLSocketFactory
|
||||
*/
|
||||
class Tls12SocketFactory(val delegate: SSLSocketFactory) : SSLSocketFactory() {
|
||||
override fun getDefaultCipherSuites(): Array<String> {
|
||||
return delegate.defaultCipherSuites
|
||||
}
|
||||
|
||||
override fun getSupportedCipherSuites(): Array<String> {
|
||||
return delegate.supportedCipherSuites
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
|
||||
return patch(delegate.createSocket(s, host, port, autoClose))
|
||||
}
|
||||
|
||||
@Throws(IOException::class, UnknownHostException::class)
|
||||
override fun createSocket(host: String, port: Int): Socket {
|
||||
return patch(delegate.createSocket(host, port))
|
||||
}
|
||||
|
||||
@Throws(IOException::class, UnknownHostException::class)
|
||||
override fun createSocket(
|
||||
host: String,
|
||||
port: Int,
|
||||
localHost: InetAddress,
|
||||
localPort: Int
|
||||
): Socket {
|
||||
return patch(delegate.createSocket(host, port, localHost, localPort))
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun createSocket(host: InetAddress, port: Int): Socket {
|
||||
return patch(delegate.createSocket(host, port))
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun createSocket(
|
||||
address: InetAddress,
|
||||
port: Int,
|
||||
localAddress: InetAddress,
|
||||
localPort: Int
|
||||
): Socket {
|
||||
return patch(delegate.createSocket(address, port, localAddress, localPort))
|
||||
}
|
||||
|
||||
private fun patch(s: Socket): Socket {
|
||||
if (s is SSLSocket) {
|
||||
s.enabledProtocols = TLS_V12_ONLY
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TLS_V12_ONLY = arrayOf("TLSv1.2")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
data class Token2(
|
||||
val ret: Int?,
|
||||
val msg: String?,
|
||||
val data: Data,
|
||||
) {
|
||||
data class Data(
|
||||
val token: String,
|
||||
)
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.lizongying.mytv.Encryptor
|
||||
import com.lizongying.mytv.MainActivity
|
||||
import android.util.Log
|
||||
import com.lizongying.mytv.SP
|
||||
import com.lizongying.mytv.Utils
|
||||
import com.lizongying.mytv.Utils.getDateTimestamp
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
import java.security.MessageDigest
|
||||
import kotlin.math.floor
|
||||
import kotlin.random.Random
|
||||
|
||||
class YSP(var context: Context) {
|
||||
object YSP {
|
||||
private const val TAG = "YSP"
|
||||
|
||||
private var cnlid = ""
|
||||
|
||||
private var livepid = ""
|
||||
@@ -50,23 +53,27 @@ class YSP(var context: Context) {
|
||||
|
||||
private var signature = ""
|
||||
|
||||
private var encryptor: Encryptor? = null
|
||||
private var sharedPref: SharedPreferences? = null
|
||||
private var appid = "ysp_pc"
|
||||
var token = ""
|
||||
var token2 = ""
|
||||
|
||||
init {
|
||||
if (context is MainActivity) {
|
||||
encryptor = Encryptor()
|
||||
encryptor!!.init(context)
|
||||
var yspsdkinput = ""
|
||||
var openapi_signature = ""
|
||||
var yspticket = ""
|
||||
|
||||
sharedPref = (context as MainActivity).getPreferences(Context.MODE_PRIVATE)
|
||||
}
|
||||
var nseqId = 1
|
||||
var nrequest_id = ""
|
||||
|
||||
private var encryptor = Encryptor()
|
||||
|
||||
fun init(context: Context) {
|
||||
encryptor.init(context)
|
||||
guid = getGuid()
|
||||
}
|
||||
|
||||
fun switch(tvModel: TVViewModel): String {
|
||||
livepid = tvModel.pid.value!!
|
||||
cnlid = tvModel.sid.value!!
|
||||
livepid = tvModel.getTV().pid
|
||||
cnlid = tvModel.getTV().sid
|
||||
defn = "fhd"
|
||||
|
||||
randStr = getRand()
|
||||
@@ -77,18 +84,61 @@ class YSP(var context: Context) {
|
||||
|
||||
timeStr = getTimeStr()
|
||||
|
||||
// guid = "lq3oqitm_1e15dnzgjnb"
|
||||
// randStr = "BfcCPQp8Hq"
|
||||
// timeStr = "1702166501"
|
||||
|
||||
cKey =
|
||||
encryptor!!.encrypt(cnlid, timeStr, appVer, guid, platform)
|
||||
encryptor.encrypt(cnlid, timeStr, appVer, guid, platform)
|
||||
randStr = getRand()
|
||||
signature = getSignature()
|
||||
yspticket = encryptor.encrypt2("$livepid&$timeStr&$guid&519748109").lowercase()
|
||||
|
||||
return """{"cnlid":"$cnlid","livepid":"$livepid","stream":"$stream","guid":"$guid","cKey":"$cKey","adjust":$adjust,"sphttps":"$sphttps","platform":"$platform","cmd":"$cmd","encryptVer":"$encryptVer","dtype":"$dtype","devid":"$devid","otype":"$otype","appVer":"$appVer","app_version":"$appVersion","rand_str":"$randStr","channel":"$channel","defn":"$defn","signature":"$signature"}"""
|
||||
// return """{"cnlid":"2000203803","livepid":"600001801","stream":"2","guid":"lq1y36mb_ccfwmja9zan","cKey":"--01A9F5E89BB86A0C61F4025BEDE15309B6913A79FD1AF1EE7F5EC9C7605F377D1D2281488385C32DEB9E7D0DD3559CB700BD7AF44DD5C9DE0AE14D94B8214027B5D664C108AEE23532348DCC61B86F7C8FBB6CF14D588E6093A25E97DF6D66F4882AB28F17016472DD43D45EF076B7F505176A5E8DEDF2662E5F9AB12B69CB20BCE1579BE724091F3AF6826AE34B713906F3FE139C3783F80EECBD08416DC525E1","adjust":1,"sphttps":"1","platform":"5910204","cmd":"2","encryptVer":"8.1","dtype":"1","devid":"devid","otype":"ojson","appVer":"V1.0.0","app_version":"V1.0.0","rand_str":"IOS2soOw44","channel":"ysp_tx","defn":"fhd","signature":"6ca945f651817de8c6e6910457ceafd6"}"""
|
||||
}
|
||||
|
||||
private fun getTimeStr(): String {
|
||||
fun getTokenData(tvModel: TVViewModel) {
|
||||
livepid = tvModel.getTV().pid
|
||||
cnlid = tvModel.getTV().sid
|
||||
defn = "fhd"
|
||||
|
||||
randStr = getRand()
|
||||
|
||||
if (tvModel.retryTimes > 0) {
|
||||
guid = newGuid()
|
||||
}
|
||||
|
||||
cKey =
|
||||
encryptor.encrypt(cnlid, timeStr, appVer, guid, platform)
|
||||
|
||||
yspsdkinput =
|
||||
md("adjust=$adjust&app_version=$appVersion&appVer=$appVer&channel=$channel&cKey=$cKey&cmd=$cmd&cnlid=$cnlid&defn=$defn&devid=$devid&dtype=$dtype&encryptVer=$encryptVer&guid=$guid&livepid=$livepid&otype=$otype&platform=$platform&sphttps=$sphttps&stream=$stream")
|
||||
|
||||
nseqId++
|
||||
|
||||
nrequest_id = "999999" + getRand() + getTimeStr()
|
||||
|
||||
openapi_signature =
|
||||
md("yspappid:519748109;host:www.yangshipin.cn;protocol:https:;token:$token2;input:$yspsdkinput-$guid-$nseqId-$nrequest_id;")
|
||||
}
|
||||
|
||||
fun md(str: String): String {
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
md.update(str.toByteArray())
|
||||
val digest = md.digest()
|
||||
return digest.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
}
|
||||
|
||||
fun getAuthData(tvModel: TVViewModel): String {
|
||||
livepid = tvModel.getTV().pid
|
||||
|
||||
randStr = getRand()
|
||||
|
||||
if (tvModel.retryTimes > 0) {
|
||||
guid = newGuid()
|
||||
}
|
||||
|
||||
signature = getAuthSignature()
|
||||
return """pid=$livepid&guid=$guid&appid=$appid&rand_str=$randStr&signature=$signature"""
|
||||
}
|
||||
|
||||
fun getTimeStr(): String {
|
||||
return getDateTimestamp().toString()
|
||||
}
|
||||
|
||||
@@ -99,23 +149,18 @@ class YSP(var context: Context) {
|
||||
}
|
||||
|
||||
fun getGuid(): String {
|
||||
var guid = sharedPref?.getString("guid", "")
|
||||
if (guid == null || guid.length < 18) {
|
||||
var guid = SP.guid
|
||||
if (guid.length < 18) {
|
||||
guid = generateGuid()
|
||||
with(sharedPref!!.edit()) {
|
||||
putString("guid", guid)
|
||||
apply()
|
||||
}
|
||||
this.guid = guid
|
||||
SP.guid = guid
|
||||
}
|
||||
return guid
|
||||
}
|
||||
|
||||
private fun newGuid(): String {
|
||||
guid = generateGuid()
|
||||
with(sharedPref!!.edit()) {
|
||||
putString("guid", guid)
|
||||
apply()
|
||||
}
|
||||
SP.guid = guid
|
||||
return guid
|
||||
}
|
||||
|
||||
@@ -132,12 +177,19 @@ class YSP(var context: Context) {
|
||||
private fun getSignature(): String {
|
||||
val e =
|
||||
"adjust=${adjust}&appVer=${appVer}&app_version=$appVersion&cKey=$cKey&channel=$channel&cmd=$cmd&cnlid=$cnlid&defn=${defn}&devid=${devid}&dtype=${dtype}&encryptVer=${encryptVer}&guid=${guid}&livepid=${livepid}&otype=${otype}&platform=${platform}&rand_str=${randStr}&sphttps=${sphttps}&stream=${stream}".toByteArray()
|
||||
|
||||
val hashedData = encryptor?.hash(e) ?: return ""
|
||||
val hashedData = encryptor.hash(e) ?: return ""
|
||||
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "YSP"
|
||||
private fun getAuthSignature(): String {
|
||||
val e =
|
||||
"appid=${appid}&guid=${guid}&pid=${livepid}&rand_str=${randStr}${Utils.b}".toByteArray()
|
||||
val hashedData = encryptor.hash3(e) ?: return ""
|
||||
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
}
|
||||
|
||||
fun getAuthSignature(e: String): String {
|
||||
val hashedData = encryptor.hash3("$e${Utils.b}".toByteArray()) ?: return ""
|
||||
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@ package com.lizongying.mytv.api
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
||||
interface YSPApiService {
|
||||
@@ -13,9 +15,45 @@ interface YSPApiService {
|
||||
"content-type: application/json;charset=UTF-8",
|
||||
"referer: https://www.yangshipin.cn/",
|
||||
"yspappid: 519748109",
|
||||
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
)
|
||||
fun getLiveInfo(
|
||||
@Header("cookie") cookie: String,
|
||||
@Header("Yspplayertoken") token: String,
|
||||
@Header("Yspsdkinput") Yspsdkinput: String,
|
||||
@Header("yspsdksign") yspsdksign: String,
|
||||
@Header("Yspticket") Yspticket: String,
|
||||
@Header("Seqid") Seqid: String,
|
||||
@Header("Request-Id") RequestId: String,
|
||||
@Header("Yspappid") Yspappid: String="519748109",
|
||||
@Body request: LiveInfoRequest,
|
||||
): Call<LiveInfo>
|
||||
|
||||
@POST("v1/player/auth")
|
||||
@Headers(
|
||||
"content-type: application/x-www-form-urlencoded;charset=UTF-8",
|
||||
"referer: https://www.yangshipin.cn/",
|
||||
"yspappid: 519748109",
|
||||
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
)
|
||||
fun getAuth(
|
||||
@Header("cookie") cookie: String,
|
||||
@Body request: AuthRequest,
|
||||
): Call<Auth>
|
||||
|
||||
@GET("web/open/token")
|
||||
@Headers(
|
||||
"content-type: application/json",
|
||||
"referer: https://www.yangshipin.cn/",
|
||||
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
)
|
||||
fun getToken(
|
||||
@Header("cookie") cookie: String,
|
||||
@Query("yspappid") yspappid: String = "519748109",
|
||||
@Query("guid") guid: String = "",
|
||||
@Query("vappid") vappid: String = "59306155",
|
||||
@Query("vsecret") vsecret: String = "b42702bf7309a179d102f3d51b1add2fda0bc7ada64cb801",
|
||||
@Query("raw") raw: String = "1",
|
||||
@Query("ts") ts: String = "",
|
||||
): Call<Token2>
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Field
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
||||
@@ -68,4 +68,58 @@ interface YSPBtraceService {
|
||||
@Field("c_guid") c_guid: String = "",
|
||||
@Field("c_vuid") c_vuid: String = "-",
|
||||
): Call<Void>
|
||||
|
||||
@POST("kvcollect")
|
||||
@Headers(
|
||||
"content-type: application/json",
|
||||
"referer: https://www.yangshipin.cn/",
|
||||
"Cookie: guid=1; versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=123",
|
||||
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
||||
)
|
||||
fun kvcollect2(
|
||||
@Body request: KvcollectRequest,
|
||||
): Call<Void>
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("kvcollect")
|
||||
@Headers(
|
||||
"content-type: application/x-www-form-urlencoded",
|
||||
"referer: https://www.yangshipin.cn/",
|
||||
)
|
||||
fun kvcollect3(
|
||||
@Field("BossId") BossId: String = "9150",
|
||||
@Field("Pwd") Pwd: String = "1999332929",
|
||||
@Field("prog") prog: String = "", //
|
||||
@Field("playno") playno: String = "", //
|
||||
@Field("guid") guid: String = "", //
|
||||
@Field("hh_ua") hh_ua: String = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
||||
@Field("cdn") cdn: String = "waibao",
|
||||
@Field("sdtfrom") sdtfrom: String = "ysp_pc_01",
|
||||
@Field("prd") prd: String = "60000",
|
||||
@Field("platform") platform: String = "5910204",
|
||||
@Field("errcode") errcode: String = "-",
|
||||
@Field("durl") durl: String = "", //
|
||||
@Field("firstreport") firstreport: String = "-",
|
||||
@Field("sUrl") sUrl: String = "", //
|
||||
@Field("sRef") sRef: String = "-",
|
||||
@Field("fplayerver") fplayerver: String = "180",
|
||||
@Field("livepid") livepid: String = "",
|
||||
@Field("viewid") viewid: String = "",
|
||||
@Field("seq") seq: String = "",
|
||||
@Field("cmd") cmd: String = "263",
|
||||
@Field("login_type") login_type: String = "-",
|
||||
@Field("geturltime") geturltime: String = "-",
|
||||
@Field("downspeed") downspeed: String = "10",
|
||||
@Field("hc_openid") hc_openid: String = "-",
|
||||
@Field("open_id") open_id: String = "-",
|
||||
@Field("defn") defn: String = "fhd",
|
||||
@Field("fact1") fact1: String = "ysp_pc_live_b",
|
||||
@Field("openid") openid: String = "-",
|
||||
@Field("_dc") _dc: String = "",
|
||||
@Field("live_type") live_type: String = "-",
|
||||
@Field("ftime") ftime: String = "",
|
||||
// @Field("url") url: String = "",
|
||||
// @Field("rand_str") rand_str: String = "", //
|
||||
// @Field("signature") signature: String = "",
|
||||
): Call<Void>
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.TVTimeShiftProgramRequest
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.TVTimeShiftProgramResponse
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.POST
|
||||
|
||||
interface YSPJceService {
|
||||
@POST("/")
|
||||
@Headers(
|
||||
"content-type: application/octet-stream",
|
||||
"user-agent: CCTVVideo/2.9.0 (iPad; iOS 17.2; Scale/2.00)",
|
||||
)
|
||||
fun getProgram(
|
||||
@Body body: TVTimeShiftProgramRequest
|
||||
): Call<TVTimeShiftProgramResponse>
|
||||
}
|
||||
@@ -2,10 +2,21 @@ package com.lizongying.mytv.api
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
||||
interface YSPTokenService {
|
||||
@GET("my-tv/v1/info")
|
||||
fun getInfo(
|
||||
@Query("token") token: String = "",
|
||||
): Call<Info>
|
||||
|
||||
@GET("my-tv/v2/token")
|
||||
fun getToken(
|
||||
@Query("token") token: String = "",
|
||||
): Call<Token>
|
||||
|
||||
@GET("my-tv/v2/info")
|
||||
fun getInfoV2(
|
||||
): Call<InfoV2>
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.lizongying.mytv.jce;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class CompressUtils {
|
||||
private static final String TAG = "CompressUtils";
|
||||
|
||||
public static byte[] compressGZIP(byte[] bArr) {
|
||||
GZIPOutputStream gZIPOutputStream;
|
||||
Throwable th;
|
||||
if (bArr != null && bArr.length != 0) {
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bArr);
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
gZIPOutputStream = new GZIPOutputStream(byteArrayOutputStream);
|
||||
} catch (IOException unused) {
|
||||
gZIPOutputStream = null;
|
||||
} catch (Throwable th2) {
|
||||
gZIPOutputStream = null;
|
||||
th = th2;
|
||||
}
|
||||
try {
|
||||
byte[] bArr2 = new byte[4096];
|
||||
while (true) {
|
||||
int read = byteArrayInputStream.read(bArr2);
|
||||
if (read <= 0) {
|
||||
break;
|
||||
}
|
||||
gZIPOutputStream.write(bArr2, 0, read);
|
||||
}
|
||||
gZIPOutputStream.finish();
|
||||
byte[] byteArray = byteArrayOutputStream.toByteArray();
|
||||
try {
|
||||
byteArrayInputStream.close();
|
||||
byteArrayOutputStream.close();
|
||||
gZIPOutputStream.close();
|
||||
} catch (IOException e2) {
|
||||
System.out.println(e2);
|
||||
}
|
||||
return byteArray;
|
||||
} catch (IOException unused2) {
|
||||
try {
|
||||
byteArrayInputStream.close();
|
||||
byteArrayOutputStream.close();
|
||||
if (gZIPOutputStream != null) {
|
||||
gZIPOutputStream.close();
|
||||
}
|
||||
} catch (IOException e3) {
|
||||
System.out.println(e3);
|
||||
}
|
||||
return null;
|
||||
} catch (Throwable th3) {
|
||||
th = th3;
|
||||
try {
|
||||
byteArrayInputStream.close();
|
||||
byteArrayOutputStream.close();
|
||||
if (gZIPOutputStream != null) {
|
||||
gZIPOutputStream.close();
|
||||
}
|
||||
} catch (IOException e4) {
|
||||
System.out.println(e4);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] decompressGZIP(byte[] bArr) {
|
||||
GZIPInputStream gZIPInputStream;
|
||||
Throwable th;
|
||||
if (bArr != null && bArr.length != 0) {
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bArr);
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
gZIPInputStream = new GZIPInputStream(byteArrayInputStream);
|
||||
} catch (IOException unused) {
|
||||
gZIPInputStream = null;
|
||||
} catch (Throwable th2) {
|
||||
gZIPInputStream = null;
|
||||
th = th2;
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] bArr2 = new byte[4096];
|
||||
while (true) {
|
||||
int read = gZIPInputStream.read(bArr2);
|
||||
if (read <= 0) {
|
||||
break;
|
||||
}
|
||||
byteArrayOutputStream.write(bArr2, 0, read);
|
||||
}
|
||||
byteArrayOutputStream.flush();
|
||||
byte[] byteArray = byteArrayOutputStream.toByteArray();
|
||||
|
||||
try {
|
||||
byteArrayInputStream.close();
|
||||
byteArrayOutputStream.close();
|
||||
gZIPInputStream.close();
|
||||
} catch (IOException e2) {
|
||||
System.out.println(e2);
|
||||
}
|
||||
return byteArray;
|
||||
} catch (IOException unused2) {
|
||||
try {
|
||||
byteArrayInputStream.close();
|
||||
byteArrayOutputStream.close();
|
||||
if (gZIPInputStream != null) {
|
||||
gZIPInputStream.close();
|
||||
}
|
||||
} catch (IOException e3) {
|
||||
System.out.println(e3);
|
||||
}
|
||||
return null;
|
||||
} catch (Throwable th3) {
|
||||
th = th3;
|
||||
try {
|
||||
byteArrayInputStream.close();
|
||||
byteArrayOutputStream.close();
|
||||
if (gZIPInputStream != null) {
|
||||
gZIPInputStream.close();
|
||||
}
|
||||
} catch (IOException e4) {
|
||||
System.out.println(e4);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.lizongying.mytv.jce;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Converter;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
public class JceConverterFactory extends Converter.Factory {
|
||||
public static JceConverterFactory create() {
|
||||
return new JceConverterFactory();
|
||||
}
|
||||
|
||||
private JceConverterFactory() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Converter<ResponseBody, ?> responseBodyConverter(
|
||||
@NonNull Type type,
|
||||
@NonNull Annotation[] annotations,
|
||||
@NonNull Retrofit retrofit) {
|
||||
if (!(type instanceof Class<?>)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JceResponseBodyConverter<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Converter<?, RequestBody> requestBodyConverter(
|
||||
@NonNull Type type,
|
||||
@NonNull Annotation[] parameterAnnotations,
|
||||
@NonNull Annotation[] methodAnnotations,
|
||||
@NonNull Retrofit retrofit) {
|
||||
if (!(type instanceof Class<?>)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JceRequestBodyConverter<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.lizongying.mytv.jce;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.BusinessExtent;
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.BusinessHead;
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.QUA;
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.RequestCommand;
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.RequestHead;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import retrofit2.Converter;
|
||||
|
||||
public class JceRequestBodyConverter<T extends JceStruct> implements Converter<T, RequestBody> {
|
||||
private static final MediaType MEDIA_TYPE = MediaType.get("application/octet-stream");
|
||||
|
||||
@Override
|
||||
public RequestBody convert(@NonNull T value) {
|
||||
BusinessHead businessHead = new BusinessHead();
|
||||
RequestHead requestHead = new RequestHead();
|
||||
requestHead.appId = "1200013";
|
||||
requestHead.busiExtent = new BusinessExtent(1);
|
||||
requestHead.cmdId = 24897;
|
||||
requestHead.contentType = 0;
|
||||
requestHead.guid = "093e7e5989684fd986c44f07542d8dc8";
|
||||
requestHead.isSupportDolby = 0;
|
||||
requestHead.oemPlatform = 0;
|
||||
QUA qua = new QUA();
|
||||
qua.appSubVersion = "";
|
||||
qua.areaMode = 1;
|
||||
qua.channelId = "50001";
|
||||
qua.clientKey = "A41CD6B6-6A56-46DC-9AE0-4ECF2E9118DF";
|
||||
qua.countryCode = 0;
|
||||
qua.densityDpi = 0;
|
||||
qua.deviceId = "5EE46760-11B1-5FE7-949A-FF232DAE1823";
|
||||
qua.deviceModel = "iPad Pro (12.9 inch) 3G";
|
||||
qua.deviceType = 1;
|
||||
qua.extent = "";
|
||||
qua.idfa = "";
|
||||
qua.imei = "";
|
||||
qua.langCode = 0;
|
||||
qua.mac = "";
|
||||
qua.markerId = 1;
|
||||
qua.mobileISP = 0;
|
||||
qua.networkMode = 0;
|
||||
qua.omgId = "78d6ac7572afb5461e9a590f089f893d2abc0010119007";
|
||||
qua.platform = 5;
|
||||
qua.platformVersion = "sysver=ios17.2&device=iPad&modify_time=&lang=zh_CN";
|
||||
qua.qimei = "1c0fc6ed8a53584ae722b915200014317601";
|
||||
qua.screenHeight = 2360;
|
||||
qua.screenWidth = 1640;
|
||||
qua.serverid = "";
|
||||
qua.v4ip = "";
|
||||
qua.versionCode = "23399";
|
||||
qua.versionName = "2.9.0.23399";
|
||||
requestHead.qua = qua;
|
||||
requestHead.requestId = 1;
|
||||
|
||||
JceOutputStream jceOutputStream = new JceOutputStream();
|
||||
jceOutputStream.setServerEncoding("utf-8");
|
||||
value.writeTo(jceOutputStream);
|
||||
byte[] bytes = jceOutputStream.toByteArray();
|
||||
RequestCommand requestCommand = new RequestCommand(requestHead, bytes, businessHead);
|
||||
bytes = b.b(requestCommand, requestHead.requestId);
|
||||
return RequestBody.create(MEDIA_TYPE, bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.lizongying.mytv.jce;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.QQVideoJCECmd;
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.ResponseCommand;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Converter;
|
||||
|
||||
public class JceResponseBodyConverter<T extends JceStruct> implements Converter<ResponseBody, T> {
|
||||
|
||||
JceResponseBodyConverter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T convert(@NonNull ResponseBody value) throws IOException {
|
||||
return parseFrom(value.bytes());
|
||||
}
|
||||
|
||||
public T parseFrom(byte[] aa) {
|
||||
byte[] bArr;
|
||||
|
||||
int[] iArr = new int[1];
|
||||
try {
|
||||
bArr = b.a(aa, iArr);
|
||||
} catch (Throwable unused) {
|
||||
bArr = null;
|
||||
}
|
||||
|
||||
ResponseCommand responseCommand = a.g(bArr, ResponseCommand.class);
|
||||
if (responseCommand == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
QQVideoJCECmd convert = QQVideoJCECmd.convert(responseCommand.head.cmdId);
|
||||
assert convert != null;
|
||||
|
||||
JceInputStream jceInputStream = new JceInputStream(responseCommand.body);
|
||||
jceInputStream.setServerEncoding("UTF-8");
|
||||
|
||||
JceStruct instance = null;
|
||||
try {
|
||||
instance = (JceStruct) Class.forName("com.tencent.videolite.android.datamodel.cctvjce." + convert + "Response").newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (instance != null) {
|
||||
instance.readFrom(jceInputStream);
|
||||
return (T) instance;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.lizongying.mytv.jce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class a {
|
||||
|
||||
/* renamed from: a reason: collision with root package name */
|
||||
private static final String f33240a = "Net_Protocol";
|
||||
|
||||
private a() {
|
||||
}
|
||||
|
||||
private static JceStruct a(JceStruct jceStruct, ClassLoader classLoader) {
|
||||
Class<?> cls;
|
||||
if (jceStruct == null) {
|
||||
return null;
|
||||
}
|
||||
String name = jceStruct.getClass().getName();
|
||||
String str = name.substring(0, name.length() - 7) + "Response";
|
||||
try {
|
||||
if (classLoader == null) {
|
||||
cls = Class.forName(str);
|
||||
} else {
|
||||
cls = Class.forName(str, true, classLoader);
|
||||
}
|
||||
return (JceStruct) cls.newInstance();
|
||||
} catch (ClassNotFoundException unused) {
|
||||
return null;
|
||||
} catch (IllegalAccessException unused) {
|
||||
return null;
|
||||
} catch (InstantiationException unused) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static JceStruct b(String str, ClassLoader classLoader) {
|
||||
Class<?> cls;
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
String str2 = str + "Response";
|
||||
try {
|
||||
if (classLoader == null) {
|
||||
cls = Class.forName(str2);
|
||||
} else {
|
||||
cls = Class.forName(str2, true, classLoader);
|
||||
}
|
||||
return (JceStruct) cls.newInstance();
|
||||
} catch (ClassNotFoundException unused) {
|
||||
return null;
|
||||
} catch (IllegalAccessException unused) {
|
||||
return null;
|
||||
} catch (InstantiationException unused) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int c(Object obj) {
|
||||
Field[] declaredFields;
|
||||
int i2 = 0;
|
||||
if (obj == null) {
|
||||
return 0;
|
||||
}
|
||||
for (Field field : obj.getClass().getDeclaredFields()) {
|
||||
if (field != null) {
|
||||
field.setAccessible(true);
|
||||
if (!field.getName().equals("errCode")) {
|
||||
continue;
|
||||
} else if (!field.getType().getName().equals(Integer.class.getName()) && !field.getType().getName().equals("int")) {
|
||||
if (field.getType().getName().equals(Long.class.getName()) || field.getType().getName().equals("long")) {
|
||||
try {
|
||||
return (int) field.getLong(obj);
|
||||
} catch (IllegalAccessException e2) {
|
||||
e2.printStackTrace();
|
||||
} catch (IllegalArgumentException e3) {
|
||||
e3.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return field.getInt(obj);
|
||||
} catch (IllegalAccessException e4) {
|
||||
e4.printStackTrace();
|
||||
} catch (IllegalArgumentException e5) {
|
||||
e5.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return i2;
|
||||
}
|
||||
|
||||
public static byte[] d(JceStruct jceStruct) {
|
||||
if (jceStruct == null) {
|
||||
return null;
|
||||
}
|
||||
JceOutputStream jceOutputStream = new JceOutputStream();
|
||||
jceOutputStream.setServerEncoding("utf-8");
|
||||
jceStruct.writeTo(jceOutputStream);
|
||||
return jceOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
public static JceStruct e(JceStruct jceStruct, byte[] bArr, ClassLoader classLoader) {
|
||||
JceStruct a2;
|
||||
if (jceStruct != null && bArr != null && (a2 = a(jceStruct, classLoader)) != null) {
|
||||
try {
|
||||
JceInputStream jceInputStream = new JceInputStream(bArr);
|
||||
jceInputStream.setServerEncoding("utf-8");
|
||||
a2.readFrom(jceInputStream);
|
||||
return a2;
|
||||
} catch (Exception unused) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static JceStruct f(String str, byte[] bArr, ClassLoader classLoader) {
|
||||
if (str != null && bArr != null) {
|
||||
JceStruct b2 = b(str, classLoader);
|
||||
if (b2 != null) {
|
||||
try {
|
||||
JceInputStream jceInputStream = new JceInputStream(bArr);
|
||||
jceInputStream.setServerEncoding("utf-8");
|
||||
b2.readFrom(jceInputStream);
|
||||
return b2;
|
||||
} catch (Exception e2) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends JceStruct> T g(byte[] bArr, Class<T> cls) {
|
||||
T t;
|
||||
if (bArr != null && bArr.length >= 4) {
|
||||
try {
|
||||
t = cls.newInstance();
|
||||
} catch (IllegalAccessException unused) {
|
||||
t = null;
|
||||
} catch (InstantiationException unused) {
|
||||
t = null;
|
||||
}
|
||||
try {
|
||||
JceInputStream jceInputStream = new JceInputStream(bArr);
|
||||
jceInputStream.setServerEncoding("utf-8");
|
||||
if (t != null) {
|
||||
t.readFrom(jceInputStream);
|
||||
}
|
||||
return t;
|
||||
} catch (Exception unused2) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.lizongying.mytv.jce;
|
||||
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.RequestCommand;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class b {
|
||||
|
||||
/* renamed from: a reason: collision with root package name */
|
||||
private static final String f33241a = "Net_UnifiedProtocol";
|
||||
|
||||
b() {
|
||||
}
|
||||
|
||||
public static byte[] a(byte[] bArr, int[] iArr) {
|
||||
boolean z;
|
||||
if (bArr == null || bArr.length == 0) {
|
||||
return null;
|
||||
}
|
||||
ByteBuffer wrap = ByteBuffer.wrap(bArr);
|
||||
if (wrap.get() == 19 && wrap.getInt() == bArr.length) {
|
||||
wrap.getShort();
|
||||
if ((wrap.getShort() & 65535) != 65281) {
|
||||
|
||||
return null;
|
||||
}
|
||||
wrap.getShort();
|
||||
int i2 = wrap.getShort() & 65535;
|
||||
if (i2 != 0) {
|
||||
iArr[0] = i2;
|
||||
|
||||
return null;
|
||||
}
|
||||
wrap.getLong();
|
||||
int i3 = wrap.getInt();
|
||||
if ((i3 & 2) <= 0) {
|
||||
z = false;
|
||||
} else if ((i3 & 16) <= 0) {
|
||||
iArr[0] = -867;
|
||||
return null;
|
||||
} else {
|
||||
z = true;
|
||||
}
|
||||
wrap.getInt();
|
||||
wrap.getLong();
|
||||
wrap.position(wrap.position() + 32);
|
||||
wrap.get();
|
||||
wrap.position(wrap.position() + 10);
|
||||
wrap.get();
|
||||
wrap.position(wrap.position() + (wrap.getShort() & 65535));
|
||||
int i4 = 65535 & wrap.getShort();
|
||||
wrap.position(wrap.position() + i4);
|
||||
int i5 = 83 + i4 + 2;
|
||||
int i6 = wrap.getInt();
|
||||
int i7 = i5 + 4;
|
||||
if (wrap.get(bArr.length - 1) != 3) {
|
||||
iArr[0] = -869;
|
||||
return null;
|
||||
}
|
||||
int length = (bArr.length - i7) - 1;
|
||||
if (length <= 0) {
|
||||
iArr[0] = -868;
|
||||
return null;
|
||||
}
|
||||
byte[] bArr2 = new byte[length];
|
||||
System.arraycopy(bArr, i7, bArr2, 0, length);
|
||||
return c(bArr2, z, i6, iArr);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] b(RequestCommand requestCommand, long j) {
|
||||
byte[] d2 = a.d(requestCommand);
|
||||
if (d2 == null) {
|
||||
return null;
|
||||
}
|
||||
byte[] d3 = d(d2);
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(d3.length + 50);
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
|
||||
try {
|
||||
dataOutputStream.writeByte(19);
|
||||
dataOutputStream.writeInt(0);
|
||||
dataOutputStream.writeShort(2);
|
||||
dataOutputStream.writeShort(65281);
|
||||
dataOutputStream.writeShort(requestCommand.head.cmdId);
|
||||
dataOutputStream.writeShort(0);
|
||||
dataOutputStream.writeLong(j);
|
||||
dataOutputStream.writeInt(531);
|
||||
dataOutputStream.writeInt(0);
|
||||
dataOutputStream.writeLong(0);
|
||||
e(dataOutputStream, requestCommand.head.guid, 32);
|
||||
dataOutputStream.writeByte(0);
|
||||
// VersionCode
|
||||
dataOutputStream.writeInt(0);
|
||||
e(dataOutputStream, null, 6);
|
||||
dataOutputStream.writeByte(0);
|
||||
dataOutputStream.writeShort(0);
|
||||
dataOutputStream.writeShort(0);
|
||||
dataOutputStream.writeInt(d3.length);
|
||||
byte[] compressGZIP = CompressUtils.compressGZIP(d3);
|
||||
if (compressGZIP == null) {
|
||||
return null;
|
||||
}
|
||||
dataOutputStream.write(compressGZIP);
|
||||
dataOutputStream.writeByte(3);
|
||||
dataOutputStream.close();
|
||||
byte[] byteArray = byteArrayOutputStream.toByteArray();
|
||||
ByteBuffer wrap = ByteBuffer.wrap(byteArray);
|
||||
wrap.putInt(1, byteArray.length);
|
||||
return wrap.array();
|
||||
} catch (IOException unused) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] c(byte[] bArr, boolean z, int i2, int[] iArr) {
|
||||
if (bArr == null) {
|
||||
iArr[0] = -871;
|
||||
return null;
|
||||
// } else if (z && ((bArr = CompressUtils.decompressGZIP(bArr)) == null || bArr.length != i2)) {
|
||||
} else if (z && ((bArr = CompressUtils.decompressGZIP(bArr)) == null)) {
|
||||
iArr[0] = -871;
|
||||
|
||||
return null;
|
||||
} else {
|
||||
ByteBuffer wrap = ByteBuffer.wrap(bArr);
|
||||
int position = wrap.position() + 16;
|
||||
if (wrap.get() != 38) {
|
||||
iArr[0] = -869;
|
||||
return null;
|
||||
} else if (wrap.get(bArr.length - 1) != 40) {
|
||||
iArr[0] = -869;
|
||||
return null;
|
||||
} else {
|
||||
int length = (bArr.length - position) - 1;
|
||||
if (length <= 0) {
|
||||
iArr[0] = -869;
|
||||
return null;
|
||||
}
|
||||
byte[] bArr2 = new byte[length];
|
||||
System.arraycopy(bArr, position, bArr2, 0, length);
|
||||
return bArr2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] d(byte[] bArr) {
|
||||
int length = bArr.length + 17;
|
||||
ByteBuffer allocate = ByteBuffer.allocate(length);
|
||||
allocate.put((byte) 38);
|
||||
allocate.putInt(length);
|
||||
allocate.put((byte) 1);
|
||||
allocate.position(allocate.position() + 10);
|
||||
allocate.put(bArr);
|
||||
allocate.put((byte) 40);
|
||||
return allocate.array();
|
||||
}
|
||||
|
||||
private static void e(OutputStream outputStream, String str, int i2) throws IOException {
|
||||
byte[] bArr;
|
||||
int i3;
|
||||
if (i2 > 0) {
|
||||
if (str != null) {
|
||||
bArr = str.getBytes("UTF-8");
|
||||
i3 = bArr.length;
|
||||
} else {
|
||||
bArr = null;
|
||||
i3 = 0;
|
||||
}
|
||||
if (i3 >= i2) {
|
||||
outputStream.write(bArr, 0, i2);
|
||||
return;
|
||||
}
|
||||
if (i3 > 0) {
|
||||
outputStream.write(bArr, 0, i3);
|
||||
}
|
||||
while (i3 < i2) {
|
||||
outputStream.write(0);
|
||||
i3++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
|
||||
data class EPG(
|
||||
val title: String,
|
||||
val beginTime: Int,
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class MyViewModel : ViewModel() {
|
||||
private var _downloadProgress = MutableLiveData<Int>()
|
||||
val downloadProgress: LiveData<Int>
|
||||
get() = _downloadProgress
|
||||
|
||||
fun setDownloadProgress(progress: Int) {
|
||||
_downloadProgress.value = progress
|
||||
}
|
||||
|
||||
init {
|
||||
_downloadProgress.value = 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
enum class ProgramType {
|
||||
Y_PROTO,
|
||||
Y_JCE,
|
||||
F,
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
import com.lizongying.mytv.models.ProgramType
|
||||
import java.io.Serializable
|
||||
|
||||
data class TV(
|
||||
var id: Int = 0,
|
||||
var title: String,
|
||||
var alias: String = "",
|
||||
var videoUrl: List<String>,
|
||||
var channel: String = "",
|
||||
var logo: Any = "",
|
||||
var pid: String = "",
|
||||
var sid: String = "",
|
||||
var programType: ProgramType,
|
||||
var needToken: Boolean = false,
|
||||
var mustToken: Boolean = false,
|
||||
var volume: Float = 0.1F,
|
||||
) : Serializable {
|
||||
|
||||
override fun toString(): String {
|
||||
return "TV{" +
|
||||
"id=" + id +
|
||||
", title='" + title + '\'' +
|
||||
", alias='" + alias + '\'' +
|
||||
", videoUrl='" + videoUrl + '\'' +
|
||||
", channel='" + channel + '\'' +
|
||||
", logo='" + logo + '\'' +
|
||||
", pid='" + pid + '\'' +
|
||||
", sid='" + sid + '\'' +
|
||||
", programType='" + programType + '\'' +
|
||||
", needToken='" + needToken + '\'' +
|
||||
", mustToken='" + mustToken + '\'' +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,946 @@
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
import com.lizongying.mytv.R
|
||||
|
||||
object TVList {
|
||||
val list: Map<String, List<TV>> by lazy {
|
||||
setup()
|
||||
}
|
||||
|
||||
private fun setup(): Map<String, List<TV>> {
|
||||
var list = mapOf(
|
||||
"央视" to listOf(
|
||||
TV(
|
||||
0,
|
||||
"CCTV1 综合",
|
||||
"CCTV1",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp",
|
||||
"600001859",
|
||||
"2022576801",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV2 财经",
|
||||
"CCTV2",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp",
|
||||
"600001800",
|
||||
"2022576701",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV3 综艺",
|
||||
"CCTV3",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/7b7a65c712450da3deb6ca66fbacf4f9aee00d3f20bd80eafb5ada01ec63eb3a.png?imageMogr2/format/webp",
|
||||
"600001801",
|
||||
"2022576501",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV4 中文国际",
|
||||
"CCTV4",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp",
|
||||
"600001814",
|
||||
"2022576601",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV5 体育",
|
||||
"CCTV5",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp",
|
||||
"600001818",
|
||||
"2022576401",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV6 电影",
|
||||
"CCTV6",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/741515efda91f03f455df8a7da4ee11fa9329139c276435cf0a9e2af398d5bf2.png?imageMogr2/format/webp",
|
||||
"600108442",
|
||||
"2013693901",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV7 国防军事",
|
||||
"CCTV7",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp",
|
||||
"600004092",
|
||||
"2022576201",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV8 电视剧",
|
||||
"CCTV8",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/ad51de94426a0ba039e6dd6a8534ea98ecc813a6176bde87b4f18cc34d6d7590.png?imageMogr2/format/webp",
|
||||
"600001803",
|
||||
"2022576101",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV9 纪录",
|
||||
"CCTV9",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp",
|
||||
"600004078",
|
||||
"2022576001",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV10 科教",
|
||||
"CCTV10",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp",
|
||||
"600001805",
|
||||
"2022573001",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV11 戏曲",
|
||||
"CCTV11",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp",
|
||||
"600001806",
|
||||
"2022575901",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV12 社会与法",
|
||||
"CCTV12",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp",
|
||||
"600001807",
|
||||
"2022575801",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV13 新闻",
|
||||
"CCTV13",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp",
|
||||
"600001811",
|
||||
"2022575701",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV14 少儿",
|
||||
"CCTV14",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp",
|
||||
"600001809",
|
||||
"2022575601",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV15 音乐",
|
||||
"CCTV15",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp",
|
||||
"600001815",
|
||||
"2022575501",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV16 奥林匹克",
|
||||
"CCTV16-HD",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp",
|
||||
"600098637",
|
||||
"2022575401",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV17 农业农村",
|
||||
"CCTV17",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp",
|
||||
"600001810",
|
||||
"2022575301",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV5+ 体育赛事",
|
||||
"CCTV5+",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp",
|
||||
"600001817",
|
||||
"2022576301",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV4K 超高清",
|
||||
"CCTV4K",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/3e9d06fd7244d950df5838750f1c6ac3456e172b51caca2c16d2282125b111e8.png?imageMogr2/format/webp",
|
||||
"600002264",
|
||||
"2022575201",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CCTV8K 超高清",
|
||||
"CCTV8K",
|
||||
listOf(),
|
||||
"央视",
|
||||
R.drawable.cctv8k,
|
||||
"600156816",
|
||||
"2020603421",
|
||||
ProgramType.Y_JCE,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.8F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"风云剧场",
|
||||
"CCTV风云剧场频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/4d549e53e6d0f632d5a633d1945280797b153e588f919221a07faa869812cc89.png?imageMogr2/format/webp",
|
||||
"600099658",
|
||||
"2012513603",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"第一剧场",
|
||||
"CCTV第一剧场频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/a556bd7d93ce65e18f243a8892b5604f4faa994a4897315914216a710a706208.png?imageMogr2/format/webp",
|
||||
"600099655",
|
||||
"2012514403",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"怀旧剧场",
|
||||
"CCTV怀旧剧场频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/5661bd04fecdb6e899f801147a22ab5d3a475bf2b62e30aec2c0023190ebc9b1.png?imageMogr2/format/webp",
|
||||
"600099620",
|
||||
"2012511203",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"世界地理",
|
||||
"CCTV世界地理频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/bb3c6c9e145d698137f5bb64a582021a01b51344b929003630eb769ea65832a9.png?imageMogr2/format/webp",
|
||||
"600099637",
|
||||
"2012513303",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"风云音乐",
|
||||
"CCTV风云音乐频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/bbf1d024c5228b8dd128b0e3cb1717d173fab4ee84c3a4c8a57b1a215362ca3b.png?imageMogr2/format/webp",
|
||||
"600099660",
|
||||
"2012514103",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"兵器科技",
|
||||
"CCTV兵器科技频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/4c6b6a6d3839889f34d33db3c2f80233b26b74d3489b393487635f8704e70796.png?imageMogr2/format/webp",
|
||||
"600099649",
|
||||
"2012513403",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"风云足球",
|
||||
"CCTV风云足球频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/cd1e2bb52b06a991de168733e5ff0f1d85adc8042d40c8f393f723543e5dd08a.png?imageMogr2/format/webp",
|
||||
"600099636",
|
||||
"2012514203",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"高尔夫网球",
|
||||
"CCTV高尔夫·网球频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/cdd1b31ede7a5ad049ed53d9a072422f829e72dd062ed2c19e077fdd01699071.png?imageMogr2/format/webp",
|
||||
"600099659",
|
||||
"2012512503",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"女性时尚",
|
||||
"CCTV女性时尚频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/fa28955ce8b2539d728bf4c6a13a46ff57ad76eae46627f7bcfb1ed8a613d3fc.png?imageMogr2/format/webp",
|
||||
"600099650",
|
||||
"2012513903",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"央视文化精品",
|
||||
"CCTV央视文化精品频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/14ac5ce40482cacd3d4b37435222bfe86af2b452a2f04ecbfc1d13d76edd7c57.png?imageMogr2/format/webp",
|
||||
"600099653",
|
||||
"2012513803",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"央视台球",
|
||||
"CCTV央视台球频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/10e14a92478011aa6c3c8562e62127f3b1908e29fcd78e4b2b24b9e6d3ec2fbc.png?imageMogr2/format/webp",
|
||||
"600099652",
|
||||
"2012513703",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"电视指南",
|
||||
"CCTV电视指南频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/244d72c0eb1615ed7d51c2f5db5a67f306aa3f58c05bc2d34de3aa7e956dc8c9.png?imageMogr2/format/webp",
|
||||
"600099656",
|
||||
"2012514003",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"卫生健康",
|
||||
"CCTV卫生健康频道",
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/54a6863656fdfd8f803be193ddf22441c5000a108833889816fd2d8911715ce8.png?imageMogr2/format/webp",
|
||||
"600099651",
|
||||
"2012513503",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = true
|
||||
),
|
||||
),
|
||||
"地方" to listOf(
|
||||
TV(
|
||||
0,
|
||||
"东方卫视",
|
||||
"东方卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp",
|
||||
"600002483",
|
||||
"2000292403",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"湖南卫视",
|
||||
"湖南卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp",
|
||||
"600002475",
|
||||
"2000296203",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"湖北卫视",
|
||||
"湖北卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp",
|
||||
"600002508",
|
||||
"2000294503",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"辽宁卫视",
|
||||
"辽宁卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/ac4ed6058a87c101ae7147ebc38905d0cae047fb73fd277ee5049b84f52bda36.png?imageMogr2/format/webp",
|
||||
"600002505",
|
||||
"2000281303",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"江苏卫视",
|
||||
"江苏卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/380ad685c0c1d5b2c902246b8d2df6d3f9b45e2837abcfe493075bbded597a31.png?imageMogr2/format/webp",
|
||||
"600002521",
|
||||
"2000295603",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"江西卫视",
|
||||
"江西卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/3c760d0d00463855890e8a1864ea4a6b6dd66b90c29b4ac714a4b17c16519871.png?imageMogr2/format/webp",
|
||||
"600002503",
|
||||
"2000294103",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"山东卫视",
|
||||
"山东卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/22d403f07a7cf5410b3ad3ddb65a11aa229a32475fac213f5344c9f0ec330ca1.png?imageMogr2/format/webp",
|
||||
"600002513",
|
||||
"2000294803",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"广东卫视",
|
||||
"广东卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/28886880a4dc0f06fb7e0a528a1def0591d61a65870e29176ede0cc92033bbfd.png?imageMogr2/format/webp",
|
||||
"600002485",
|
||||
"2000292703",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"广西卫视",
|
||||
"广西卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/54b7e97cb816bb223fe05f3fc44da2c7820eb66e8550c19d23100f2c414ecc38.png?imageMogr2/format/webp",
|
||||
"600002509",
|
||||
"2000294203",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"重庆卫视",
|
||||
"重庆卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
R.drawable.chongqing,
|
||||
"600002531",
|
||||
"2000297803",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"河南卫视",
|
||||
"河南卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/74925962148a6d31c85808b6cd4e444c2a54bab393d2c5fc85e960b50e22fa86.png?imageMogr2/format/webp",
|
||||
"600002525",
|
||||
"2000296103",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"河北卫视",
|
||||
"河北卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/d545becdc81c60197b08c7f47380705e4665ed3fe55efc8b855e486f6e655378.png?imageMogr2/format/webp",
|
||||
"600002493",
|
||||
"2000293403",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"贵州卫视",
|
||||
"贵州卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/4eb45f4781d33d872af027dc01c941559aab55667dd99cc5c22bef7037807b13.png?imageMogr2/format/webp",
|
||||
"600002490",
|
||||
"2000293303",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"北京卫视",
|
||||
"北京卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/f4f23633c578beea49a3841d88d3490100f029ee349059fa532869db889872c5.png?imageMogr2/format/webp",
|
||||
"600002309",
|
||||
"2000272103",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"黑龙江卫视",
|
||||
"黑龙江卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/d8273ae9be698ce2db21f5b886ecac95a73429593f93713c60ed8c12c38bf0d3.png?imageMogr2/format/webp",
|
||||
"600002498",
|
||||
"2000293903",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"浙江卫视",
|
||||
"浙江卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/a66c836bd98ba3e41a2e9a570d4b9c50dedc6839e9de333e2e78212ad505f37e.png?imageMogr2/format/webp",
|
||||
"600002520",
|
||||
"2000295503",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"安徽卫视",
|
||||
"安徽卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/f35fa04b51b1ee4984b03578b65403570868ebca03c6c01e11b097f999a58d9b.png?imageMogr2/format/webp",
|
||||
"600002532",
|
||||
"2000298003",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"深圳卫视",
|
||||
"深圳卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/d59fec04c902e3581c617136d02d4b9b8c4cbe64272781ddd3525e80c823edb7.png?imageMogr2/format/webp",
|
||||
"600002481",
|
||||
"2000292203",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"四川卫视",
|
||||
"四川卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp",
|
||||
"600002516",
|
||||
"2000295003",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"东南卫视",
|
||||
"福建东南卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp",
|
||||
"600002484",
|
||||
"2000292503",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"海南卫视",
|
||||
"海南卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp",
|
||||
"600002506",
|
||||
"2000291503",
|
||||
ProgramType.Y_PROTO,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"天津卫视",
|
||||
"天津卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
R.drawable.tianjin,
|
||||
"600152137",
|
||||
"2019927003",
|
||||
ProgramType.Y_JCE,
|
||||
true,
|
||||
mustToken = false
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"新疆卫视",
|
||||
"新疆卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
R.drawable.xinjiang,
|
||||
"600152138",
|
||||
"2019927403",
|
||||
ProgramType.Y_JCE,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"兵团卫视",
|
||||
"兵团卫视",
|
||||
listOf(),
|
||||
"地方",
|
||||
R.drawable.bingtuan,
|
||||
"600170344",
|
||||
"2022606701",
|
||||
ProgramType.Y_JCE,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.8F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CETV1",
|
||||
"CETV1",
|
||||
listOf(),
|
||||
"地方",
|
||||
R.drawable.cetv1,
|
||||
"600171827",
|
||||
"2022823801",
|
||||
ProgramType.Y_JCE,
|
||||
true,
|
||||
mustToken = false,
|
||||
0.6F
|
||||
),
|
||||
),
|
||||
"港澳台" to listOf(
|
||||
TV(
|
||||
0,
|
||||
"凤凰卫视资讯台",
|
||||
"",
|
||||
listOf(),
|
||||
"港澳台",
|
||||
"http://c1.fengshows-cdn.com/a/2021_22/79dcc3a9da358a3.png",
|
||||
"7c96b084-60e1-40a9-89c5-682b994fb680",
|
||||
"",
|
||||
ProgramType.F,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.7F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"凤凰卫视中文台",
|
||||
"",
|
||||
listOf(),
|
||||
"港澳台",
|
||||
"http://c1.fengshows-cdn.com/a/2021_22/ede3d9e09be28e5.png",
|
||||
"f7f48462-9b13-485b-8101-7b54716411ec",
|
||||
"",
|
||||
ProgramType.F,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.7F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"凤凰卫视香港台",
|
||||
"",
|
||||
listOf(),
|
||||
"港澳台",
|
||||
"http://c1.fengshows-cdn.com/a/2021_23/325d941090bee17.png",
|
||||
"15e02d92-1698-416c-af2f-3e9a872b4d78",
|
||||
"",
|
||||
ProgramType.F,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.7F
|
||||
),
|
||||
),
|
||||
"国际" to listOf(
|
||||
TV(
|
||||
0,
|
||||
"CGTN",
|
||||
"CGTN",
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp",
|
||||
"600014550",
|
||||
"2022575001",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CGTN 法语频道",
|
||||
"CGTN法语频道",
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp",
|
||||
"600084704",
|
||||
"2022574901",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CGTN 俄语频道",
|
||||
"CGTN俄语频道",
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp",
|
||||
"600084758",
|
||||
"2022574801",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CGTN 阿拉伯语频道",
|
||||
"CGTN阿拉伯语频道",
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp",
|
||||
"600084782",
|
||||
"2022574601",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CGTN 西班牙语频道",
|
||||
"CGTN西班牙语频道",
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp",
|
||||
"600084744",
|
||||
"2022571701",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
TV(
|
||||
0,
|
||||
"CGTN 纪录频道",
|
||||
"CGTN外语纪录频道",
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp",
|
||||
"600084781",
|
||||
"2022574701",
|
||||
ProgramType.Y_PROTO,
|
||||
false,
|
||||
mustToken = false,
|
||||
0.5F
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
val array = arrayOf("央视", "地方")
|
||||
// list = list.filterKeys { it in array }
|
||||
|
||||
val listNew = mutableMapOf<String, List<TV>>()
|
||||
var id = 0
|
||||
list.forEach { (k, v) ->
|
||||
val group = mutableListOf<TV>()
|
||||
v.forEach { v1 ->
|
||||
if (!v1.mustToken) {
|
||||
v1.id = id
|
||||
v1.needToken = false
|
||||
id++
|
||||
group.add(v1)
|
||||
}
|
||||
}
|
||||
if (group.size > 0) {
|
||||
listNew[k] = group
|
||||
}
|
||||
}
|
||||
return listNew
|
||||
}
|
||||
}
|
||||
@@ -3,49 +3,42 @@ package com.lizongying.mytv.models
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.lizongying.mytv.SP
|
||||
|
||||
class TVListViewModel : ViewModel() {
|
||||
|
||||
var maxNum = mutableListOf<Int>()
|
||||
|
||||
private val tvListViewModel = MutableLiveData<MutableList<TVViewModel>>()
|
||||
private val _tvListViewModel = MutableLiveData<MutableList<TVViewModel>>()
|
||||
val tvListViewModel: LiveData<MutableList<TVViewModel>>
|
||||
get() = _tvListViewModel
|
||||
|
||||
private val _itemPosition = MutableLiveData<Int>()
|
||||
val itemPosition: LiveData<Int>
|
||||
get() = _itemPosition
|
||||
|
||||
private val _itemPositionCurrent = MutableLiveData<Int>()
|
||||
val itemPositionCurrent: LiveData<Int>
|
||||
get() = _itemPositionCurrent
|
||||
|
||||
fun getTVListViewModel(): MutableLiveData<MutableList<TVViewModel>> {
|
||||
return tvListViewModel
|
||||
}
|
||||
|
||||
fun addTVViewModel(tvViewModel: TVViewModel) {
|
||||
val currentTVModelList = tvListViewModel.value ?: mutableListOf()
|
||||
currentTVModelList.add(tvViewModel)
|
||||
tvListViewModel.value = currentTVModelList
|
||||
if (_tvListViewModel.value == null) {
|
||||
_tvListViewModel.value = mutableListOf(tvViewModel)
|
||||
} else {
|
||||
_tvListViewModel.value?.add(tvViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun getTVViewModel(id: Int): TVViewModel? {
|
||||
return tvListViewModel.value?.get(id)
|
||||
}
|
||||
|
||||
fun getTVViewModelCurrent(): TVViewModel? {
|
||||
return _itemPositionCurrent.value?.let { tvListViewModel.value?.get(it) }
|
||||
return _tvListViewModel.value?.get(id)
|
||||
}
|
||||
|
||||
fun setItemPosition(position: Int) {
|
||||
_itemPosition.value = position
|
||||
_itemPositionCurrent.value = position
|
||||
}
|
||||
|
||||
fun setItemPositionCurrent(position: Int) {
|
||||
_itemPositionCurrent.value = position
|
||||
SP.itemPosition = position
|
||||
}
|
||||
|
||||
fun size(): Int {
|
||||
return tvListViewModel.value!!.size
|
||||
if (_tvListViewModel.value == null) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return _tvListViewModel.value!!.size
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,37 @@
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.datasource.DefaultHttpDataSource
|
||||
import androidx.media3.exoplayer.hls.HlsMediaSource
|
||||
import com.lizongying.mytv.TV
|
||||
import com.lizongying.mytv.Utils.getDateTimestamp
|
||||
import com.lizongying.mytv.api.FEPG
|
||||
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.programModel.Program
|
||||
import com.tencent.videolite.android.datamodel.cctvjce.TVProgram
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.TimeZone
|
||||
|
||||
class TVViewModel(private var tv: TV) : ViewModel() {
|
||||
|
||||
private var rowPosition: Int = 0
|
||||
private var itemPosition: Int = 0
|
||||
|
||||
var retryTimes: Int = 0
|
||||
var retryMaxTimes: Int = 5
|
||||
var programUpdateTime: Long = 0
|
||||
var retryTimes = 0
|
||||
var retryMaxTimes = 8
|
||||
var authYSPRetryTimes = 0
|
||||
var authYSPRetryMaxTimes = 3
|
||||
var tokenYSPRetryTimes = 0
|
||||
var tokenYSPRetryMaxTimes = 0
|
||||
var tokenFHRetryTimes = 0
|
||||
var tokenFHRetryMaxTimes = 8
|
||||
|
||||
var needGetToken = false
|
||||
|
||||
private val _errInfo = MutableLiveData<String>()
|
||||
val errInfo: LiveData<String>
|
||||
get() = _errInfo
|
||||
|
||||
private val _programId = MutableLiveData<String>()
|
||||
val programId: LiveData<String>
|
||||
get() = _programId
|
||||
|
||||
private var _program = MutableLiveData<MutableList<Program>>()
|
||||
val program: LiveData<MutableList<Program>>
|
||||
get() = _program
|
||||
|
||||
private val _id = MutableLiveData<Int>()
|
||||
val id: LiveData<Int>
|
||||
get() = _id
|
||||
|
||||
private val _title = MutableLiveData<String>()
|
||||
val title: LiveData<String>
|
||||
get() = _title
|
||||
private var _epg = MutableLiveData<MutableList<EPG>>()
|
||||
val epg: LiveData<MutableList<EPG>>
|
||||
get() = _epg
|
||||
|
||||
private val _videoUrl = MutableLiveData<List<String>>()
|
||||
val videoUrl: LiveData<List<String>>
|
||||
@@ -51,105 +41,45 @@ class TVViewModel(private var tv: TV) : ViewModel() {
|
||||
val videoIndex: LiveData<Int>
|
||||
get() = _videoIndex
|
||||
|
||||
private val _logo = MutableLiveData<String>()
|
||||
val logo: LiveData<String>
|
||||
get() = _logo
|
||||
|
||||
private val _pid = MutableLiveData<String>()
|
||||
val pid: LiveData<String>
|
||||
get() = _pid
|
||||
|
||||
private val _sid = MutableLiveData<String>()
|
||||
val sid: LiveData<String>
|
||||
get() = _sid
|
||||
|
||||
private val _change = MutableLiveData<Boolean>()
|
||||
val change: LiveData<Boolean>
|
||||
private val _change = MutableLiveData<String>()
|
||||
val change: LiveData<String>
|
||||
get() = _change
|
||||
|
||||
private val _ready = MutableLiveData<Boolean>()
|
||||
val ready: LiveData<Boolean>
|
||||
get() = _ready
|
||||
|
||||
private var mMinimumLoadableRetryCount = 5
|
||||
|
||||
var seq = 0
|
||||
|
||||
var needToken = false
|
||||
|
||||
private val channelsNeedToken = arrayOf(
|
||||
"CCTV3 综艺",
|
||||
"CCTV6 电影",
|
||||
"CCTV8 电视剧",
|
||||
"风云剧场",
|
||||
"第一剧场",
|
||||
"怀旧剧场",
|
||||
"世界地理",
|
||||
"风云音乐",
|
||||
"兵器科技",
|
||||
"风云足球",
|
||||
"高尔夫网球",
|
||||
"女性时尚",
|
||||
"央视文化精品",
|
||||
"央视台球",
|
||||
"电视指南",
|
||||
"卫生健康",
|
||||
)
|
||||
|
||||
fun addVideoUrl(url: String) {
|
||||
if (_videoUrl.value?.isNotEmpty() == true) {
|
||||
if (_videoUrl.value!!.last().contains("cctv.cn")) {
|
||||
tv.videoUrl = tv.videoUrl.subList(0, tv.videoUrl.lastIndex) + listOf(url)
|
||||
} else {
|
||||
tv.videoUrl = tv.videoUrl + listOf(url)
|
||||
tv.videoUrl += listOf(url)
|
||||
}
|
||||
} else {
|
||||
tv.videoUrl = tv.videoUrl + listOf(url)
|
||||
tv.videoUrl += listOf(url)
|
||||
}
|
||||
tv.videoIndex = tv.videoUrl.lastIndex
|
||||
_videoUrl.value = tv.videoUrl
|
||||
_videoIndex.value = tv.videoIndex
|
||||
_videoIndex.value = tv.videoUrl.lastIndex
|
||||
}
|
||||
|
||||
fun firstSource() {
|
||||
if (_videoUrl.value!!.isNotEmpty()) {
|
||||
setVideoIndex(0)
|
||||
allReady()
|
||||
} else {
|
||||
Log.e(TAG, "no first")
|
||||
}
|
||||
}
|
||||
|
||||
fun changed() {
|
||||
_change.value = true
|
||||
fun changed(from: String) {
|
||||
retryTimes = 0
|
||||
authYSPRetryTimes = 0
|
||||
tokenYSPRetryTimes = 0
|
||||
tokenFHRetryTimes = 0
|
||||
_change.value = from
|
||||
}
|
||||
|
||||
fun allReady() {
|
||||
_ready.value = true
|
||||
}
|
||||
|
||||
fun setVideoIndex(videoIndex: Int) {
|
||||
_videoIndex.value = videoIndex
|
||||
}
|
||||
|
||||
fun setLogo(url: String) {
|
||||
_logo.value = url
|
||||
}
|
||||
|
||||
init {
|
||||
_id.value = tv.id
|
||||
_title.value = tv.title
|
||||
_videoUrl.value = tv.videoUrl
|
||||
_videoIndex.value = tv.videoIndex
|
||||
_logo.value = tv.logo
|
||||
_programId.value = tv.programId
|
||||
_pid.value = tv.pid
|
||||
_sid.value = tv.sid
|
||||
_program.value = mutableListOf()
|
||||
|
||||
if (tv.title in channelsNeedToken) {
|
||||
needToken = true
|
||||
}
|
||||
_videoIndex.value = tv.videoUrl.lastIndex
|
||||
}
|
||||
|
||||
fun getRowPosition(): Int {
|
||||
@@ -172,66 +102,34 @@ class TVViewModel(private var tv: TV) : ViewModel() {
|
||||
_errInfo.value = info
|
||||
}
|
||||
|
||||
fun update(t: TV) {
|
||||
tv = t
|
||||
}
|
||||
|
||||
fun getTV(): TV {
|
||||
return tv
|
||||
}
|
||||
|
||||
fun getProgramOne(): Program? {
|
||||
val programNew =
|
||||
(_program.value?.filter { it.et > getDateTimestamp() })?.toMutableList()
|
||||
if (programNew != null && _program.value != programNew) {
|
||||
_program.value = programNew
|
||||
}
|
||||
if (_program.value!!.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
return _program.value?.first()
|
||||
fun addYJceEPG(p: MutableList<TVProgram>) {
|
||||
_epg.value = p.map { EPG(it.name, it.start_time_stamp.toInt()) }.toMutableList()
|
||||
}
|
||||
|
||||
fun addProgram(p: MutableList<Program>) {
|
||||
val timestamp = getDateTimestamp()
|
||||
fun addYEPG(p: MutableList<Program>) {
|
||||
_epg.value = p.map { EPG(it.name, it.st.toInt()) }.toMutableList()
|
||||
}
|
||||
|
||||
// after now & not empty & different
|
||||
val p1 = (p.filter { it.et > timestamp }).toMutableList()
|
||||
if (p1.isEmpty() || _program.value == p1) {
|
||||
return
|
||||
}
|
||||
|
||||
if (_program.value!!.isEmpty()) {
|
||||
_program.value = p1
|
||||
} else {
|
||||
_program.value =
|
||||
((_program.value?.filter { it.et > timestamp && it.st < p1.first().st })?.plus(
|
||||
p1
|
||||
))?.toMutableList()
|
||||
private fun formatFTime(s: String): Int {
|
||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
|
||||
dateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
val date = dateFormat.parse(s.substring(0, 19))
|
||||
if (date != null) {
|
||||
return (date.time / 1000).toInt()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
private var mHeaders: Map<String, String>? = mapOf()
|
||||
|
||||
fun setHeaders(headers: Map<String, String>) {
|
||||
mHeaders = headers
|
||||
fun addFEPG(p: List<FEPG>) {
|
||||
_epg.value = p.map { EPG(it.title, formatFTime(it.event_time)) }.toMutableList()
|
||||
}
|
||||
|
||||
fun setMinimumLoadableRetryCount(minimumLoadableRetryCount: Int) {
|
||||
mMinimumLoadableRetryCount = minimumLoadableRetryCount
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
fun buildSource(videoUrl: String, mHeaders: Map<String, String>?): HlsMediaSource {
|
||||
val httpDataSource = DefaultHttpDataSource.Factory()
|
||||
mHeaders?.let { httpDataSource.setDefaultRequestProperties(it) }
|
||||
|
||||
return HlsMediaSource.Factory(httpDataSource).createMediaSource(
|
||||
MediaItem.fromUri(
|
||||
Uri.parse(videoUrl)
|
||||
)
|
||||
)
|
||||
fun getVideoUrlCurrent(): String {
|
||||
return _videoUrl.value!![_videoIndex.value!!]
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.lizongying.mytv.requests
|
||||
|
||||
import com.lizongying.mytv.api.ApiClient
|
||||
import com.lizongying.mytv.api.ReleaseV2
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class ReleaseRequest {
|
||||
private var releaseService = ApiClient().releaseService
|
||||
|
||||
suspend fun getRelease(): ReleaseResponse? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
fetchRelease()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchRelease(): ReleaseResponse? {
|
||||
return suspendCoroutine { continuation ->
|
||||
releaseService.getRelease()
|
||||
.enqueue(object : Callback<ReleaseResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<ReleaseResponse>,
|
||||
response: Response<ReleaseResponse>
|
||||
) {
|
||||
if (response.isSuccessful) {
|
||||
continuation.resume(response.body())
|
||||
} else {
|
||||
continuation.resume(null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<ReleaseResponse>, t: Throwable) {
|
||||
continuation.resume(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ReleaseRequest"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.lizongying.mytv.requests
|
||||
|
||||
|
||||
data class ReleaseResponse(
|
||||
val version_code: Int?,
|
||||
val version_name: String?,
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lizongying.mytv.requests
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.GET
|
||||
|
||||
interface ReleaseService {
|
||||
@GET("JELLY_BEAN_MR1/version.json")
|
||||
fun getRelease(
|
||||
): Call<ReleaseResponse>
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
package com.qq.taf.jce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
public class HexUtil {
|
||||
private static final char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
public static final byte[] emptybytes = new byte[0];
|
||||
|
||||
public static String byte2HexStr(byte b2) {
|
||||
char[] cArr = digits;
|
||||
return new String(new char[]{cArr[((byte) (b2 >>> 4)) & 15], cArr[b2 & 15]});
|
||||
}
|
||||
|
||||
public static String bytes2HexStr(ByteBuffer byteBuffer) {
|
||||
ByteBuffer duplicate = byteBuffer.duplicate();
|
||||
duplicate.flip();
|
||||
byte[] bArr = new byte[duplicate.limit()];
|
||||
duplicate.get(bArr);
|
||||
return bytes2HexStr(bArr);
|
||||
}
|
||||
|
||||
public static byte char2Byte(char c2) {
|
||||
int i2;
|
||||
if (c2 < '0' || c2 > '9') {
|
||||
char c3 = 'a';
|
||||
if (c2 < 'a' || c2 > 'f') {
|
||||
c3 = 'A';
|
||||
if (c2 < 'A' || c2 > 'F') {
|
||||
return (byte) 0;
|
||||
}
|
||||
}
|
||||
i2 = (c2 - c3) + 10;
|
||||
} else {
|
||||
i2 = c2 - '0';
|
||||
}
|
||||
return (byte) i2;
|
||||
}
|
||||
|
||||
public static byte hexStr2Byte(String str) {
|
||||
if (str == null || str.length() != 1) {
|
||||
return (byte) 0;
|
||||
}
|
||||
return char2Byte(str.charAt(0));
|
||||
}
|
||||
|
||||
public static byte[] hexStr2Bytes(String str) {
|
||||
if (str != null && !str.equals("")) {
|
||||
int length = str.length() / 2;
|
||||
byte[] bArr = new byte[length];
|
||||
for (int i2 = 0; i2 < length; i2++) {
|
||||
int i3 = i2 * 2;
|
||||
bArr[i2] = (byte) ((char2Byte(str.charAt(i3)) * 16) + char2Byte(str.charAt(i3 + 1)));
|
||||
}
|
||||
return bArr;
|
||||
}
|
||||
return emptybytes;
|
||||
}
|
||||
|
||||
public static String bytes2HexStr(byte[] bArr) {
|
||||
if (bArr == null || bArr.length == 0) {
|
||||
return null;
|
||||
}
|
||||
char[] cArr = new char[bArr.length * 2];
|
||||
for (int i2 = 0; i2 < bArr.length; i2++) {
|
||||
byte b2 = bArr[i2];
|
||||
int i3 = i2 * 2;
|
||||
char[] cArr2 = digits;
|
||||
cArr[i3 + 1] = cArr2[b2 & 15];
|
||||
cArr[i3 + 0] = cArr2[((byte) (b2 >>> 4)) & 15];
|
||||
}
|
||||
return new String(cArr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.qq.taf.jce;
|
||||
|
||||
public class JceDecodeException extends RuntimeException {
|
||||
public JceDecodeException(String str) {
|
||||
super(str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.qq.taf.jce;
|
||||
|
||||
public class JceEncodeException extends RuntimeException {
|
||||
public JceEncodeException(String str) {
|
||||
super(str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,896 @@
|
||||
package com.qq.taf.jce;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class JceInputStream {
|
||||
private ByteBuffer bs;
|
||||
protected String sServerEncoding = "GBK";
|
||||
|
||||
|
||||
public static class HeadData {
|
||||
public int tag;
|
||||
public byte type;
|
||||
|
||||
public void clear() {
|
||||
this.type = (byte) 0;
|
||||
this.tag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public JceInputStream() {
|
||||
}
|
||||
|
||||
private int peakHead(HeadData headData) {
|
||||
return readHead(headData, this.bs.duplicate());
|
||||
}
|
||||
|
||||
private <T> T[] readArrayImpl(T t, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read >= 0) {
|
||||
T[] tArr = (T[]) ((Object[]) Array.newInstance(t.getClass(), read));
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
tArr[i3] = (T) read( t, 0, true);
|
||||
}
|
||||
return tArr;
|
||||
}
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
|
||||
public static int readHead(HeadData headData, ByteBuffer byteBuffer) {
|
||||
byte b2 = byteBuffer.get();
|
||||
headData.type = (byte) (b2 & 15);
|
||||
int i2 = (b2 & 240) >> 4;
|
||||
headData.tag = i2;
|
||||
if (i2 == 15) {
|
||||
headData.tag = byteBuffer.get() & 255;
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void skip(int i2) {
|
||||
ByteBuffer byteBuffer = this.bs;
|
||||
byteBuffer.position(byteBuffer.position() + i2);
|
||||
}
|
||||
|
||||
private void skipField() {
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
skipField(headData.type);
|
||||
}
|
||||
|
||||
public JceStruct directRead(JceStruct jceStruct, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JceStruct newInit = jceStruct.newInit();
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 10) {
|
||||
newInit.readFrom(this);
|
||||
skipToStructEnd();
|
||||
return newInit;
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
} catch (Exception e2) {
|
||||
throw new JceDecodeException(e2.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ByteBuffer getBs() {
|
||||
return this.bs;
|
||||
}
|
||||
|
||||
public boolean read(boolean z, int i2, boolean z2) {
|
||||
return read((byte) 0, i2, z2) != 0;
|
||||
}
|
||||
|
||||
public <T> T[] readArray(T[] tArr, int i2, boolean z) {
|
||||
if (tArr != null && tArr.length != 0) {
|
||||
return (T[]) readArrayImpl(tArr[0], i2, z);
|
||||
}
|
||||
throw new JceDecodeException("unable to get type of key and value.");
|
||||
}
|
||||
|
||||
public String readByteString(String str, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 == 6) {
|
||||
int i3 = this.bs.get();
|
||||
if (i3 < 0) {
|
||||
i3 += 256;
|
||||
}
|
||||
byte[] bArr = new byte[i3];
|
||||
this.bs.get(bArr);
|
||||
return HexUtil.bytes2HexStr(bArr);
|
||||
} else if (b2 == 7) {
|
||||
int i4 = this.bs.getInt();
|
||||
if (i4 <= 104857600 && i4 >= 0 && i4 <= this.bs.capacity()) {
|
||||
byte[] bArr2 = new byte[i4];
|
||||
this.bs.get(bArr2);
|
||||
return HexUtil.bytes2HexStr(bArr2);
|
||||
}
|
||||
throw new JceDecodeException("String too long: " + i4);
|
||||
} else {
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
}
|
||||
|
||||
public List readList(int i2, boolean z) {
|
||||
ArrayList arrayList = new ArrayList();
|
||||
if (skipToTag(i2)) {
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read < 0) {
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
HeadData headData2 = new HeadData();
|
||||
readHead(headData2);
|
||||
switch (headData2.type) {
|
||||
case 0:
|
||||
skip(1);
|
||||
break;
|
||||
case 1:
|
||||
skip(2);
|
||||
break;
|
||||
case 2:
|
||||
skip(4);
|
||||
break;
|
||||
case 3:
|
||||
skip(8);
|
||||
break;
|
||||
case 4:
|
||||
skip(4);
|
||||
break;
|
||||
case 5:
|
||||
skip(8);
|
||||
break;
|
||||
case 6:
|
||||
int i4 = this.bs.get();
|
||||
if (i4 < 0) {
|
||||
i4 += 256;
|
||||
}
|
||||
skip(i4);
|
||||
break;
|
||||
case 7:
|
||||
skip(this.bs.getInt());
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
break;
|
||||
case 10:
|
||||
try {
|
||||
JceStruct jceStruct = (JceStruct) Class.forName(JceStruct.class.getName()).getConstructor(new Class[0]).newInstance(new Object[0]);
|
||||
jceStruct.readFrom(this);
|
||||
skipToStructEnd();
|
||||
arrayList.add(jceStruct);
|
||||
break;
|
||||
} catch (Exception e2) {
|
||||
e2.printStackTrace();
|
||||
throw new JceDecodeException("type mismatch." + e2);
|
||||
}
|
||||
case 11:
|
||||
default:
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
case 12:
|
||||
arrayList.add(Integer.valueOf(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
} else if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
public <K, V> HashMap<K, V> readMap(Map<K, V> map, int i2, boolean z) {
|
||||
return (HashMap) readMap(new HashMap(), map, i2, z);
|
||||
}
|
||||
|
||||
public String readString(int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 == 6) {
|
||||
int i3 = this.bs.get();
|
||||
if (i3 < 0) {
|
||||
i3 += 256;
|
||||
}
|
||||
byte[] bArr = new byte[i3];
|
||||
this.bs.get(bArr);
|
||||
try {
|
||||
return new String(bArr, this.sServerEncoding);
|
||||
} catch (UnsupportedEncodingException unused) {
|
||||
return new String(bArr);
|
||||
}
|
||||
} else if (b2 == 7) {
|
||||
int i4 = this.bs.getInt();
|
||||
if (i4 <= 104857600 && i4 >= 0 && i4 <= this.bs.capacity()) {
|
||||
byte[] bArr2 = new byte[i4];
|
||||
this.bs.get(bArr2);
|
||||
try {
|
||||
return new String(bArr2, this.sServerEncoding);
|
||||
} catch (UnsupportedEncodingException unused2) {
|
||||
return new String(bArr2);
|
||||
}
|
||||
}
|
||||
throw new JceDecodeException("String too long: " + i4);
|
||||
} else {
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> readStringMap(int i2, boolean z) {
|
||||
HashMap hashMap = new HashMap();
|
||||
if (skipToTag(i2)) {
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 8) {
|
||||
int read = read(0, 0, true);
|
||||
if (read < 0) {
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
hashMap.put(readString(0, true), readString(1, true));
|
||||
}
|
||||
} else {
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
} else if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return hashMap;
|
||||
}
|
||||
|
||||
public int setServerEncoding(String str) {
|
||||
this.sServerEncoding = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void skipToStructEnd() {
|
||||
HeadData headData = new HeadData();
|
||||
do {
|
||||
readHead(headData);
|
||||
skipField(headData.type);
|
||||
} while (headData.type != 11);
|
||||
}
|
||||
|
||||
public boolean skipToTag(int i2) {
|
||||
try {
|
||||
HeadData headData = new HeadData();
|
||||
while (true) {
|
||||
int peakHead = peakHead(headData);
|
||||
if (i2 <= headData.tag || headData.type == 11) {
|
||||
break;
|
||||
}
|
||||
skip(peakHead);
|
||||
skipField(headData.type);
|
||||
}
|
||||
if (headData.type == 11) {
|
||||
return false;
|
||||
}
|
||||
return i2 == headData.tag;
|
||||
} catch (JceDecodeException | BufferUnderflowException unused) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void warp(byte[] bArr) {
|
||||
wrap(bArr);
|
||||
}
|
||||
|
||||
public void wrap(byte[] bArr) {
|
||||
this.bs = ByteBuffer.wrap(bArr);
|
||||
}
|
||||
|
||||
|
||||
private <K, V> Map<K, V> readMap(Map<K, V> map, Map<K, V> map2, int i2, boolean z) {
|
||||
if (map2 != null && !map2.isEmpty()) {
|
||||
Map.Entry<K, V> next = map2.entrySet().iterator().next();
|
||||
K key = next.getKey();
|
||||
V value = next.getValue();
|
||||
if (skipToTag(i2)) {
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 8) {
|
||||
int read = read(0, 0, true);
|
||||
if (read < 0) {
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
map.put((K)read( key, 0, true), (V)read(value, 1, true));
|
||||
}
|
||||
} else {
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
} else if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return map;
|
||||
}
|
||||
return new HashMap();
|
||||
}
|
||||
|
||||
public byte read(byte b2, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return b2;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b3 = headData.type;
|
||||
if (b3 != 0) {
|
||||
if (b3 == 12) {
|
||||
return (byte) 0;
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
return this.bs.get();
|
||||
}
|
||||
|
||||
public JceInputStream(ByteBuffer byteBuffer) {
|
||||
this.bs = byteBuffer;
|
||||
}
|
||||
|
||||
private void skipField(byte b2) {
|
||||
int i2 = 0;
|
||||
switch (b2) {
|
||||
case 0:
|
||||
skip(1);
|
||||
return;
|
||||
case 1:
|
||||
skip(2);
|
||||
return;
|
||||
case 2:
|
||||
skip(4);
|
||||
return;
|
||||
case 3:
|
||||
skip(8);
|
||||
return;
|
||||
case 4:
|
||||
skip(4);
|
||||
return;
|
||||
case 5:
|
||||
skip(8);
|
||||
return;
|
||||
case 6:
|
||||
int i3 = this.bs.get();
|
||||
if (i3 < 0) {
|
||||
i3 += 256;
|
||||
}
|
||||
skip(i3);
|
||||
return;
|
||||
case 7:
|
||||
skip(this.bs.getInt());
|
||||
return;
|
||||
case 8:
|
||||
int read = read(0, 0, true);
|
||||
while (i2 < read * 2) {
|
||||
skipField();
|
||||
i2++;
|
||||
}
|
||||
return;
|
||||
case 9:
|
||||
int read2 = read(0, 0, true);
|
||||
while (i2 < read2) {
|
||||
skipField();
|
||||
i2++;
|
||||
}
|
||||
return;
|
||||
case 10:
|
||||
skipToStructEnd();
|
||||
return;
|
||||
case 11:
|
||||
case 12:
|
||||
return;
|
||||
case 13:
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 0) {
|
||||
skip(read(0, 0, true));
|
||||
return;
|
||||
}
|
||||
throw new JceDecodeException("skipField with invalid type, type value: " + ((int) b2) + ", " + ((int) headData.type));
|
||||
default:
|
||||
throw new JceDecodeException("invalid type.");
|
||||
}
|
||||
}
|
||||
|
||||
public <T> List<T> readArray(List<T> list, int i2, boolean z) {
|
||||
if (list != null && !list.isEmpty()) {
|
||||
Object[] readArrayImpl = readArrayImpl(list.get(0), i2, z);
|
||||
if (readArrayImpl == null) {
|
||||
return null;
|
||||
}
|
||||
ArrayList arrayList = new ArrayList();
|
||||
for (Object obj : readArrayImpl) {
|
||||
arrayList.add(obj);
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
return new ArrayList();
|
||||
}
|
||||
|
||||
public void readHead(HeadData headData) {
|
||||
readHead(headData, this.bs);
|
||||
}
|
||||
|
||||
public JceInputStream(byte[] bArr) {
|
||||
this.bs = ByteBuffer.wrap(bArr);
|
||||
}
|
||||
|
||||
public JceInputStream(byte[] bArr, int i2) {
|
||||
ByteBuffer wrap = ByteBuffer.wrap(bArr);
|
||||
this.bs = wrap;
|
||||
wrap.position(i2);
|
||||
}
|
||||
|
||||
public short read(short s, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 != 0) {
|
||||
if (b2 != 1) {
|
||||
if (b2 == 12) {
|
||||
return (short) 0;
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
return this.bs.getShort();
|
||||
}
|
||||
return this.bs.get();
|
||||
}
|
||||
|
||||
public int read(int i2, int i3, boolean z) {
|
||||
if (!skipToTag(i3)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return i2;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 != 0) {
|
||||
if (b2 != 1) {
|
||||
if (b2 != 2) {
|
||||
if (b2 == 12) {
|
||||
return 0;
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
return this.bs.getInt();
|
||||
}
|
||||
return this.bs.getShort();
|
||||
}
|
||||
return this.bs.get();
|
||||
}
|
||||
|
||||
public long read(long j, int i2, boolean z) {
|
||||
int i3;
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return j;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 == 0) {
|
||||
i3 = this.bs.get();
|
||||
} else if (b2 == 1) {
|
||||
i3 = this.bs.getShort();
|
||||
} else if (b2 != 2) {
|
||||
if (b2 != 3) {
|
||||
if (b2 == 12) {
|
||||
return 0L;
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
return this.bs.getLong();
|
||||
} else {
|
||||
i3 = this.bs.getInt();
|
||||
}
|
||||
return i3;
|
||||
}
|
||||
|
||||
public float read(float f2, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return f2;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 != 4) {
|
||||
if (b2 == 12) {
|
||||
return 0.0f;
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
return this.bs.getFloat();
|
||||
}
|
||||
|
||||
public double read(double d2, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return d2;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 != 4) {
|
||||
if (b2 != 5) {
|
||||
if (b2 == 12) {
|
||||
return 0.0d;
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
return this.bs.getDouble();
|
||||
}
|
||||
return this.bs.getFloat();
|
||||
}
|
||||
|
||||
public String read(String str, int i2, boolean z) {
|
||||
String str2;
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 == 6) {
|
||||
int i3 = this.bs.get();
|
||||
if (i3 < 0) {
|
||||
i3 += 256;
|
||||
}
|
||||
byte[] bArr = new byte[i3];
|
||||
this.bs.get(bArr);
|
||||
try {
|
||||
str2 = new String(bArr, this.sServerEncoding);
|
||||
} catch (UnsupportedEncodingException unused) {
|
||||
str2 = new String(bArr);
|
||||
}
|
||||
} else if (b2 == 7) {
|
||||
int i4 = this.bs.getInt();
|
||||
if (i4 <= 104857600 && i4 >= 0 && i4 <= this.bs.capacity()) {
|
||||
byte[] bArr2 = new byte[i4];
|
||||
this.bs.get(bArr2);
|
||||
try {
|
||||
str2 = new String(bArr2, this.sServerEncoding);
|
||||
} catch (UnsupportedEncodingException unused2) {
|
||||
str2 = new String(bArr2);
|
||||
}
|
||||
} else {
|
||||
throw new JceDecodeException("String too long: " + i4);
|
||||
}
|
||||
} else {
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
return str2;
|
||||
}
|
||||
|
||||
public String[] read(String[] strArr, int i2, boolean z) {
|
||||
return (String[]) readArray(strArr, i2, z);
|
||||
}
|
||||
|
||||
public boolean[] read(boolean[] zArr, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read >= 0) {
|
||||
boolean[] zArr2 = new boolean[read];
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
zArr2[i3] = read(zArr2[0], 0, true);
|
||||
}
|
||||
return zArr2;
|
||||
}
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
|
||||
public byte[] read(byte[] bArr, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
byte b2 = headData.type;
|
||||
if (b2 == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read >= 0 && read <= this.bs.capacity()) {
|
||||
byte[] bArr2 = new byte[read];
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
bArr2[i3] = read(bArr2[0], 0, true);
|
||||
}
|
||||
return bArr2;
|
||||
}
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
} else if (b2 == 13) {
|
||||
HeadData headData2 = new HeadData();
|
||||
readHead(headData2);
|
||||
if (headData2.type == 0) {
|
||||
int read2 = read(0, 0, true);
|
||||
if (read2 >= 0 && read2 <= this.bs.capacity()) {
|
||||
byte[] bArr3 = new byte[read2];
|
||||
this.bs.get(bArr3);
|
||||
return bArr3;
|
||||
}
|
||||
throw new JceDecodeException("invalid size, tag: " + i2 + ", type: " + ((int) headData.type) + ", " + ((int) headData2.type) + ", size: " + read2);
|
||||
}
|
||||
throw new JceDecodeException("type mismatch, tag: " + i2 + ", type: " + ((int) headData.type) + ", " + ((int) headData2.type));
|
||||
} else {
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
}
|
||||
|
||||
public short[] read(short[] sArr, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read >= 0) {
|
||||
short[] sArr2 = new short[read];
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
sArr2[i3] = read(sArr2[0], 0, true);
|
||||
}
|
||||
return sArr2;
|
||||
}
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
|
||||
public int[] read(int[] iArr, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read >= 0) {
|
||||
int[] iArr2 = new int[read];
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
iArr2[i3] = read(iArr2[0], 0, true);
|
||||
}
|
||||
return iArr2;
|
||||
}
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
|
||||
public long[] read(long[] jArr, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read >= 0) {
|
||||
long[] jArr2 = new long[read];
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
jArr2[i3] = read(jArr2[0], 0, true);
|
||||
}
|
||||
return jArr2;
|
||||
}
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
|
||||
public float[] read(float[] fArr, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read >= 0) {
|
||||
float[] fArr2 = new float[read];
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
fArr2[i3] = read(fArr2[0], 0, true);
|
||||
}
|
||||
return fArr2;
|
||||
}
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
|
||||
public double[] read(double[] dArr, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 9) {
|
||||
int read = read(0, 0, true);
|
||||
if (read >= 0) {
|
||||
double[] dArr2 = new double[read];
|
||||
for (int i3 = 0; i3 < read; i3++) {
|
||||
dArr2[i3] = read(dArr2[0], 0, true);
|
||||
}
|
||||
return dArr2;
|
||||
}
|
||||
throw new JceDecodeException("size invalid: " + read);
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
}
|
||||
|
||||
public JceStruct read(JceStruct jceStruct, int i2, boolean z) {
|
||||
if (!skipToTag(i2)) {
|
||||
if (z) {
|
||||
throw new JceDecodeException("require field not exist.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JceStruct jceStruct2 = (JceStruct) jceStruct.getClass().newInstance();
|
||||
HeadData headData = new HeadData();
|
||||
readHead(headData);
|
||||
if (headData.type == 10) {
|
||||
jceStruct2.readFrom(this);
|
||||
skipToStructEnd();
|
||||
return jceStruct2;
|
||||
}
|
||||
throw new JceDecodeException("type mismatch.");
|
||||
} catch (Exception e2) {
|
||||
throw new JceDecodeException(e2.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public JceStruct[] read(JceStruct[] jceStructArr, int i2, boolean z) {
|
||||
return (JceStruct[]) readArray(jceStructArr, i2, z);
|
||||
}
|
||||
|
||||
public <T> Object read(T t, int i2, boolean z) {
|
||||
if (t instanceof Byte) {
|
||||
return Byte.valueOf(read((byte) 0, i2, z));
|
||||
}
|
||||
if (t instanceof Boolean) {
|
||||
return Boolean.valueOf(read(false, i2, z));
|
||||
}
|
||||
if (t instanceof Short) {
|
||||
return Short.valueOf(read((short) 0, i2, z));
|
||||
}
|
||||
if (t instanceof Integer) {
|
||||
return Integer.valueOf(read(0, i2, z));
|
||||
}
|
||||
if (t instanceof Long) {
|
||||
return Long.valueOf(read(0L, i2, z));
|
||||
}
|
||||
if (t instanceof Float) {
|
||||
return Float.valueOf(read(0.0f, i2, z));
|
||||
}
|
||||
if (t instanceof Double) {
|
||||
return Double.valueOf(read(0.0d, i2, z));
|
||||
}
|
||||
if (t instanceof String) {
|
||||
return readString(i2, z);
|
||||
}
|
||||
if (t instanceof Map) {
|
||||
return readMap((Map) t, i2, z);
|
||||
}
|
||||
if (t instanceof List) {
|
||||
return readArray((List) t, i2, z);
|
||||
}
|
||||
if (t instanceof JceStruct) {
|
||||
return read((JceStruct) t, i2, z);
|
||||
}
|
||||
if (t.getClass().isArray()) {
|
||||
if (!(t instanceof byte[]) && !(t instanceof Byte[])) {
|
||||
if (t instanceof boolean[]) {
|
||||
return read((boolean[]) null, i2, z);
|
||||
}
|
||||
if (t instanceof short[]) {
|
||||
return read((short[]) null, i2, z);
|
||||
}
|
||||
if (t instanceof int[]) {
|
||||
return read((int[]) null, i2, z);
|
||||
}
|
||||
if (t instanceof long[]) {
|
||||
return read((long[]) null, i2, z);
|
||||
}
|
||||
if (t instanceof float[]) {
|
||||
return read((float[]) null, i2, z);
|
||||
}
|
||||
if (t instanceof double[]) {
|
||||
return read((double[]) null, i2, z);
|
||||
}
|
||||
return readArray((Object[]) t, i2, z);
|
||||
}
|
||||
return read((byte[]) null, i2, z);
|
||||
}
|
||||
throw new JceDecodeException("read object error: unsupport type.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
package com.qq.taf.jce;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JceOutputStream {
|
||||
private ByteBuffer bs;
|
||||
private OnIllegalArgumentException exceptionHandler;
|
||||
protected String sServerEncoding;
|
||||
|
||||
public JceOutputStream(ByteBuffer byteBuffer) {
|
||||
this.sServerEncoding = "GBK";
|
||||
this.bs = byteBuffer;
|
||||
}
|
||||
|
||||
private void writeArray(Object[] objArr, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 9, i2);
|
||||
write(objArr.length, 0);
|
||||
for (Object obj : objArr) {
|
||||
write(obj, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public ByteBuffer getByteBuffer() {
|
||||
return this.bs;
|
||||
}
|
||||
|
||||
public OnIllegalArgumentException getExceptionHandler() {
|
||||
return this.exceptionHandler;
|
||||
}
|
||||
|
||||
public void reserve(int i2) {
|
||||
if (this.bs.remaining() < i2) {
|
||||
int capacity = (this.bs.capacity() + i2) * 2;
|
||||
try {
|
||||
ByteBuffer allocate = ByteBuffer.allocate(capacity);
|
||||
allocate.put(this.bs.array(), 0, this.bs.position());
|
||||
this.bs = allocate;
|
||||
} catch (IllegalArgumentException e2) {
|
||||
OnIllegalArgumentException onIllegalArgumentException = this.exceptionHandler;
|
||||
if (onIllegalArgumentException != null) {
|
||||
onIllegalArgumentException.onException(e2, this.bs, i2, capacity);
|
||||
}
|
||||
throw e2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setExceptionHandler(OnIllegalArgumentException onIllegalArgumentException) {
|
||||
this.exceptionHandler = onIllegalArgumentException;
|
||||
}
|
||||
|
||||
public int setServerEncoding(String str) {
|
||||
this.sServerEncoding = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
byte[] bArr = new byte[this.bs.position()];
|
||||
System.arraycopy(this.bs.array(), 0, bArr, 0, this.bs.position());
|
||||
return bArr;
|
||||
}
|
||||
|
||||
public void write(boolean z, int i2) {
|
||||
write(z ? (byte) 1 : (byte) 0, i2);
|
||||
}
|
||||
|
||||
public void writeByteString(String str, int i2) {
|
||||
reserve(str.length() + 10);
|
||||
byte[] hexStr2Bytes = HexUtil.hexStr2Bytes(str);
|
||||
if (hexStr2Bytes.length > 255) {
|
||||
writeHead((byte) 7, i2);
|
||||
this.bs.putInt(hexStr2Bytes.length);
|
||||
this.bs.put(hexStr2Bytes);
|
||||
return;
|
||||
}
|
||||
writeHead((byte) 6, i2);
|
||||
this.bs.put((byte) hexStr2Bytes.length);
|
||||
this.bs.put(hexStr2Bytes);
|
||||
}
|
||||
|
||||
public void writeHead(byte b2, int i2) {
|
||||
if (i2 < 15) {
|
||||
this.bs.put((byte) (b2 | (i2 << 4)));
|
||||
} else if (i2 < 256) {
|
||||
this.bs.put((byte) (b2 | 240));
|
||||
this.bs.put((byte) i2);
|
||||
} else {
|
||||
throw new JceEncodeException("tag is too large: " + i2);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeStringByte(String str, int i2) {
|
||||
byte[] hexStr2Bytes = HexUtil.hexStr2Bytes(str);
|
||||
reserve(hexStr2Bytes.length + 10);
|
||||
if (hexStr2Bytes.length > 255) {
|
||||
writeHead((byte) 7, i2);
|
||||
this.bs.putInt(hexStr2Bytes.length);
|
||||
this.bs.put(hexStr2Bytes);
|
||||
return;
|
||||
}
|
||||
writeHead((byte) 6, i2);
|
||||
this.bs.put((byte) hexStr2Bytes.length);
|
||||
this.bs.put(hexStr2Bytes);
|
||||
}
|
||||
|
||||
public void write(byte b2, int i2) {
|
||||
reserve(3);
|
||||
if (b2 == 0) {
|
||||
writeHead((byte) 12, i2);
|
||||
return;
|
||||
}
|
||||
writeHead((byte) 0, i2);
|
||||
this.bs.put(b2);
|
||||
}
|
||||
|
||||
public JceOutputStream(int i2) {
|
||||
this.sServerEncoding = "GBK";
|
||||
this.bs = ByteBuffer.allocate(i2);
|
||||
}
|
||||
|
||||
public void write(short s, int i2) {
|
||||
reserve(4);
|
||||
if (s >= -128 && s <= 127) {
|
||||
write((byte) s, i2);
|
||||
return;
|
||||
}
|
||||
writeHead((byte) 1, i2);
|
||||
this.bs.putShort(s);
|
||||
}
|
||||
|
||||
public JceOutputStream() {
|
||||
this(128);
|
||||
}
|
||||
|
||||
public void write(int i2, int i3) {
|
||||
reserve(6);
|
||||
if (i2 >= -32768 && i2 <= 32767) {
|
||||
write((short) i2, i3);
|
||||
return;
|
||||
}
|
||||
writeHead((byte) 2, i3);
|
||||
this.bs.putInt(i2);
|
||||
}
|
||||
|
||||
public void write(long j, int i2) {
|
||||
reserve(10);
|
||||
if (j >= -2147483648L && j <= 2147483647L) {
|
||||
write((int) j, i2);
|
||||
return;
|
||||
}
|
||||
writeHead((byte) 3, i2);
|
||||
this.bs.putLong(j);
|
||||
}
|
||||
|
||||
public void write(float f2, int i2) {
|
||||
reserve(6);
|
||||
writeHead((byte) 4, i2);
|
||||
this.bs.putFloat(f2);
|
||||
}
|
||||
|
||||
public void write(double d2, int i2) {
|
||||
reserve(10);
|
||||
writeHead((byte) 5, i2);
|
||||
this.bs.putDouble(d2);
|
||||
}
|
||||
|
||||
public void write(String str, int i2) {
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = str.getBytes(this.sServerEncoding);
|
||||
} catch (UnsupportedEncodingException unused) {
|
||||
bytes = str.getBytes();
|
||||
}
|
||||
reserve(bytes.length + 10);
|
||||
if (bytes.length > 255) {
|
||||
writeHead((byte) 7, i2);
|
||||
this.bs.putInt(bytes.length);
|
||||
this.bs.put(bytes);
|
||||
return;
|
||||
}
|
||||
writeHead((byte) 6, i2);
|
||||
this.bs.put((byte) bytes.length);
|
||||
this.bs.put(bytes);
|
||||
}
|
||||
|
||||
public <K, V> void write(Map<K, V> map, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 8, i2);
|
||||
write(map == null ? 0 : map.size(), 0);
|
||||
if (map != null) {
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
write(entry.getKey(), 0);
|
||||
write(entry.getValue(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write(boolean[] zArr, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 9, i2);
|
||||
write(zArr.length, 0);
|
||||
for (boolean z : zArr) {
|
||||
write(z, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(byte[] bArr, int i2) {
|
||||
reserve(bArr.length + 8);
|
||||
writeHead((byte) 13, i2);
|
||||
writeHead((byte) 0, 0);
|
||||
write(bArr.length, 0);
|
||||
this.bs.put(bArr);
|
||||
}
|
||||
|
||||
public void write(short[] sArr, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 9, i2);
|
||||
write(sArr.length, 0);
|
||||
for (short s : sArr) {
|
||||
write(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int[] iArr, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 9, i2);
|
||||
write(iArr.length, 0);
|
||||
for (int i3 : iArr) {
|
||||
write(i3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(long[] jArr, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 9, i2);
|
||||
write(jArr.length, 0);
|
||||
for (long j : jArr) {
|
||||
write(j, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(float[] fArr, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 9, i2);
|
||||
write(fArr.length, 0);
|
||||
for (float f2 : fArr) {
|
||||
write(f2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(double[] dArr, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 9, i2);
|
||||
write(dArr.length, 0);
|
||||
for (double d2 : dArr) {
|
||||
write(d2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void write(T[] tArr, int i2) {
|
||||
writeArray(tArr, i2);
|
||||
}
|
||||
|
||||
public <T> void write(Collection<T> collection, int i2) {
|
||||
reserve(8);
|
||||
writeHead((byte) 9, i2);
|
||||
write(collection == null ? 0 : collection.size(), 0);
|
||||
if (collection != null) {
|
||||
for (T t : collection) {
|
||||
write(t, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write(JceStruct jceStruct, int i2) {
|
||||
reserve(2);
|
||||
writeHead((byte) 10, i2);
|
||||
jceStruct.writeTo(this);
|
||||
reserve(2);
|
||||
writeHead((byte) 11, 0);
|
||||
}
|
||||
|
||||
public void write(Byte b2, int i2) {
|
||||
write(b2.byteValue(), i2);
|
||||
}
|
||||
|
||||
public void write(Boolean bool, int i2) {
|
||||
write(bool.booleanValue(), i2);
|
||||
}
|
||||
|
||||
public void write(Short sh, int i2) {
|
||||
write(sh.shortValue(), i2);
|
||||
}
|
||||
|
||||
public void write(Integer num, int i2) {
|
||||
write(num.intValue(), i2);
|
||||
}
|
||||
|
||||
public void write(Long l, int i2) {
|
||||
write(l.longValue(), i2);
|
||||
}
|
||||
|
||||
public void write(Float f2, int i2) {
|
||||
write(f2.floatValue(), i2);
|
||||
}
|
||||
|
||||
public void write(Double d2, int i2) {
|
||||
write(d2.doubleValue(), i2);
|
||||
}
|
||||
|
||||
public void write(Object obj, int i2) {
|
||||
if (obj instanceof Byte) {
|
||||
write(((Byte) obj).byteValue(), i2);
|
||||
} else if (obj instanceof Boolean) {
|
||||
write(((Boolean) obj).booleanValue(), i2);
|
||||
} else if (obj instanceof Short) {
|
||||
write(((Short) obj).shortValue(), i2);
|
||||
} else if (obj instanceof Integer) {
|
||||
write(((Integer) obj).intValue(), i2);
|
||||
} else if (obj instanceof Long) {
|
||||
write(((Long) obj).longValue(), i2);
|
||||
} else if (obj instanceof Float) {
|
||||
write(((Float) obj).floatValue(), i2);
|
||||
} else if (obj instanceof Double) {
|
||||
write(((Double) obj).doubleValue(), i2);
|
||||
} else if (obj instanceof String) {
|
||||
write((String) obj, i2);
|
||||
} else if (obj instanceof Map) {
|
||||
write((Map) obj, i2);
|
||||
} else if (obj instanceof List) {
|
||||
write((Collection) ((List) obj), i2);
|
||||
} else if (obj instanceof JceStruct) {
|
||||
write((JceStruct) obj, i2);
|
||||
} else if (obj instanceof byte[]) {
|
||||
write((byte[]) obj, i2);
|
||||
} else if (obj instanceof boolean[]) {
|
||||
write((boolean[]) obj, i2);
|
||||
} else if (obj instanceof short[]) {
|
||||
write((short[]) obj, i2);
|
||||
} else if (obj instanceof int[]) {
|
||||
write((int[]) obj, i2);
|
||||
} else if (obj instanceof long[]) {
|
||||
write((long[]) obj, i2);
|
||||
} else if (obj instanceof float[]) {
|
||||
write((float[]) obj, i2);
|
||||
} else if (obj instanceof double[]) {
|
||||
write((double[]) obj, i2);
|
||||
} else if (obj.getClass().isArray()) {
|
||||
writeArray((Object[]) obj, i2);
|
||||
} else if (obj instanceof Collection) {
|
||||
write((Collection) obj, i2);
|
||||
} else {
|
||||
throw new JceEncodeException("write object error: unsupport type. " + obj.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.qq.taf.jce;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public abstract class JceStruct implements Serializable {
|
||||
public static final byte BYTE = 0;
|
||||
public static final byte DOUBLE = 5;
|
||||
public static final byte FLOAT = 4;
|
||||
public static final byte INT = 2;
|
||||
public static final int JCE_MAX_STRING_LENGTH = 104857600;
|
||||
public static final byte LIST = 9;
|
||||
public static final byte LONG = 3;
|
||||
public static final byte MAP = 8;
|
||||
public static final byte SHORT = 1;
|
||||
public static final byte SIMPLE_LIST = 13;
|
||||
public static final byte STRING1 = 6;
|
||||
public static final byte STRING4 = 7;
|
||||
public static final byte STRUCT_BEGIN = 10;
|
||||
public static final byte STRUCT_END = 11;
|
||||
public static final byte ZERO_TAG = 12;
|
||||
private Object tag;
|
||||
|
||||
public static String toDisplaySimpleString(JceStruct jceStruct) {
|
||||
if (jceStruct == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
jceStruct.displaySimple(sb, 0);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean containField(String str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void display(StringBuilder sb, int i2) {
|
||||
}
|
||||
|
||||
public void displaySimple(StringBuilder sb, int i2) {
|
||||
}
|
||||
|
||||
public Object getFieldByName(String str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getTag() {
|
||||
return this.tag;
|
||||
}
|
||||
|
||||
public JceStruct newInit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract void readFrom(JceInputStream jceInputStream);
|
||||
|
||||
public void recyle() {
|
||||
}
|
||||
|
||||
public void setFieldByName(String str, Object obj) {
|
||||
}
|
||||
|
||||
public void setTag(Object obj) {
|
||||
this.tag = obj;
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
JceOutputStream jceOutputStream = new JceOutputStream();
|
||||
writeTo(jceOutputStream);
|
||||
return jceOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
display(sb, 0);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public abstract void writeTo(JceOutputStream jceOutputStream);
|
||||
|
||||
public byte[] toByteArray(String str) {
|
||||
JceOutputStream jceOutputStream = new JceOutputStream();
|
||||
jceOutputStream.setServerEncoding(str);
|
||||
writeTo(jceOutputStream);
|
||||
return jceOutputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.qq.taf.jce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface OnIllegalArgumentException {
|
||||
void onException(IllegalArgumentException illegalArgumentException, ByteBuffer byteBuffer, int i2, int i3);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
|
||||
public final class Action extends JceStruct {
|
||||
public String reportEventId;
|
||||
public String reportKey;
|
||||
public String reportParams;
|
||||
public String url;
|
||||
|
||||
public Action() {
|
||||
this.url = "";
|
||||
this.reportEventId = "";
|
||||
this.reportKey = "";
|
||||
this.reportParams = "";
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.url = jceInputStream.readString(0, true);
|
||||
this.reportEventId = jceInputStream.readString(1, false);
|
||||
this.reportKey = jceInputStream.readString(2, false);
|
||||
this.reportParams = jceInputStream.readString(3, false);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
jceOutputStream.write(this.url, 0);
|
||||
String str = this.reportEventId;
|
||||
if (str != null) {
|
||||
jceOutputStream.write(str, 1);
|
||||
}
|
||||
String str2 = this.reportKey;
|
||||
if (str2 != null) {
|
||||
jceOutputStream.write(str2, 2);
|
||||
}
|
||||
String str3 = this.reportParams;
|
||||
if (str3 != null) {
|
||||
jceOutputStream.write(str3, 3);
|
||||
}
|
||||
}
|
||||
|
||||
public Action(String str, String str2, String str3, String str4) {
|
||||
this.url = "";
|
||||
this.reportEventId = "";
|
||||
this.reportKey = "";
|
||||
this.reportParams = "";
|
||||
this.url = str;
|
||||
this.reportEventId = str2;
|
||||
this.reportKey = str3;
|
||||
this.reportParams = str4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
public final class BucketConfig extends JceStruct {
|
||||
public int bucketId;
|
||||
public String extra;
|
||||
|
||||
public BucketConfig() {
|
||||
this.bucketId = 0;
|
||||
this.extra = "";
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.bucketId = jceInputStream.read(this.bucketId, 0, true);
|
||||
this.extra = jceInputStream.readString(1, false);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
jceOutputStream.write(this.bucketId, 0);
|
||||
String str = this.extra;
|
||||
if (str != null) {
|
||||
jceOutputStream.write(str, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public BucketConfig(int i2, String str) {
|
||||
this.bucketId = 0;
|
||||
this.extra = "";
|
||||
this.bucketId = i2;
|
||||
this.extra = str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
public final class BusinessExtent extends JceStruct {
|
||||
public int launchType;
|
||||
|
||||
public BusinessExtent() {
|
||||
this.launchType = 0;
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.launchType = jceInputStream.read(this.launchType, 0, false);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
jceOutputStream.write(this.launchType, 0);
|
||||
}
|
||||
|
||||
public BusinessExtent(int i2) {
|
||||
this.launchType = 0;
|
||||
this.launchType = i2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
public final class BusinessHead extends JceStruct {
|
||||
static byte[] cache_head;
|
||||
public byte[] head;
|
||||
public int type;
|
||||
|
||||
static {
|
||||
cache_head = new byte[0];
|
||||
byte[] bArr = {0};
|
||||
}
|
||||
|
||||
public BusinessHead() {
|
||||
this.type = 0;
|
||||
this.head = null;
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.type = jceInputStream.read(this.type, 0, true);
|
||||
this.head = jceInputStream.read(cache_head, 1, false);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
jceOutputStream.write(this.type, 0);
|
||||
byte[] bArr = this.head;
|
||||
if (bArr != null) {
|
||||
jceOutputStream.write(bArr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public BusinessHead(int i2, byte[] bArr) {
|
||||
this.type = 0;
|
||||
this.head = null;
|
||||
this.type = i2;
|
||||
this.head = bArr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
|
||||
public final class Coordinates extends JceStruct {
|
||||
public double accuracy;
|
||||
public float latitude;
|
||||
public float longitude;
|
||||
public int type;
|
||||
|
||||
public Coordinates() {
|
||||
this.type = 0;
|
||||
this.latitude = 0.0f;
|
||||
this.longitude = 0.0f;
|
||||
this.accuracy = 0.0d;
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.type = jceInputStream.read(this.type, 0, false);
|
||||
this.latitude = jceInputStream.read(this.latitude, 1, false);
|
||||
this.longitude = jceInputStream.read(this.longitude, 2, false);
|
||||
this.accuracy = jceInputStream.read(this.accuracy, 3, false);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
jceOutputStream.write(this.type, 0);
|
||||
jceOutputStream.write(this.latitude, 1);
|
||||
jceOutputStream.write(this.longitude, 2);
|
||||
jceOutputStream.write(this.accuracy, 3);
|
||||
}
|
||||
|
||||
public Coordinates(int i2, float f2, float f3, double d2) {
|
||||
this.type = 0;
|
||||
this.latitude = 0.0f;
|
||||
this.longitude = 0.0f;
|
||||
this.accuracy = 0.0d;
|
||||
this.type = i2;
|
||||
this.latitude = f2;
|
||||
this.longitude = f3;
|
||||
this.accuracy = d2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
public final class ExtentAccount extends JceStruct {
|
||||
public String accountId;
|
||||
public int type;
|
||||
|
||||
public ExtentAccount() {
|
||||
this.type = 0;
|
||||
this.accountId = "";
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.type = jceInputStream.read(this.type, 0, true);
|
||||
this.accountId = jceInputStream.readString(1, true);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
jceOutputStream.write(this.type, 0);
|
||||
jceOutputStream.write(this.accountId, 1);
|
||||
}
|
||||
|
||||
public ExtentAccount(int i2, String str) {
|
||||
this.type = 0;
|
||||
this.accountId = "";
|
||||
this.type = i2;
|
||||
this.accountId = str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
public final class ExtentData extends JceStruct {
|
||||
static BucketConfig cache_bucketInfo = new BucketConfig();
|
||||
public BucketConfig bucketInfo;
|
||||
public int checkFlag;
|
||||
public String extra;
|
||||
public byte flagByte;
|
||||
|
||||
public ExtentData() {
|
||||
this.checkFlag = 0;
|
||||
this.flagByte = (byte) 0;
|
||||
this.extra = "";
|
||||
this.bucketInfo = null;
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.checkFlag = jceInputStream.read(this.checkFlag, 0, false);
|
||||
this.flagByte = jceInputStream.read(this.flagByte, 1, false);
|
||||
this.extra = jceInputStream.readString(2, false);
|
||||
this.bucketInfo = (BucketConfig) jceInputStream.read((JceStruct) cache_bucketInfo, 3, false);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
jceOutputStream.write(this.checkFlag, 0);
|
||||
jceOutputStream.write(this.flagByte, 1);
|
||||
String str = this.extra;
|
||||
if (str != null) {
|
||||
jceOutputStream.write(str, 2);
|
||||
}
|
||||
BucketConfig bucketConfig = this.bucketInfo;
|
||||
if (bucketConfig != null) {
|
||||
jceOutputStream.write((JceStruct) bucketConfig, 3);
|
||||
}
|
||||
}
|
||||
|
||||
public ExtentData(int i2, byte b2, String str, BucketConfig bucketConfig) {
|
||||
this.checkFlag = 0;
|
||||
this.flagByte = (byte) 0;
|
||||
this.extra = "";
|
||||
this.bucketInfo = null;
|
||||
this.checkFlag = i2;
|
||||
this.flagByte = b2;
|
||||
this.extra = str;
|
||||
this.bucketInfo = bucketConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
|
||||
public final class LogReport extends JceStruct {
|
||||
public String callType;
|
||||
public String channelId;
|
||||
public String extent;
|
||||
public String from;
|
||||
public int isAuto;
|
||||
public String mid;
|
||||
public String pageId;
|
||||
public int pageStep;
|
||||
public String pid;
|
||||
public String refPageId;
|
||||
public String vid;
|
||||
|
||||
public LogReport() {
|
||||
this.pageId = "";
|
||||
this.refPageId = "";
|
||||
this.pageStep = 0;
|
||||
this.callType = "";
|
||||
this.isAuto = 0;
|
||||
this.vid = "";
|
||||
this.pid = "";
|
||||
this.from = "";
|
||||
this.channelId = "";
|
||||
this.mid = "";
|
||||
this.extent = "";
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.pageId = jceInputStream.readString(0, false);
|
||||
this.refPageId = jceInputStream.readString(1, false);
|
||||
this.pageStep = jceInputStream.read(this.pageStep, 2, false);
|
||||
this.callType = jceInputStream.readString(3, false);
|
||||
this.isAuto = jceInputStream.read(this.isAuto, 4, false);
|
||||
this.vid = jceInputStream.readString(5, false);
|
||||
this.pid = jceInputStream.readString(6, false);
|
||||
this.from = jceInputStream.readString(7, false);
|
||||
this.channelId = jceInputStream.readString(8, false);
|
||||
this.mid = jceInputStream.readString(9, false);
|
||||
this.extent = jceInputStream.readString(10, false);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
String str = this.pageId;
|
||||
if (str != null) {
|
||||
jceOutputStream.write(str, 0);
|
||||
}
|
||||
String str2 = this.refPageId;
|
||||
if (str2 != null) {
|
||||
jceOutputStream.write(str2, 1);
|
||||
}
|
||||
jceOutputStream.write(this.pageStep, 2);
|
||||
String str3 = this.callType;
|
||||
if (str3 != null) {
|
||||
jceOutputStream.write(str3, 3);
|
||||
}
|
||||
jceOutputStream.write(this.isAuto, 4);
|
||||
String str4 = this.vid;
|
||||
if (str4 != null) {
|
||||
jceOutputStream.write(str4, 5);
|
||||
}
|
||||
String str5 = this.pid;
|
||||
if (str5 != null) {
|
||||
jceOutputStream.write(str5, 6);
|
||||
}
|
||||
String str6 = this.from;
|
||||
if (str6 != null) {
|
||||
jceOutputStream.write(str6, 7);
|
||||
}
|
||||
String str7 = this.channelId;
|
||||
if (str7 != null) {
|
||||
jceOutputStream.write(str7, 8);
|
||||
}
|
||||
String str8 = this.mid;
|
||||
if (str8 != null) {
|
||||
jceOutputStream.write(str8, 9);
|
||||
}
|
||||
String str9 = this.extent;
|
||||
if (str9 != null) {
|
||||
jceOutputStream.write(str9, 10);
|
||||
}
|
||||
}
|
||||
|
||||
public LogReport(String str, String str2, int i2, String str3, int i3, String str4, String str5, String str6, String str7, String str8, String str9) {
|
||||
this.pageId = "";
|
||||
this.refPageId = "";
|
||||
this.pageStep = 0;
|
||||
this.callType = "";
|
||||
this.isAuto = 0;
|
||||
this.vid = "";
|
||||
this.pid = "";
|
||||
this.from = "";
|
||||
this.channelId = "";
|
||||
this.mid = "";
|
||||
this.extent = "";
|
||||
this.pageId = str;
|
||||
this.refPageId = str2;
|
||||
this.pageStep = i2;
|
||||
this.callType = str3;
|
||||
this.isAuto = i3;
|
||||
this.vid = str4;
|
||||
this.pid = str5;
|
||||
this.from = str6;
|
||||
this.channelId = str7;
|
||||
this.mid = str8;
|
||||
this.extent = str9;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.tencent.videolite.android.datamodel.cctvjce;
|
||||
|
||||
import com.qq.taf.jce.JceInputStream;
|
||||
import com.qq.taf.jce.JceOutputStream;
|
||||
import com.qq.taf.jce.JceStruct;
|
||||
|
||||
public final class LoginToken extends JceStruct {
|
||||
static byte[] cache_TokenValue;
|
||||
public boolean IsMainLogin;
|
||||
public String TokenAppID;
|
||||
public byte TokenKeyType;
|
||||
public String TokenUin;
|
||||
public byte[] TokenValue;
|
||||
|
||||
static {
|
||||
cache_TokenValue = new byte[0];
|
||||
byte[] bArr = {0};
|
||||
}
|
||||
|
||||
public LoginToken() {
|
||||
this.TokenAppID = "";
|
||||
this.TokenKeyType = (byte) 0;
|
||||
this.TokenValue = null;
|
||||
this.TokenUin = "";
|
||||
this.IsMainLogin = true;
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void readFrom(JceInputStream jceInputStream) {
|
||||
this.TokenAppID = jceInputStream.readString(0, true);
|
||||
this.TokenKeyType = jceInputStream.read(this.TokenKeyType, 1, true);
|
||||
this.TokenValue = jceInputStream.read(cache_TokenValue, 2, true);
|
||||
this.TokenUin = jceInputStream.readString(3, false);
|
||||
this.IsMainLogin = jceInputStream.read(this.IsMainLogin, 4, false);
|
||||
}
|
||||
|
||||
@Override // com.qq.taf.jce.JceStruct
|
||||
public void writeTo(JceOutputStream jceOutputStream) {
|
||||
jceOutputStream.write(this.TokenAppID, 0);
|
||||
jceOutputStream.write(this.TokenKeyType, 1);
|
||||
jceOutputStream.write(this.TokenValue, 2);
|
||||
String str = this.TokenUin;
|
||||
if (str != null) {
|
||||
jceOutputStream.write(str, 3);
|
||||
}
|
||||
jceOutputStream.write(this.IsMainLogin, 4);
|
||||
}
|
||||
|
||||
public LoginToken(String str, byte b2, byte[] bArr, String str2, boolean z) {
|
||||
this.TokenAppID = "";
|
||||
this.TokenKeyType = (byte) 0;
|
||||
this.TokenValue = null;
|
||||
this.TokenUin = "";
|
||||
this.IsMainLogin = true;
|
||||
this.TokenAppID = str;
|
||||
this.TokenKeyType = b2;
|
||||
this.TokenValue = bArr;
|
||||
this.TokenUin = str2;
|
||||
this.IsMainLogin = z;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user