mirror of
https://github.com/lizongying/my-tv.git
synced 2026-10-26 22:26:29 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39e5cc7c1f | ||
|
|
df5b006024 | ||
|
|
fe888c36ac | ||
|
|
5aa08f51a1 | ||
|
|
fe92fa5853 | ||
|
|
3d8ab65955 | ||
|
|
5485a686bc | ||
|
|
716efb5a83 | ||
|
|
ca82d5409f |
+60
@@ -1,10 +1,70 @@
|
||||
## 更新日志
|
||||
|
||||
### 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(通用)
|
||||
|
||||
* 支持节目列表网格样式和行样式切换,软件重启后生效
|
||||
|
||||
@@ -4,17 +4,16 @@
|
||||
|
||||
## 使用
|
||||
|
||||
下载安装:
|
||||
[github](https://github.com/lizongying/my-tv/releases/)
|
||||
[gitee](https://gitee.com/lizongying/my-tv/releases/)
|
||||
|
||||
小米电视可以使用小米电视助手进行安装
|
||||
|
||||
如电视可以启用ADB,也可以通过ADB进行安装:
|
||||
|
||||
```shell
|
||||
adb install my-tv.apk
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||

|
||||

|
||||
@@ -31,13 +30,20 @@ adb install my-tv.apk
|
||||
* CHC高清三个电影频道
|
||||
* 地方频道
|
||||
* 收藏夹
|
||||
* 自定义源
|
||||
* 海外
|
||||
* 1.5.0 无法安装,1.5.1 可以安装
|
||||
* 节目增加预告
|
||||
* 频道列表优化
|
||||
* epg更新后菜单更新
|
||||
* 防止菜单第一个获取焦点
|
||||
* 隐藏频道
|
||||
* 亮度调节
|
||||
* 音量调节
|
||||
* 軟解
|
||||
* 自動更新
|
||||
|
||||
## 版权说明
|
||||
|
||||
本项目仅供学习研究,禁止用于商业用途,请于下载二十四小时内删除。
|
||||
|
||||
本项目可能随时终止,请大家谨慎使用,建议使用官方渠道进行观看。
|
||||
|
||||
本项目使用的部分代码、图片、文字等资源来源于网络,如有侵权,请联系删除。
|
||||
|
||||
## 赞赏
|
||||
|
||||
|
||||
+8
-3
@@ -112,11 +112,16 @@ dependencies {
|
||||
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.core:core-ktx:1.11.0-beta02'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.leanback:leanback:1.2.0-alpha02'
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC")
|
||||
|
||||
implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.3'
|
||||
|
||||
@@ -1,37 +1,46 @@
|
||||
<?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.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:name=".MyTvApplication"
|
||||
android:allowBackup="true"
|
||||
android:largeHeap="true"
|
||||
android:icon="@drawable/logo"
|
||||
android:logo="@drawable/logo"
|
||||
android:banner="@drawable/banner"
|
||||
android:icon="@drawable/logo"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:largeHeap="true"
|
||||
android:logo="@drawable/logo"
|
||||
android:networkSecurityConfig="@xml/network"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@style/Theme.MyTV">
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MyTV"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:keepScreenOn="true"
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
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">
|
||||
@@ -40,15 +49,19 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".NetworkChangeReceiver"
|
||||
<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"/>
|
||||
android:exported="false" />
|
||||
|
||||
<meta-data android:name="android.max_aspect" android:value="2.5" />
|
||||
</application>
|
||||
</manifest>
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,18 +1,21 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.util.Log
|
||||
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.core.view.updatePadding
|
||||
import androidx.leanback.widget.ImageCardView
|
||||
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(
|
||||
@@ -25,15 +28,31 @@ class CardAdapter(
|
||||
private var listener: ItemListener? = null
|
||||
private var focused: View? = null
|
||||
|
||||
var visiable = false
|
||||
var focusable = false
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val cardView = object :
|
||||
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {}
|
||||
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
|
||||
cardView.updatePadding(1, 1, 1, 1)
|
||||
return ViewHolder(cardView)
|
||||
return ViewHolder(cardView, binding)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
@@ -41,14 +60,14 @@ class CardAdapter(
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
|
||||
private fun startScaleAnimation(view: View, fromScale: Float, toScale: Float, duration: Long) {
|
||||
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 = duration
|
||||
scaleAnimation.duration = 200
|
||||
scaleAnimation.fillAfter = false
|
||||
view.startAnimation(scaleAnimation)
|
||||
}
|
||||
@@ -57,7 +76,7 @@ class CardAdapter(
|
||||
val item = tvListViewModel.getTVViewModel(position)
|
||||
|
||||
val tvViewModel = item as TVViewModel
|
||||
val cardView = viewHolder.view as ImageCardView
|
||||
val cardView = viewHolder.view
|
||||
cardView.tag = tvViewModel
|
||||
|
||||
val onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus ->
|
||||
@@ -65,16 +84,12 @@ class CardAdapter(
|
||||
listener?.onItemHasFocus(item)
|
||||
focused = cardView
|
||||
|
||||
startScaleAnimation(cardView, 0.9f, 1.0f, 200)
|
||||
//
|
||||
// if (mainFragment.shouldHasFocus(view.tag as TVViewModel)) {
|
||||
// }
|
||||
|
||||
// if (visiable) {
|
||||
// startScaleAnimation(cardView, 0.9f, 1.0f, 200)
|
||||
// } else {
|
||||
// visiable = true
|
||||
// }
|
||||
if (focusable) {
|
||||
startScaleAnimation(cardView)
|
||||
viewHolder.focus(true)
|
||||
}
|
||||
} else {
|
||||
viewHolder.focus(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +99,34 @@ class CardAdapter(
|
||||
listener?.onItemClicked(item)
|
||||
}
|
||||
|
||||
var downX = 0f
|
||||
var downY = 0f
|
||||
val touchSlop = ViewConfiguration.get(cardView.context).scaledTouchSlop
|
||||
|
||||
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.setOnKeyListener { _, keyCode, event: KeyEvent? ->
|
||||
if (event?.action == KeyEvent.ACTION_DOWN) {
|
||||
return@setOnKeyListener listener?.onKey(keyCode) ?: false
|
||||
@@ -91,28 +134,62 @@ class CardAdapter(
|
||||
false
|
||||
}
|
||||
|
||||
cardView.titleText = tvViewModel.getTV().title
|
||||
viewHolder.binding.title.text = tvViewModel.getTV().title
|
||||
|
||||
Glide.with(viewHolder.view.context)
|
||||
.load(tvViewModel.getTV().logo)
|
||||
.centerInside()
|
||||
.into(cardView.mainImageView)
|
||||
|
||||
cardView.mainImageView.setBackgroundColor(Color.WHITE)
|
||||
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
|
||||
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()) {
|
||||
cardView.contentText = epg.last().title
|
||||
viewHolder.binding.desc.text = epg.last().title
|
||||
} else {
|
||||
cardView.contentText = ""
|
||||
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) {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
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
|
||||
@@ -14,8 +17,9 @@ class ChannelFragment : Fragment() {
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val handler = Handler()
|
||||
private val delay: Long = 3000
|
||||
private val delay: Long = 5000
|
||||
private var channel = 0
|
||||
private var channelCount = 0
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
@@ -23,24 +27,44 @@ class ChannelFragment : Fragment() {
|
||||
): View {
|
||||
_binding = ChannelBinding.inflate(inflater, container, false)
|
||||
_binding!!.root.visibility = View.GONE
|
||||
(activity as MainActivity).fragmentReady()
|
||||
|
||||
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.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.channelContent.text = (tvViewModel.getTV().id.plus(1)).toString()
|
||||
binding.content.text = (tvViewModel.getTV().id.plus(1)).toString()
|
||||
view?.visibility = View.VISIBLE
|
||||
handler.postDelayed(hideRunnable, delay)
|
||||
}
|
||||
|
||||
fun show(channel: String) {
|
||||
this.channel = "${binding.channelContent.text}$channel".toInt()
|
||||
if (channelCount > 1) {
|
||||
return
|
||||
}
|
||||
channelCount++
|
||||
this.channel = "${this.channel}$channel".toInt()
|
||||
handler.removeCallbacks(hideRunnable)
|
||||
handler.removeCallbacks(playRunnable)
|
||||
if (binding.channelContent.text == "") {
|
||||
binding.channelContent.text = channel
|
||||
if (channelCount < 2) {
|
||||
binding.content.text = "${this.channel}"
|
||||
view?.visibility = View.VISIBLE
|
||||
handler.postDelayed(playRunnable, delay)
|
||||
} else {
|
||||
@@ -62,14 +86,18 @@ class ChannelFragment : Fragment() {
|
||||
}
|
||||
|
||||
private val hideRunnable = Runnable {
|
||||
binding.channelContent.text = ""
|
||||
binding.content.text = ""
|
||||
view?.visibility = View.GONE
|
||||
channel = 0
|
||||
channelCount = 0
|
||||
}
|
||||
|
||||
private val playRunnable = Runnable {
|
||||
(activity as MainActivity).play(channel - 1)
|
||||
binding.channelContent.text = ""
|
||||
binding.content.text = ""
|
||||
view?.visibility = View.GONE
|
||||
channel = 0
|
||||
channelCount = 0
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ val Context.appVersionCode: Long
|
||||
* 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.appVersionName: String get() = packageInfo.versionName
|
||||
|
||||
val Context.appSignature: String
|
||||
get() {
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.leanback.widget.ImageCardView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class GrayOverlayItemDecoration(private val context: Context) : RecyclerView.ItemDecoration() {
|
||||
|
||||
private val grayOverlayPaint = Paint().apply {
|
||||
color = ContextCompat.getColor(context, R.color.gray_overlay)
|
||||
style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
super.onDrawOver(c, parent, state)
|
||||
|
||||
val childCount = parent.childCount
|
||||
for (i in 0 until childCount) {
|
||||
val child = parent.getChildAt(i)
|
||||
if (!child.hasFocus()) {
|
||||
(child as ImageCardView).setInfoAreaBackgroundColor(ContextCompat.getColor(context, R.color.blur))
|
||||
// child.alpha = 0.5f
|
||||
// 计算遮罩层的大小
|
||||
// val overlayRect = Rect(
|
||||
// child.left,
|
||||
// child.top,
|
||||
// child.right,
|
||||
// child.bottom
|
||||
// )
|
||||
// // 绘制灰色遮罩层
|
||||
// c.drawRect(overlayRect, grayOverlayPaint)
|
||||
} else {
|
||||
(child as ImageCardView).setInfoAreaBackgroundColor(ContextCompat.getColor(context, R.color.focus))
|
||||
// child.alpha = 1f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
super.getItemOffsets(outRect, view, parent, state)
|
||||
// 在此处设置偏移量为0,以防止遮罩层影响项的布局
|
||||
outRect.setEmpty()
|
||||
}
|
||||
}
|
||||
@@ -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,31 +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)
|
||||
|
||||
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()
|
||||
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
fun show(tvViewModel: TVViewModel) {
|
||||
binding.textView.text = tvViewModel.getTV().title
|
||||
if (_binding == null) {
|
||||
return
|
||||
}
|
||||
binding.title.text = tvViewModel.getTV().title
|
||||
|
||||
Glide.with(this)
|
||||
.load(tvViewModel.getTV().logo)
|
||||
.into(binding.infoLogo)
|
||||
.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.infoDesc.text = epg.last().title
|
||||
binding.desc.text = epg.last().title
|
||||
} else {
|
||||
binding.infoDesc.text = ""
|
||||
binding.desc.text = ""
|
||||
}
|
||||
|
||||
handler.removeCallbacks(removeRunnable)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,17 @@ 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.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
import com.lizongying.mytv.requests.Request
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
@@ -28,9 +32,13 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
|
||||
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
|
||||
@@ -42,57 +50,79 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
private val delayHideSetting: Long = 10000
|
||||
|
||||
init {
|
||||
Utils.setRequestListener(this)
|
||||
}
|
||||
|
||||
private fun syncTime() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() }
|
||||
|
||||
utilsJob.start()
|
||||
|
||||
// utilsJob.await()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.i(TAG, "onCreate")
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
Request.setRequestListener(this)
|
||||
|
||||
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.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
|
||||
// }
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
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(mainFragment)
|
||||
.hide(errorFragment)
|
||||
// .hide(loadingFragment)
|
||||
.hide(timeFragment)
|
||||
.commit()
|
||||
}
|
||||
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++
|
||||
}
|
||||
// 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++
|
||||
// }
|
||||
|
||||
showTime()
|
||||
mainFragment.changeMenu()
|
||||
}
|
||||
|
||||
fun showInfoFragment(tvViewModel: TVViewModel) {
|
||||
@@ -102,6 +132,44 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
}
|
||||
}
|
||||
|
||||
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 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
|
||||
@@ -118,7 +186,6 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
|
||||
fun play(tvViewModel: TVViewModel) {
|
||||
playerFragment.play(tvViewModel)
|
||||
mainFragment.view?.requestFocus()
|
||||
}
|
||||
|
||||
fun play(itemPosition: Int) {
|
||||
@@ -133,14 +200,6 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
mainFragment.next()
|
||||
}
|
||||
|
||||
private fun prevSource() {
|
||||
// mainFragment.prevSource()
|
||||
}
|
||||
|
||||
private fun nextSource() {
|
||||
// mainFragment.nextSource()
|
||||
}
|
||||
|
||||
fun switchMainFragment() {
|
||||
val transaction = supportFragmentManager.beginTransaction()
|
||||
|
||||
@@ -163,6 +222,8 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
fun settingDelayHide() {
|
||||
handler.removeCallbacks(hideSetting)
|
||||
handler.postDelayed(hideSetting, delayHideSetting)
|
||||
showTime()
|
||||
mainFragment.changeMenu()
|
||||
}
|
||||
|
||||
fun settingNeverHide() {
|
||||
@@ -187,14 +248,42 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
}
|
||||
}
|
||||
|
||||
fun fragmentReady() {
|
||||
fun fragmentReady(tag: String) {
|
||||
ready++
|
||||
Log.i(TAG, "ready $ready")
|
||||
if (ready == 5) {
|
||||
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 {
|
||||
if (event != null) {
|
||||
gestureDetector.onTouchEvent(event)
|
||||
@@ -266,9 +355,15 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
private val hideSetting = Runnable {
|
||||
if (settingFragment.isVisible) {
|
||||
settingFragment.dismiss()
|
||||
showTime()
|
||||
mainFragment.changeMenu()
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideSettingFragment() {
|
||||
handler.postDelayed(hideSetting, 0)
|
||||
}
|
||||
|
||||
private fun channelUp() {
|
||||
if (mainFragment.isHidden) {
|
||||
if (SP.channelReversal) {
|
||||
@@ -308,6 +403,11 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
return
|
||||
}
|
||||
|
||||
if (settingFragment.isVisible) {
|
||||
hideSettingFragment()
|
||||
return
|
||||
}
|
||||
|
||||
if (doubleBackToExitPressedOnce) {
|
||||
super.onBackPressed()
|
||||
return
|
||||
@@ -435,30 +535,17 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
channelUp()
|
||||
// if (mainFragment.isHidden) {
|
||||
// prevSource()
|
||||
// } else {
|
||||
//// if (mainFragment.tvListViewModel.getTVViewModelCurrent()
|
||||
//// ?.getItemPosition() == 0
|
||||
//// ) {
|
||||
////// mainFragment.toLastPosition()
|
||||
//// hideMainFragment()
|
||||
//// }
|
||||
// }
|
||||
if (mainFragment.isHidden && !settingFragment.isVisible) {
|
||||
switchMainFragment()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
channelDown()
|
||||
// if (mainFragment.isHidden) {
|
||||
// nextSource()
|
||||
// } else {
|
||||
//// if (mainFragment.tvListViewModel.getTVViewModelCurrent()
|
||||
//// ?.getItemPosition() == mainFragment.tvListViewModel.maxNum[mainFragment.selectedPosition] - 1
|
||||
//// ) {
|
||||
//// mainFragment.toFirstPosition()
|
||||
//// }
|
||||
// }
|
||||
if (mainFragment.isHidden && !settingFragment.isVisible) {
|
||||
showSetting()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
@@ -475,7 +562,8 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
private fun getAppSignature() = this.appSignature
|
||||
|
||||
override fun onStart() {
|
||||
Log.i(TAG, "onStart")
|
||||
Log.i(TAG, "onStart MainActivity")
|
||||
syncTime()
|
||||
super.onStart()
|
||||
}
|
||||
|
||||
@@ -499,7 +587,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
|
||||
}
|
||||
|
||||
override fun onRequestFinished(message: String?) {
|
||||
fragmentReady()
|
||||
fragmentReady("request")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.lizongying.mytv
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
@@ -7,18 +9,22 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
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 androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lizongying.mytv.Utils.dpToPx
|
||||
import com.lizongying.mytv.api.YSP
|
||||
import com.lizongying.mytv.databinding.MenuBinding
|
||||
import com.lizongying.mytv.databinding.RowBinding
|
||||
import com.lizongying.mytv.databinding.ShowBinding
|
||||
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
|
||||
|
||||
@@ -29,21 +35,39 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
|
||||
private var rowList: MutableList<View> = mutableListOf()
|
||||
|
||||
private var _binding: ShowBinding? = null
|
||||
private var _binding: MenuBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
var tvListViewModel = TVListViewModel()
|
||||
|
||||
private var lastVideoUrl = ""
|
||||
|
||||
private lateinit var application: MyTvApplication
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = ShowBinding.inflate(inflater, container, false)
|
||||
_binding = MenuBinding.inflate(inflater, container, false)
|
||||
|
||||
application = requireActivity().applicationContext as MyTvApplication
|
||||
|
||||
binding.menu.layoutParams.width = application.shouldWidthPx()
|
||||
binding.menu.layoutParams.height = application.shouldHeightPx()
|
||||
|
||||
binding.container.setOnClickListener {
|
||||
hideSelf()
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun hideSelf() {
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.hide(this)
|
||||
.commit()
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
@@ -71,43 +95,55 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
|
||||
val adapter =
|
||||
CardAdapter(
|
||||
itemBinding.rowItems,
|
||||
itemBinding.items,
|
||||
this,
|
||||
tvListViewModelCurrent,
|
||||
)
|
||||
rowList.add(itemBinding.rowItems)
|
||||
rowList.add(itemBinding.items)
|
||||
|
||||
adapter.setItemListener(this)
|
||||
|
||||
itemBinding.rowHeader.text = k
|
||||
itemBinding.rowItems.tag = idx.toInt()
|
||||
itemBinding.rowItems.adapter = adapter
|
||||
itemBinding.header.text = k
|
||||
itemBinding.items.tag = idx.toInt()
|
||||
itemBinding.items.adapter = adapter
|
||||
|
||||
itemBinding.rowItems.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
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 { GrayOverlayItemDecoration(it) }
|
||||
val itemDecoration = context?.let { ItemDecoration(it) }
|
||||
if (itemDecoration != null) {
|
||||
itemBinding.rowItems.addItemDecoration(itemDecoration)
|
||||
itemBinding.items.addItemDecoration(itemDecoration)
|
||||
}
|
||||
|
||||
if (SP.grid) {
|
||||
itemBinding.rowItems.layoutManager =
|
||||
itemBinding.items.layoutManager =
|
||||
GridLayoutManager(context, 6)
|
||||
itemBinding.rowItems.layoutParams.height =
|
||||
dpToPx(92 * ((tvListViewModelCurrent.size() + 6 - 1) / 6))
|
||||
itemBinding.items.layoutParams.height =
|
||||
application.dp2Px(110 * ((tvListViewModelCurrent.size() + 6 - 1) / 6) + 5)
|
||||
} else {
|
||||
itemBinding.rowItems.layoutManager =
|
||||
itemBinding.items.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
val layoutParams = itemBinding.row.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.topMargin = dpToPx(11F)
|
||||
layoutParams.topMargin = application.dp2Px(11)
|
||||
itemBinding.row.layoutParams = layoutParams
|
||||
itemBinding.row.setOnClickListener {
|
||||
hideSelf()
|
||||
}
|
||||
|
||||
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++
|
||||
@@ -123,8 +159,13 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
if (tvViewModel.errInfo.value != null
|
||||
&& tvViewModel.getTV().id == itemPosition
|
||||
) {
|
||||
Toast.makeText(context, tvViewModel.errInfo.value, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
if (tvViewModel.errInfo.value == "") {
|
||||
(activity as? MainActivity)?.showPlayerFragment()
|
||||
(activity as? MainActivity)?.hideErrorFragment()
|
||||
} else {
|
||||
(activity as? MainActivity)?.hidePlayerFragment()
|
||||
(activity as? MainActivity)?.showErrorFragment(tvViewModel.errInfo.value.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
|
||||
@@ -164,12 +205,28 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
(activity as MainActivity).fragmentReady()
|
||||
(activity as MainActivity).fragmentReady(TAG)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKey(keyCode: Int): Boolean {
|
||||
@@ -190,13 +247,14 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
}
|
||||
|
||||
override fun onItemHasFocus(tvViewModel: TVViewModel) {
|
||||
tvListViewModel.setItemPositionCurrent(tvViewModel.getTV().id)
|
||||
|
||||
val row = tvViewModel.getRowPosition()
|
||||
|
||||
for (i in rowList) {
|
||||
if (i.tag as Int != row) {
|
||||
((i as RecyclerView).adapter as CardAdapter).clear()
|
||||
((i as RecyclerView).adapter as CardAdapter).focusable = false
|
||||
(i.adapter as CardAdapter).clear()
|
||||
} else {
|
||||
((i as RecyclerView).adapter as CardAdapter).focusable = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +262,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
}
|
||||
|
||||
override fun onItemClicked(tvViewModel: TVViewModel) {
|
||||
Log.i(TAG, "onItemClicked")
|
||||
if (this.isHidden) {
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
return
|
||||
@@ -213,14 +270,15 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
if (itemPosition != tvViewModel.getTV().id) {
|
||||
itemPosition = tvViewModel.getTV().id
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("menu")
|
||||
Log.i(TAG, "onItemClicked ${tvViewModel.getTV().id}")
|
||||
}
|
||||
(activity as? MainActivity)?.switchMainFragment()
|
||||
}
|
||||
|
||||
fun setPosition() {
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
val rowPosition = tvViewModel!!.getRowPosition()
|
||||
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) ?: return
|
||||
val rowPosition = tvViewModel.getRowPosition()
|
||||
val itemPosition = tvViewModel.getItemPosition()
|
||||
setPosition(rowPosition, itemPosition)
|
||||
}
|
||||
@@ -260,8 +318,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
}
|
||||
|
||||
fun fragmentReady() {
|
||||
// request.fetchPage()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("init")
|
||||
|
||||
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
|
||||
updateEPG(tvViewModel)
|
||||
@@ -273,7 +330,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
if (itemPosition > -1 && itemPosition < tvListViewModel.size()) {
|
||||
this.itemPosition = itemPosition
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("num")
|
||||
} else {
|
||||
Toast.makeText(context, "频道不存在", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -287,7 +344,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
itemPosition = tvListViewModel.size() - 1
|
||||
}
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("prev")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +355,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
itemPosition = 0
|
||||
}
|
||||
tvListViewModel.setItemPosition(itemPosition)
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed()
|
||||
tvListViewModel.getTVViewModel(itemPosition)?.changed("next")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,6 +380,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
}
|
||||
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
Log.i(TAG, "onHiddenChanged $hidden")
|
||||
super.onHiddenChanged(hidden)
|
||||
if (!hidden) {
|
||||
val tvModel = tvListViewModel.getTVViewModel(itemPosition)
|
||||
@@ -331,15 +389,17 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
Log.i(TAG, "toPosition $rowPosition $itemPosition")
|
||||
for (i in rowList) {
|
||||
if (i.tag as Int == rowPosition) {
|
||||
((i as RecyclerView).adapter as CardAdapter).toPosition(itemPosition!!)
|
||||
((i as RecyclerView).adapter as CardAdapter).updateEPG()
|
||||
(i.adapter as CardAdapter).focusable = true
|
||||
(i.adapter as CardAdapter).toPosition(itemPosition!!)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view?.post {
|
||||
// for (i in rowList) {
|
||||
// ((i as RecyclerView).adapter as CardAdapter).visiable = false
|
||||
// }
|
||||
for (i in rowList) {
|
||||
((i as RecyclerView).adapter as CardAdapter).focusable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,13 +409,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
Log.i(TAG, "onStop")
|
||||
super.onStop()
|
||||
SP.itemPosition = itemPosition
|
||||
Log.i(TAG, "$POSITION $itemPosition saved")
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.i(TAG, "onDestroy")
|
||||
super.onDestroy()
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
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.multidex.MultiDex
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -78,14 +78,22 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
override fun onPlayerError(error: PlaybackException) {
|
||||
super.onPlayerError(error)
|
||||
|
||||
Log.e(TAG, "PlaybackException $error")
|
||||
tvViewModel?.changed()
|
||||
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
|
||||
}
|
||||
|
||||
@@ -146,7 +154,7 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PlaybackVideoFragment"
|
||||
private const val TAG = "PlayerFragment"
|
||||
}
|
||||
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
|
||||
@@ -13,6 +13,8 @@ object SP {
|
||||
// If use channel num to select channel or not
|
||||
private const val KEY_CHANNEL_NUM = "channel_num"
|
||||
|
||||
private const val KEY_TIME = "time"
|
||||
|
||||
// If start app on device boot or not
|
||||
private const val KEY_BOOT_STARTUP = "boot_startup"
|
||||
|
||||
@@ -41,6 +43,10 @@ object SP {
|
||||
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) = sp.edit().putBoolean(KEY_TIME, value).apply()
|
||||
|
||||
var bootStartup: Boolean
|
||||
get() = sp.getBoolean(KEY_BOOT_STARTUP, false)
|
||||
set(value) = sp.edit().putBoolean(KEY_BOOT_STARTUP, value).apply()
|
||||
|
||||
@@ -4,6 +4,9 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.marginEnd
|
||||
import androidx.core.view.marginTop
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.lizongying.mytv.databinding.SettingBinding
|
||||
|
||||
@@ -15,6 +18,14 @@ class SettingFragment : DialogFragment() {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
@@ -46,6 +57,14 @@ class SettingFragment : DialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchTime.run {
|
||||
isChecked = SP.time
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
SP.time = isChecked
|
||||
(activity as MainActivity).settingDelayHide()
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchBootStartup.run {
|
||||
isChecked = SP.bootStartup
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
@@ -70,6 +89,70 @@ class SettingFragment : DialogFragment() {
|
||||
)
|
||||
)
|
||||
|
||||
val application = requireActivity().applicationContext as MyTvApplication
|
||||
|
||||
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 = application.px2PxFont(binding.version.textSize)
|
||||
val layoutParamsVersion = binding.version.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsVersion.topMargin = application.px2Px(binding.version.marginTop)
|
||||
binding.version.layoutParams = layoutParamsVersion
|
||||
|
||||
binding.checkVersion.textSize = application.px2PxFont(binding.checkVersion.textSize)
|
||||
val layoutParamsCheckVersion =
|
||||
binding.checkVersion.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsCheckVersion.marginEnd = application.px2Px(binding.checkVersion.marginEnd)
|
||||
binding.checkVersion.layoutParams = layoutParamsCheckVersion
|
||||
|
||||
binding.versionName.textSize = application.px2PxFont(binding.versionName.textSize)
|
||||
|
||||
binding.exit.textSize = application.px2PxFont(binding.exit.textSize)
|
||||
|
||||
binding.switchChannelReversal.textSize =
|
||||
application.px2PxFont(binding.switchChannelReversal.textSize)
|
||||
val layoutParamsChannelReversal =
|
||||
binding.switchChannelReversal.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsChannelReversal.topMargin =
|
||||
application.px2Px(binding.switchChannelReversal.marginTop)
|
||||
binding.switchChannelReversal.layoutParams = layoutParamsChannelReversal
|
||||
|
||||
binding.switchChannelNum.textSize = application.px2PxFont(binding.switchChannelNum.textSize)
|
||||
val layoutParamsChannelNum =
|
||||
binding.switchChannelNum.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsChannelNum.topMargin = application.px2Px(binding.switchChannelNum.marginTop)
|
||||
binding.switchChannelNum.layoutParams = layoutParamsChannelNum
|
||||
|
||||
binding.switchTime.textSize = application.px2PxFont(binding.switchTime.textSize)
|
||||
val layoutParamsTime = binding.switchTime.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsTime.topMargin = application.px2Px(binding.switchTime.marginTop)
|
||||
binding.switchTime.layoutParams = layoutParamsTime
|
||||
|
||||
binding.switchBootStartup.textSize =
|
||||
application.px2PxFont(binding.switchBootStartup.textSize)
|
||||
val layoutParamsBootStartup =
|
||||
binding.switchBootStartup.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsBootStartup.topMargin = application.px2Px(binding.switchBootStartup.marginTop)
|
||||
binding.switchBootStartup.layoutParams = layoutParamsBootStartup
|
||||
|
||||
binding.switchGrid.textSize = application.px2PxFont(binding.switchGrid.textSize)
|
||||
val layoutParamsGrid = binding.switchGrid.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParamsGrid.topMargin = application.px2Px(binding.switchGrid.marginTop)
|
||||
binding.switchGrid.layoutParams = layoutParamsGrid
|
||||
|
||||
binding.appreciate.layoutParams.width =
|
||||
application.px2Px(binding.appreciate.layoutParams.width)
|
||||
|
||||
binding.exit.setOnClickListener {
|
||||
requireActivity().finishAffinity()
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
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.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.main.layoutParams.width = application.shouldWidthPx()
|
||||
binding.main.layoutParams.height = application.shouldHeightPx()
|
||||
|
||||
(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"
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.PermissionChecker
|
||||
import androidx.core.content.PermissionChecker.checkSelfPermission
|
||||
import com.lizongying.mytv.api.ReleaseV2
|
||||
import com.lizongying.mytv.requests.MyRequest
|
||||
import com.lizongying.mytv.requests.ReleaseRequest
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -32,7 +32,7 @@ class UpdateManager(
|
||||
) :
|
||||
ConfirmationFragment.ConfirmationListener {
|
||||
|
||||
private var myRequest = MyRequest()
|
||||
private var myRequest = ReleaseRequest()
|
||||
private var release: ReleaseV2? = null
|
||||
|
||||
private var downloadReceiver: DownloadReceiver? = null
|
||||
@@ -62,7 +62,7 @@ class UpdateManager(
|
||||
|
||||
private fun updateUI(text: String) {
|
||||
val dialog = ConfirmationFragment(this@UpdateManager, text)
|
||||
dialog.show(settingFragment.fragmentManager, TAG)
|
||||
settingFragment.fragmentManager?.let { dialog.show(it, TAG) }
|
||||
}
|
||||
|
||||
private fun haveStoragePermission(): Boolean {
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.os.Build
|
||||
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 java.io.IOException
|
||||
@@ -15,6 +16,8 @@ import java.util.Locale
|
||||
object Utils {
|
||||
private var between: Long = 0
|
||||
|
||||
private var listener: Request.RequestListener? = null
|
||||
|
||||
fun getDateFormat(format: String): String {
|
||||
return SimpleDateFormat(
|
||||
format,
|
||||
@@ -38,6 +41,10 @@ object Utils {
|
||||
println("Failed to retrieve timestamp from server: ${e.message}")
|
||||
}
|
||||
between = System.currentTimeMillis() - currentTimeMillis
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
listener?.onRequestFinished(null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,10 +79,26 @@ object Utils {
|
||||
}
|
||||
|
||||
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
|
||||
).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
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ 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 trace3Url = "https://dtrace.ysp.cctv.cn/"
|
||||
private val jceUrl = "https://jacc.ysp.cctv.cn/"
|
||||
private val fUrl = "https://m.fengshows.com/"
|
||||
|
||||
@@ -67,6 +69,22 @@ class ApiClient {
|
||||
.build().create(YSPBtraceService::class.java)
|
||||
}
|
||||
|
||||
val yspBtraceService2: YSPBtraceService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(trace2Url)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build().create(YSPBtraceService::class.java)
|
||||
}
|
||||
|
||||
val yspBtraceService3: YSPBtraceService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(trace3Url)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build().create(YSPBtraceService::class.java)
|
||||
}
|
||||
|
||||
val yspJceService: YSPJceService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(jceUrl)
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.lizongying.mytv
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
data class KvcollectRequest(
|
||||
val BossId: Int = 2727,
|
||||
val Pwd: Int = 1424084450,
|
||||
val cdn: String = "waibao",
|
||||
val cmd: Int = 263,
|
||||
val defn: String = "fhd",
|
||||
val downspeed: Double = 10.0,
|
||||
val durl: String = "", //
|
||||
val errcode: String = "",
|
||||
val fact1: String = "ysp_pc_live_b",
|
||||
val firstreport: Int = 0,
|
||||
val fplayerver: String = "100",
|
||||
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 = 2727,
|
||||
val Pwd: Int = 1424084450,
|
||||
val cdn: String = "waibao",
|
||||
val cmd: Int = 263,
|
||||
val defn: String = "fhd",
|
||||
val downspeed: String = "10",
|
||||
val durl: String = "", //
|
||||
val errcode: String = "",
|
||||
val fact1: String = "ysp_pc_live_b",
|
||||
val firstreport: Int = 0,
|
||||
val fplayerver: String = "100",
|
||||
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,
|
||||
)
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.lizongying.mytv.api
|
||||
|
||||
import android.content.Context
|
||||
import com.lizongying.mytv.Encryptor
|
||||
import com.lizongying.mytv.MainActivity
|
||||
import com.lizongying.mytv.SP
|
||||
import com.lizongying.mytv.Utils.getDateTimestamp
|
||||
import com.lizongying.mytv.models.TVViewModel
|
||||
@@ -142,4 +140,9 @@ object YSP {
|
||||
val hashedData = encryptor.hash2(e) ?: return ""
|
||||
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
}
|
||||
|
||||
fun getAuthSignature(e: String): String {
|
||||
val hashedData = encryptor.hash2(e.toByteArray()) ?: return ""
|
||||
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
|
||||
}
|
||||
}
|
||||
@@ -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 = "9141",
|
||||
@Field("Pwd") Pwd: String = "1137850982",
|
||||
@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 = "100",
|
||||
@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>
|
||||
}
|
||||
+6
-3
@@ -1,4 +1,4 @@
|
||||
package com.lizongying.mytv
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
import com.lizongying.mytv.models.ProgramType
|
||||
import java.io.Serializable
|
||||
@@ -15,18 +15,21 @@ data class TV(
|
||||
var programType: ProgramType,
|
||||
var needToken: Boolean = false,
|
||||
var mustToken: Boolean = false,
|
||||
|
||||
) : Serializable {
|
||||
) : 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 + '\'' +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
+59
-123
@@ -1,6 +1,6 @@
|
||||
package com.lizongying.mytv
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
import com.lizongying.mytv.models.ProgramType
|
||||
import com.lizongying.mytv.R
|
||||
|
||||
object TVList {
|
||||
val list: Map<String, List<TV>> by lazy {
|
||||
@@ -14,7 +14,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV1 综合",
|
||||
"CCTV1",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226231/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp",
|
||||
"600001859",
|
||||
@@ -27,7 +27,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV2 财经",
|
||||
"CCTV2",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226195/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp",
|
||||
"600001800",
|
||||
@@ -40,7 +40,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV3 综艺",
|
||||
"CCTV3",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226397/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/7b7a65c712450da3deb6ca66fbacf4f9aee00d3f20bd80eafb5ada01ec63eb3a.png?imageMogr2/format/webp",
|
||||
"600001801",
|
||||
@@ -53,10 +53,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV4 中文国际",
|
||||
"CCTV4",
|
||||
listOf(
|
||||
"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"
|
||||
),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp",
|
||||
"600001814",
|
||||
@@ -69,7 +66,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV5 体育",
|
||||
"CCTV5",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226395/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp",
|
||||
"600001818",
|
||||
@@ -82,7 +79,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV6 电影",
|
||||
"CCTV6",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226393/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/741515efda91f03f455df8a7da4ee11fa9329139c276435cf0a9e2af398d5bf2.png?imageMogr2/format/webp",
|
||||
"600108442",
|
||||
@@ -95,7 +92,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV7 国防军事",
|
||||
"CCTV7",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226192/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp",
|
||||
"600004092",
|
||||
@@ -108,7 +105,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV8 电视剧",
|
||||
"CCTV8",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226391/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/ad51de94426a0ba039e6dd6a8534ea98ecc813a6176bde87b4f18cc34d6d7590.png?imageMogr2/format/webp",
|
||||
"600001803",
|
||||
@@ -121,7 +118,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV9 纪录",
|
||||
"CCTV9",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226197/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp",
|
||||
"600004078",
|
||||
@@ -134,7 +131,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV10 科教",
|
||||
"CCTV10",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226189/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp",
|
||||
"600001805",
|
||||
@@ -147,7 +144,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV11 戏曲",
|
||||
"CCTV11",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226240/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp",
|
||||
"600001806",
|
||||
@@ -160,7 +157,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV12 社会与法",
|
||||
"CCTV12",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226190/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp",
|
||||
"600001807",
|
||||
@@ -173,9 +170,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV13 新闻",
|
||||
"CCTV13",
|
||||
listOf(
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226233/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp",
|
||||
"600001811",
|
||||
@@ -188,7 +183,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV14 少儿",
|
||||
"CCTV14",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226193/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp",
|
||||
"600001809",
|
||||
@@ -201,7 +196,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV15 音乐",
|
||||
"CCTV15",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp",
|
||||
"600001815",
|
||||
@@ -214,10 +209,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV16 奥林匹克",
|
||||
"CCTV16-HD",
|
||||
listOf(
|
||||
"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"
|
||||
),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp",
|
||||
"600098637",
|
||||
@@ -230,7 +222,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV17 农业农村",
|
||||
"CCTV17",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226198/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp",
|
||||
"600001810",
|
||||
@@ -243,7 +235,7 @@ object TVList {
|
||||
0,
|
||||
"CCTV5+ 体育赛事",
|
||||
"CCTV5+",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226221/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp",
|
||||
"600001817",
|
||||
@@ -282,7 +274,7 @@ object TVList {
|
||||
0,
|
||||
"风云剧场",
|
||||
"CCTV风云剧场频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/4d549e53e6d0f632d5a633d1945280797b153e588f919221a07faa869812cc89.png?imageMogr2/format/webp",
|
||||
"600099658",
|
||||
@@ -295,7 +287,7 @@ object TVList {
|
||||
0,
|
||||
"第一剧场",
|
||||
"CCTV第一剧场频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226959/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/a556bd7d93ce65e18f243a8892b5604f4faa994a4897315914216a710a706208.png?imageMogr2/format/webp",
|
||||
"600099655",
|
||||
@@ -308,7 +300,7 @@ object TVList {
|
||||
0,
|
||||
"怀旧剧场",
|
||||
"CCTV怀旧剧场频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226972/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/5661bd04fecdb6e899f801147a22ab5d3a475bf2b62e30aec2c0023190ebc9b1.png?imageMogr2/format/webp",
|
||||
"600099620",
|
||||
@@ -334,7 +326,7 @@ object TVList {
|
||||
0,
|
||||
"风云音乐",
|
||||
"CCTV风云音乐频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226953/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/bbf1d024c5228b8dd128b0e3cb1717d173fab4ee84c3a4c8a57b1a215362ca3b.png?imageMogr2/format/webp",
|
||||
"600099660",
|
||||
@@ -347,7 +339,7 @@ object TVList {
|
||||
0,
|
||||
"兵器科技",
|
||||
"CCTV兵器科技频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226975/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/4c6b6a6d3839889f34d33db3c2f80233b26b74d3489b393487635f8704e70796.png?imageMogr2/format/webp",
|
||||
"600099649",
|
||||
@@ -360,7 +352,7 @@ object TVList {
|
||||
0,
|
||||
"风云足球",
|
||||
"CCTV风云足球频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226984/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/cd1e2bb52b06a991de168733e5ff0f1d85adc8042d40c8f393f723543e5dd08a.png?imageMogr2/format/webp",
|
||||
"600099636",
|
||||
@@ -373,7 +365,7 @@ object TVList {
|
||||
0,
|
||||
"高尔夫网球",
|
||||
"CCTV高尔夫·网球频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226978/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/cdd1b31ede7a5ad049ed53d9a072422f829e72dd062ed2c19e077fdd01699071.png?imageMogr2/format/webp",
|
||||
"600099659",
|
||||
@@ -386,7 +378,7 @@ object TVList {
|
||||
0,
|
||||
"女性时尚",
|
||||
"CCTV女性时尚频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226969/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/fa28955ce8b2539d728bf4c6a13a46ff57ad76eae46627f7bcfb1ed8a613d3fc.png?imageMogr2/format/webp",
|
||||
"600099650",
|
||||
@@ -399,7 +391,7 @@ object TVList {
|
||||
0,
|
||||
"央视文化精品",
|
||||
"CCTV央视文化精品频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226981/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/14ac5ce40482cacd3d4b37435222bfe86af2b452a2f04ecbfc1d13d76edd7c57.png?imageMogr2/format/webp",
|
||||
"600099653",
|
||||
@@ -412,7 +404,7 @@ object TVList {
|
||||
0,
|
||||
"央视台球",
|
||||
"CCTV央视台球频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226956/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/10e14a92478011aa6c3c8562e62127f3b1908e29fcd78e4b2b24b9e6d3ec2fbc.png?imageMogr2/format/webp",
|
||||
"600099652",
|
||||
@@ -425,7 +417,7 @@ object TVList {
|
||||
0,
|
||||
"电视指南",
|
||||
"CCTV电视指南频道",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226987/index.m3u8"),
|
||||
listOf(),
|
||||
"央视",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/244d72c0eb1615ed7d51c2f5db5a67f306aa3f58c05bc2d34de3aa7e956dc8c9.png?imageMogr2/format/webp",
|
||||
"600099656",
|
||||
@@ -453,7 +445,7 @@ object TVList {
|
||||
0,
|
||||
"东方卫视",
|
||||
"东方卫视",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226217/index.m3u8"),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp",
|
||||
"600002483",
|
||||
@@ -466,10 +458,7 @@ object TVList {
|
||||
0,
|
||||
"湖南卫视",
|
||||
"湖南卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226307/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226211/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp",
|
||||
"600002475",
|
||||
@@ -482,10 +471,7 @@ object TVList {
|
||||
0,
|
||||
"湖北卫视",
|
||||
"湖北卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226477/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226194/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp",
|
||||
"600002508",
|
||||
@@ -498,10 +484,7 @@ object TVList {
|
||||
0,
|
||||
"辽宁卫视",
|
||||
"辽宁卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226546/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226201/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/ac4ed6058a87c101ae7147ebc38905d0cae047fb73fd277ee5049b84f52bda36.png?imageMogr2/format/webp",
|
||||
"600002505",
|
||||
@@ -514,10 +497,7 @@ object TVList {
|
||||
0,
|
||||
"江苏卫视",
|
||||
"江苏卫视",
|
||||
listOf(
|
||||
"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"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/380ad685c0c1d5b2c902246b8d2df6d3f9b45e2837abcfe493075bbded597a31.png?imageMogr2/format/webp",
|
||||
"600002521",
|
||||
@@ -530,10 +510,7 @@ object TVList {
|
||||
0,
|
||||
"江西卫视",
|
||||
"江西卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226344/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225764/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/3c760d0d00463855890e8a1864ea4a6b6dd66b90c29b4ac714a4b17c16519871.png?imageMogr2/format/webp",
|
||||
"600002503",
|
||||
@@ -546,7 +523,7 @@ object TVList {
|
||||
0,
|
||||
"山东卫视",
|
||||
"山东卫视",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226209/index.m3u8"),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/22d403f07a7cf5410b3ad3ddb65a11aa229a32475fac213f5344c9f0ec330ca1.png?imageMogr2/format/webp",
|
||||
"600002513",
|
||||
@@ -559,7 +536,7 @@ object TVList {
|
||||
0,
|
||||
"广东卫视",
|
||||
"广东卫视",
|
||||
listOf("http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226216/index.m3u8"),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/28886880a4dc0f06fb7e0a528a1def0591d61a65870e29176ede0cc92033bbfd.png?imageMogr2/format/webp",
|
||||
"600002485",
|
||||
@@ -572,10 +549,7 @@ object TVList {
|
||||
0,
|
||||
"广西卫视",
|
||||
"广西卫视",
|
||||
listOf(
|
||||
"http://live.gxrb.com.cn/tv/gxtvlive03/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225770/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/54b7e97cb816bb223fe05f3fc44da2c7820eb66e8550c19d23100f2c414ecc38.png?imageMogr2/format/webp",
|
||||
"600002509",
|
||||
@@ -588,10 +562,7 @@ object TVList {
|
||||
0,
|
||||
"重庆卫视",
|
||||
"重庆卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226409/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226202/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
R.drawable.chongqing,
|
||||
"600002531",
|
||||
@@ -604,10 +575,7 @@ object TVList {
|
||||
0,
|
||||
"河南卫视",
|
||||
"河南卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226480/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225767/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/74925962148a6d31c85808b6cd4e444c2a54bab393d2c5fc85e960b50e22fa86.png?imageMogr2/format/webp",
|
||||
"600002525",
|
||||
@@ -620,10 +588,7 @@ object TVList {
|
||||
0,
|
||||
"河北卫视",
|
||||
"河北卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226406/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225750/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/d545becdc81c60197b08c7f47380705e4665ed3fe55efc8b855e486f6e655378.png?imageMogr2/format/webp",
|
||||
"600002493",
|
||||
@@ -636,10 +601,7 @@ object TVList {
|
||||
0,
|
||||
"贵州卫视",
|
||||
"贵州卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226474/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225793/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/4eb45f4781d33d872af027dc01c941559aab55667dd99cc5c22bef7037807b13.png?imageMogr2/format/webp",
|
||||
"600002490",
|
||||
@@ -652,10 +614,7 @@ object TVList {
|
||||
0,
|
||||
"北京卫视",
|
||||
"北京卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225728/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226222/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/f4f23633c578beea49a3841d88d3490100f029ee349059fa532869db889872c5.png?imageMogr2/format/webp",
|
||||
"600002309",
|
||||
@@ -668,10 +627,7 @@ object TVList {
|
||||
0,
|
||||
"黑龙江卫视",
|
||||
"黑龙江卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226327/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226215/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/d8273ae9be698ce2db21f5b886ecac95a73429593f93713c60ed8c12c38bf0d3.png?imageMogr2/format/webp",
|
||||
"600002498",
|
||||
@@ -684,10 +640,7 @@ object TVList {
|
||||
0,
|
||||
"浙江卫视",
|
||||
"浙江卫视",
|
||||
listOf(
|
||||
"http://hw-m-l.cztv.com/channels/lantian/channel01/1080p.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226199/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/a66c836bd98ba3e41a2e9a570d4b9c50dedc6839e9de333e2e78212ad505f37e.png?imageMogr2/format/webp",
|
||||
"600002520",
|
||||
@@ -700,10 +653,7 @@ object TVList {
|
||||
0,
|
||||
"安徽卫视",
|
||||
"安徽卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226391/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226203/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/f35fa04b51b1ee4984b03578b65403570868ebca03c6c01e11b097f999a58d9b.png?imageMogr2/format/webp",
|
||||
"600002532",
|
||||
@@ -716,10 +666,7 @@ object TVList {
|
||||
0,
|
||||
"深圳卫视",
|
||||
"深圳卫视",
|
||||
listOf(
|
||||
"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"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/d59fec04c902e3581c617136d02d4b9b8c4cbe64272781ddd3525e80c823edb7.png?imageMogr2/format/webp",
|
||||
"600002481",
|
||||
@@ -732,10 +679,7 @@ object TVList {
|
||||
0,
|
||||
"四川卫视",
|
||||
"四川卫视",
|
||||
listOf(
|
||||
"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"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp",
|
||||
"600002516",
|
||||
@@ -748,10 +692,7 @@ object TVList {
|
||||
0,
|
||||
"东南卫视",
|
||||
"福建东南卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226341/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225766/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp",
|
||||
"600002484",
|
||||
@@ -764,10 +705,7 @@ object TVList {
|
||||
0,
|
||||
"海南卫视",
|
||||
"海南卫视",
|
||||
listOf(
|
||||
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226465/index.m3u8",
|
||||
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225769/index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"地方",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp",
|
||||
"600002506",
|
||||
@@ -875,7 +813,7 @@ object TVList {
|
||||
0,
|
||||
"CGTN",
|
||||
"CGTN",
|
||||
listOf("http://live.cgtn.com/1000/prog_index.m3u8"),
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp",
|
||||
"600014550",
|
||||
@@ -888,7 +826,7 @@ object TVList {
|
||||
0,
|
||||
"CGTN 法语频道",
|
||||
"CGTN法语频道",
|
||||
listOf("https://livefr.cgtn.com/1000f/prog_index.m3u8"),
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp",
|
||||
"600084704",
|
||||
@@ -901,7 +839,7 @@ object TVList {
|
||||
0,
|
||||
"CGTN 俄语频道",
|
||||
"CGTN俄语频道",
|
||||
listOf("http://liveru.cgtn.com/1000r/prog_index.m3u8"),
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp",
|
||||
"600084758",
|
||||
@@ -914,7 +852,7 @@ object TVList {
|
||||
0,
|
||||
"CGTN 阿拉伯语频道",
|
||||
"CGTN阿拉伯语频道",
|
||||
listOf("http://livear.cgtn.com/1000a/prog_index.m3u8"),
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp",
|
||||
"600084782",
|
||||
@@ -927,10 +865,7 @@ object TVList {
|
||||
0,
|
||||
"CGTN 西班牙语频道",
|
||||
"CGTN西班牙语频道",
|
||||
listOf(
|
||||
"http://livees.cgtn.com/500e/prog_index.m3u8",
|
||||
"http://livees.cgtn.com/1000e/prog_index.m3u8"
|
||||
),
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp",
|
||||
"600084744",
|
||||
@@ -943,7 +878,7 @@ object TVList {
|
||||
0,
|
||||
"CGTN 纪录频道",
|
||||
"CGTN外语纪录频道",
|
||||
listOf("https://livedoc.cgtn.com/500d/prog_index.m3u8"),
|
||||
listOf(),
|
||||
"国际",
|
||||
"https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp",
|
||||
"600084781",
|
||||
@@ -965,6 +900,7 @@ object TVList {
|
||||
v.forEach { v1 ->
|
||||
if (!v1.mustToken) {
|
||||
v1.id = id
|
||||
v1.needToken = false
|
||||
id++
|
||||
group.add(v1)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ 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() {
|
||||
|
||||
@@ -16,10 +17,6 @@ class TVListViewModel : ViewModel() {
|
||||
val itemPosition: LiveData<Int>
|
||||
get() = _itemPosition
|
||||
|
||||
private val _itemPositionCurrent = MutableLiveData<Int>()
|
||||
val itemPositionCurrent: LiveData<Int>
|
||||
get() = _itemPositionCurrent
|
||||
|
||||
fun addTVViewModel(tvViewModel: TVViewModel) {
|
||||
if (_tvListViewModel.value == null) {
|
||||
_tvListViewModel.value = mutableListOf(tvViewModel)
|
||||
@@ -32,23 +29,16 @@ class TVListViewModel : ViewModel() {
|
||||
return _tvListViewModel.value?.get(id)
|
||||
}
|
||||
|
||||
fun getTVViewModelCurrent(): TVViewModel? {
|
||||
return _itemPositionCurrent.value?.let { _tvListViewModel.value?.get(it) }
|
||||
}
|
||||
|
||||
fun setItemPosition(position: Int) {
|
||||
_itemPosition.value = position
|
||||
_itemPositionCurrent.value = position
|
||||
}
|
||||
|
||||
fun setItemPositionCurrent(position: Int) {
|
||||
_itemPositionCurrent.value = position
|
||||
SP.itemPosition = position
|
||||
}
|
||||
|
||||
fun size(): Int {
|
||||
if (_tvListViewModel.value == null) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return _tvListViewModel.value!!.size
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.lizongying.mytv.models
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.lizongying.mytv.TV
|
||||
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
|
||||
@@ -18,6 +16,8 @@ class TVViewModel(private var tv: TV) : ViewModel() {
|
||||
|
||||
var retryTimes = 0
|
||||
var retryMaxTimes = 8
|
||||
var authYSPRetryTimes = 0
|
||||
var authYSPRetryMaxTimes = 3
|
||||
var tokenYSPRetryTimes = 0
|
||||
var tokenYSPRetryMaxTimes = 0
|
||||
var tokenFHRetryTimes = 0
|
||||
@@ -41,8 +41,8 @@ class TVViewModel(private var tv: TV) : ViewModel() {
|
||||
val videoIndex: LiveData<Int>
|
||||
get() = _videoIndex
|
||||
|
||||
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>()
|
||||
@@ -56,36 +56,23 @@ class TVViewModel(private var tv: TV) : ViewModel() {
|
||||
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)
|
||||
}
|
||||
_videoUrl.value = tv.videoUrl
|
||||
_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) {
|
||||
_change.value = from
|
||||
}
|
||||
|
||||
fun allReady() {
|
||||
_ready.value = true
|
||||
}
|
||||
|
||||
fun setVideoIndex(videoIndex: Int) {
|
||||
_videoIndex.value = videoIndex
|
||||
}
|
||||
|
||||
init {
|
||||
_videoUrl.value = tv.videoUrl
|
||||
_videoIndex.value = tv.videoUrl.lastIndex
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import retrofit2.Response
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class MyRequest {
|
||||
class ReleaseRequest {
|
||||
private var releaseService = ApiClient().releaseService
|
||||
|
||||
suspend fun getRelease(): ReleaseV2? {
|
||||
@@ -39,6 +39,6 @@ class MyRequest {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MyRequest"
|
||||
private const val TAG = "ReleaseRequest"
|
||||
}
|
||||
}
|
||||
+240
-26
@@ -1,9 +1,10 @@
|
||||
package com.lizongying.mytv
|
||||
package com.lizongying.mytv.requests
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.lizongying.mytv.Utils
|
||||
import com.lizongying.mytv.Utils.getDateFormat
|
||||
import com.lizongying.mytv.api.ApiClient
|
||||
import com.lizongying.mytv.api.Auth
|
||||
@@ -13,6 +14,8 @@ import com.lizongying.mytv.api.FAuthService
|
||||
import com.lizongying.mytv.api.FEPG
|
||||
import com.lizongying.mytv.api.Info
|
||||
import com.lizongying.mytv.api.InfoV2
|
||||
import com.lizongying.mytv.api.KvcollectRequest
|
||||
import com.lizongying.mytv.api.KvcollectRequest2
|
||||
import com.lizongying.mytv.api.LiveInfo
|
||||
import com.lizongying.mytv.api.LiveInfoRequest
|
||||
import com.lizongying.mytv.api.YSP
|
||||
@@ -21,6 +24,7 @@ import com.lizongying.mytv.api.YSPBtraceService
|
||||
import com.lizongying.mytv.api.YSPJceService
|
||||
import com.lizongying.mytv.api.YSPProtoService
|
||||
import com.lizongying.mytv.api.YSPTokenService
|
||||
import com.lizongying.mytv.models.TVList
|
||||
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
|
||||
@@ -32,6 +36,7 @@ import retrofit2.Response
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
object Request {
|
||||
@@ -39,6 +44,8 @@ object Request {
|
||||
private var yspTokenService: YSPTokenService = ApiClient().yspTokenService
|
||||
private var yspApiService: YSPApiService = ApiClient().yspApiService
|
||||
private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService
|
||||
private var yspBtraceService2: YSPBtraceService = ApiClient().yspBtraceService2
|
||||
private var yspBtraceService3: YSPBtraceService = ApiClient().yspBtraceService3
|
||||
private var yspProtoService: YSPProtoService = ApiClient().yspProtoService
|
||||
private var yspJceService: YSPJceService = ApiClient().yspJceService
|
||||
private var fAuthService: FAuthService = ApiClient().fAuthService
|
||||
@@ -47,7 +54,7 @@ object Request {
|
||||
private var token = ""
|
||||
private var tokenFH = ""
|
||||
|
||||
private var needAuth = false
|
||||
private var needAuth = true
|
||||
private var needToken = false
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
@@ -111,11 +118,19 @@ object Request {
|
||||
} else {
|
||||
if (!tvModel.getTV().mustToken) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
val err = "错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fetchAuth(tvModel, cookie)
|
||||
}
|
||||
} else {
|
||||
val err = "认证结果错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -130,11 +145,19 @@ object Request {
|
||||
} else {
|
||||
if (!tvModel.getTV().mustToken) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
val err = "错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fetchAuth(tvModel, cookie)
|
||||
}
|
||||
} else {
|
||||
val err = "认证状态错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,11 +174,19 @@ object Request {
|
||||
} else {
|
||||
if (!tvModel.getTV().mustToken) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
val err = "错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fetchAuth(tvModel, cookie)
|
||||
}
|
||||
} else {
|
||||
val err = "认证请求错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -164,7 +195,7 @@ object Request {
|
||||
private fun fetchVideo(tvModel: TVViewModel, cookie: String) {
|
||||
cancelCall()
|
||||
|
||||
if (::btraceRunnable.isInitialized) {
|
||||
if (Request::btraceRunnable.isInitialized) {
|
||||
handler.removeCallbacks(btraceRunnable)
|
||||
}
|
||||
|
||||
@@ -216,24 +247,42 @@ object Request {
|
||||
if (needToken && tvModel.tokenYSPRetryTimes < tvModel.tokenYSPRetryMaxTimes) {
|
||||
tvModel.tokenYSPRetryTimes++
|
||||
tvModel.needGetToken = true
|
||||
fetchVideo(tvModel)
|
||||
// fetchAuth(tvModel)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel)
|
||||
} else {
|
||||
fetchVideo(tvModel)
|
||||
}
|
||||
} else {
|
||||
if (!tvModel.getTV().mustToken) {
|
||||
fetchVideo(tvModel, cookie)
|
||||
// fetchAuth(tvModel, cookie)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
} else {
|
||||
val err = "错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
// fetchAuth(tvModel, cookie)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val err = "结果错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (liveInfo?.data?.errinfo != null && liveInfo.data.errinfo == "应版权方要求,暂停提供直播信号,请点击观看其他精彩节目") {
|
||||
Log.e(TAG, "$title error ${liveInfo.data.errinfo}")
|
||||
tvModel.setErrInfo(liveInfo.data.errinfo)
|
||||
val err = "版权错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
} else {
|
||||
Log.e(TAG, "$title url error $request $liveInfo")
|
||||
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
|
||||
@@ -242,18 +291,35 @@ object Request {
|
||||
if (needToken && tvModel.tokenYSPRetryTimes < tvModel.tokenYSPRetryMaxTimes) {
|
||||
tvModel.tokenYSPRetryTimes++
|
||||
tvModel.needGetToken = true
|
||||
fetchVideo(tvModel)
|
||||
// fetchAuth(tvModel)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel)
|
||||
} else {
|
||||
fetchVideo(tvModel)
|
||||
}
|
||||
} else {
|
||||
if (!tvModel.getTV().mustToken) {
|
||||
fetchVideo(tvModel, cookie)
|
||||
// fetchAuth(tvModel, cookie)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
} else {
|
||||
val err = "错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
// fetchAuth(tvModel, cookie)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val err = "其他错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,18 +331,35 @@ object Request {
|
||||
if (needToken && tvModel.tokenYSPRetryTimes < tvModel.tokenYSPRetryMaxTimes) {
|
||||
tvModel.tokenYSPRetryTimes++
|
||||
tvModel.needGetToken = true
|
||||
fetchVideo(tvModel)
|
||||
// fetchAuth(tvModel)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
} else {
|
||||
if (!tvModel.getTV().mustToken) {
|
||||
fetchVideo(tvModel, cookie)
|
||||
// fetchAuth(tvModel, cookie)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
} else {
|
||||
val err = "错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
// fetchAuth(tvModel, cookie)
|
||||
if (needAuth) {
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val err = "状态错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,11 +376,17 @@ object Request {
|
||||
} else {
|
||||
if (!tvModel.getTV().mustToken) {
|
||||
fetchVideo(tvModel, cookie)
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fetchVideo(tvModel, cookie)
|
||||
}
|
||||
} else {
|
||||
val err = "请求错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -333,6 +422,8 @@ object Request {
|
||||
val cookie =
|
||||
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,6 +439,8 @@ object Request {
|
||||
val cookie =
|
||||
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
|
||||
fetchAuth(tvModel, cookie)
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,6 +483,8 @@ object Request {
|
||||
val cookie =
|
||||
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
|
||||
fetchVideo(tvModel, cookie)
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,6 +500,8 @@ object Request {
|
||||
val cookie =
|
||||
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
|
||||
fetchVideo(tvModel, cookie)
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -440,6 +537,10 @@ object Request {
|
||||
if (tvModel.tokenFHRetryTimes < tvModel.tokenFHRetryMaxTimes) {
|
||||
tvModel.tokenFHRetryTimes++
|
||||
fetchFAuth(tvModel)
|
||||
} else {
|
||||
val err = "状态错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,6 +550,10 @@ object Request {
|
||||
if (tvModel.tokenFHRetryTimes < tvModel.tokenFHRetryMaxTimes) {
|
||||
tvModel.tokenFHRetryTimes++
|
||||
fetchFAuth(tvModel)
|
||||
} else {
|
||||
val err = "请求错误"
|
||||
Log.e(TAG, "$title $err")
|
||||
tvModel.setErrInfo(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -531,7 +636,7 @@ object Request {
|
||||
|
||||
class BtraceRunnable(private val tvModel: TVViewModel) : Runnable {
|
||||
override fun run() {
|
||||
fetchBtrace(tvModel)
|
||||
fetchBtrace3(tvModel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,7 +663,116 @@ object Request {
|
||||
callBtracePage?.enqueue(object : Callback<Void> {
|
||||
override fun onResponse(call: Call<Void>, response: Response<Void>) {
|
||||
if (response.isSuccessful) {
|
||||
// Log.d(TAG, "$title kvcollect success")
|
||||
Log.d(TAG, "$title kvcollect success")
|
||||
} else {
|
||||
Log.e(TAG, "$title kvcollect status error")
|
||||
}
|
||||
handler.postDelayed(btraceRunnable, 60 * 1000)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Void>, t: Throwable) {
|
||||
Log.e(TAG, "$title kvcollect request error")
|
||||
handler.postDelayed(btraceRunnable, 60 * 1000)
|
||||
}
|
||||
})
|
||||
tvModel.seq++
|
||||
}
|
||||
|
||||
fun fetchBtrace2(tvModel: TVViewModel) {
|
||||
callBtracePage?.cancel()
|
||||
val title = tvModel.getTV().title
|
||||
|
||||
val guid = YSP.getGuid()
|
||||
val pid = tvModel.getTV().pid
|
||||
val sid = tvModel.getTV().sid
|
||||
val randomNumber = Random.nextDouble()
|
||||
val url = tvModel.getTV().videoUrl.first()
|
||||
|
||||
val r = KvcollectRequest(
|
||||
guid = guid,
|
||||
prog = sid,
|
||||
viewid = sid,
|
||||
livepid = pid,
|
||||
sUrl = "https://www.yangshipin.cn/#/tv/home?pid=$pid",
|
||||
playno = YSP.getRand(),
|
||||
rand_str = YSP.getRand(),
|
||||
ftime = getDateFormat("yyyy-MM-dd HH:mm:ss"),
|
||||
seq = tvModel.seq,
|
||||
durl = url,
|
||||
url = url,
|
||||
_dc = randomNumber,
|
||||
)
|
||||
|
||||
val e =
|
||||
"BossId=${r.BossId}&Pwd=${r.Pwd}&_dc=${r._dc}&cdn=${r.cdn}&cmd=${r.cmd}&defn=${r.defn}&downspeed=${r.downspeed}&durl=${r.durl}&errcode=${r.errcode}&fact1=${r.fact1}&firstreport=${r.firstreport}&fplayerver=${r.fplayerver}&ftime=${r.ftime}&geturltime=6&guid=${r.guid}&hc_openid=${r.hc_openid}&hh_ua=${r.hh_ua}&live_type=${r.live_type}&livepid=${r.livepid}&login_type=${r.login_type}&open_id=&openid=${r.openid}&platform=${r.platform}&playno=${r.playno}&prd=${r.prd}&prog=${r.prog}&rand_str=${r.rand_str}&sRef=${r.sRef}&sUrl=${r.sUrl}&sdtfrom=${r.sdtfrom}&seq=${r.seq}&url=${r.url}&viewid=${r.viewid}"
|
||||
r.signature = YSP.getAuthSignature(e)
|
||||
callBtracePage = yspBtraceService2.kvcollect2(r)
|
||||
callBtracePage?.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")
|
||||
}
|
||||
handler.postDelayed(btraceRunnable, 60 * 1000)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Void>, t: Throwable) {
|
||||
Log.e(TAG, "$title kvcollect request error")
|
||||
handler.postDelayed(btraceRunnable, 60 * 1000)
|
||||
}
|
||||
})
|
||||
tvModel.seq++
|
||||
}
|
||||
|
||||
fun fetchBtrace3(tvModel: TVViewModel) {
|
||||
callBtracePage?.cancel()
|
||||
val title = tvModel.getTV().title
|
||||
|
||||
val guid = YSP.getGuid()
|
||||
val pid = tvModel.getTV().pid
|
||||
val sid = tvModel.getTV().sid
|
||||
val randomNumber = Random.nextDouble()
|
||||
val url = tvModel.getTV().videoUrl.first()
|
||||
|
||||
val r = KvcollectRequest2(
|
||||
guid = guid,
|
||||
prog = sid,
|
||||
viewid = sid,
|
||||
livepid = pid,
|
||||
sUrl = "https://www.yangshipin.cn/#/tv/home?pid=$pid",
|
||||
playno = YSP.getRand(),
|
||||
rand_str = YSP.getRand(),
|
||||
ftime = getDateFormat("yyyy-MM-dd HH:mm:ss"),
|
||||
seq = tvModel.seq,
|
||||
durl = url,
|
||||
url = url,
|
||||
_dc = randomNumber,
|
||||
)
|
||||
|
||||
val e =
|
||||
"BossId=${r.BossId}&Pwd=${r.Pwd}&_dc=${r._dc}&cdn=${r.cdn}&cmd=${r.cmd}&defn=${r.defn}&downspeed=${r.downspeed}&durl=${r.durl}&errcode=${r.errcode}&fact1=${r.fact1}&firstreport=${r.firstreport}&fplayerver=${r.fplayerver}&ftime=${r.ftime}&geturltime=${r.geturltime}&guid=${r.guid}&hc_openid=${r.hc_openid}&hh_ua=${r.hh_ua}&live_type=${r.live_type}&livepid=${r.livepid}&login_type=${r.login_type}&open_id=${r.open_id}&openid=${r.openid}&platform=${r.platform}&playno=${r.playno}&prd=${r.prd}&prog=${r.prog}&rand_str=${r.rand_str}&sRef=${r.sRef}&sUrl=${r.sUrl}&sdtfrom=${r.sdtfrom}&seq=${r.seq}&url=${r.url}&viewid=${r.viewid}"
|
||||
r.signature = YSP.getAuthSignature(e)
|
||||
|
||||
callBtracePage = yspBtraceService3.kvcollect3(
|
||||
guid = r.guid,
|
||||
prog = r.prog,
|
||||
viewid = r.viewid,
|
||||
livepid = r.livepid,
|
||||
sUrl = r.sUrl,
|
||||
playno = r.playno,
|
||||
rand_str = r.rand_str,
|
||||
ftime = r.ftime,
|
||||
seq = "${r.seq}",
|
||||
durl = r.durl,
|
||||
url = r.url,
|
||||
_dc = "${r._dc}",
|
||||
signature = r.signature
|
||||
)
|
||||
callBtracePage?.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")
|
||||
}
|
||||
@@ -701,6 +915,6 @@ object Request {
|
||||
}
|
||||
|
||||
fun setRequestListener(listener: RequestListener) {
|
||||
this.listener = listener
|
||||
Request.listener = listener
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FF263238" />
|
||||
<corners android:radius="4dp" /> <!-- Adjust the radius as needed -->
|
||||
</shape>
|
||||
@@ -0,0 +1,6 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/blur" />
|
||||
<corners
|
||||
android:bottomLeftRadius="4dp"
|
||||
android:bottomRightRadius="4dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,6 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/blur" />
|
||||
<corners
|
||||
android:topLeftRadius="4dp"
|
||||
android:bottomLeftRadius="4dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,6 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/blur" />
|
||||
<corners
|
||||
android:topRightRadius="4dp"
|
||||
android:bottomRightRadius="4dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,6 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/focus" />
|
||||
<corners
|
||||
android:bottomLeftRadius="4dp"
|
||||
android:bottomRightRadius="4dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,6 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/white" />
|
||||
<corners
|
||||
android:topLeftRadius="4dp"
|
||||
android:bottomLeftRadius="4dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,6 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/white" />
|
||||
<corners
|
||||
android:topRightRadius="4dp"
|
||||
android:bottomRightRadius="4dp" />
|
||||
</shape>
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/white" />
|
||||
<corners android:radius="4dp" /> <!-- Adjust the radius as needed -->
|
||||
<corners
|
||||
android:topLeftRadius="4dp"
|
||||
android:topRightRadius="4dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,23 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:viewportWidth="50"
|
||||
android:viewportHeight="50"
|
||||
android:width="62.5dp"
|
||||
android:height="62.5dp">
|
||||
<group
|
||||
android:scaleX="0.1"
|
||||
android:scaleY="-0.1"
|
||||
android:translateY="50">
|
||||
<path
|
||||
android:pathData="M205 377c-27 -27 -40 -33 -65 -29 -30 4 -70 -19 -70 -40 0 -5 -16 -24 -35 -42 -38 -36 -44 -66 -23 -111 22 -49 51 -55 248 -55l182 0 29 29c46 46 37 106 -20 136 -20 10 -31 25 -35 49 -4 22 -20 46 -42 65 -29 26 -43 31 -85 31 -44 0 -55 -4 -84 -33zm138 2c29 -13 57 -58 57 -93 0 -17 5 -26 15 -26 24 0 65 -46 65 -71 0 -13 -10 -33 -22 -46 -21 -22 -26 -23 -205 -23 -182 0 -184 0 -208 25 -33 32 -32 70 3 105 16 15 36 40 45 54 18 27 54 35 74 15 8 -8 16 -2 29 19 9 17 27 35 38 41 28 13 80 13 109 0z"
|
||||
android:fillColor="#FFEEEEEE" />
|
||||
<path
|
||||
android:pathData="M174 269c-10 -17 13 -36 27 -22 12 12 4 33 -11 33 -5 0 -12 -5 -16 -11z"
|
||||
android:fillColor="#FFEEEEEE" />
|
||||
<path
|
||||
android:pathData="M294 269c-10 -17 13 -36 27 -22 12 12 4 33 -11 33 -5 0 -12 -5 -16 -11z"
|
||||
android:fillColor="#FFEEEEEE" />
|
||||
<path
|
||||
android:pathData="M194 196c-32 -14 -50 -33 -41 -42 3 -3 20 4 38 15 43 26 75 26 118 0 34 -20 49 -20 36 1 -10 16 -69 40 -97 40 -13 -1 -37 -7 -54 -14z"
|
||||
android:fillColor="#FFEEEEEE" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/card"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="100dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center_vertical"
|
||||
>
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/rounded_white_top"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:padding="5dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/icon"
|
||||
android:background="@drawable/rounded_dark_bottom"
|
||||
android:orientation="vertical"
|
||||
android:padding="7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:gravity="start"
|
||||
android:text="Title"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textColor="#FFEEEEEE"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/desc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="start"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:text="Description"
|
||||
android:textColor="#B3EEEEEE"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,19 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/channel_fragment"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_gravity="end|top"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="50dp">
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
>
|
||||
<FrameLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/channel_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="0dp"
|
||||
android:textColor="#FFEEEEEE"
|
||||
android:textStyle="bold"
|
||||
android:textSize="50sp" />
|
||||
</FrameLayout>
|
||||
<FrameLayout
|
||||
android:id="@+id/channel"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="end|top"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="170dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:textSize="40sp" />
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logo"
|
||||
android:src="@drawable/sad_cloud"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="#FFEEEEEE"
|
||||
android:text="Message"
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -1,53 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/info_fragment"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:background="@drawable/rounded_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/info_logo"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:padding="10dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="216dp"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
>
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="100dp"
|
||||
android:background="#FF263238"
|
||||
android:gravity="start|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:padding="7dp">
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginTop="0dp"
|
||||
android:gravity="start"
|
||||
android:textColor="#FFEEEEEE"
|
||||
android:textSize="22sp" />
|
||||
<LinearLayout
|
||||
android:id="@+id/info"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/info_desc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="start"
|
||||
android:singleLine="true"
|
||||
android:textColor="#B3EEEEEE"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/logo"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/rounded_white_left"
|
||||
android:padding="10dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:background="@drawable/rounded_background2" />
|
||||
</FrameLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="0dp"
|
||||
android:background="@drawable/rounded_dark_right"
|
||||
android:gravity="start|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:padding="7dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:gravity="start"
|
||||
android:textColor="#FFEEEEEE"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/desc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="start"
|
||||
android:singleLine="true"
|
||||
android:textColor="#B3EEEEEE"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center" >
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/bar"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:indeterminateTint="@color/white"/>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/transparent"
|
||||
android:clickable="true"
|
||||
android:focusable="false"
|
||||
android:gravity="center"
|
||||
>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scroll"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/player_fragment"
|
||||
android:id="@+id/player"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
|
||||
@@ -3,22 +3,26 @@
|
||||
android:id="@+id/row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/transparent"
|
||||
android:clickable="true"
|
||||
android:focusable="false"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/row_header"
|
||||
android:id="@+id/header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:gravity="start"
|
||||
android:textColor="#FFEEEEEE"
|
||||
android:textSize="22sp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/row_items"
|
||||
android:id="@+id/items"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/dialog"
|
||||
android:id="@+id/setting"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
@@ -8,15 +8,17 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingLeft="7dp"
|
||||
android:paddingRight="7dp"
|
||||
android:background="#FF263238"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:background="@drawable/rounded_dark_left"
|
||||
>
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_name"
|
||||
@@ -27,9 +29,10 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/"
|
||||
android:textSize="14sp"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:layout_width="300dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
@@ -38,6 +41,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/check_version"
|
||||
/>
|
||||
<ProgressBar
|
||||
@@ -48,39 +52,58 @@
|
||||
<TextView
|
||||
android:id="@+id/version_name"
|
||||
android:text=""
|
||||
android:textSize="14sp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/exit"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/exit"
|
||||
/>
|
||||
<Switch
|
||||
android:id="@+id/switch_channel_reversal"
|
||||
android:text="@string/title_channel_reversal"
|
||||
android:layout_marginTop="10dp"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Switch
|
||||
android:id="@+id/switch_channel_num"
|
||||
android:text="@string/title_channel_num"
|
||||
android:layout_marginTop="10dp"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Switch
|
||||
android:id="@+id/switch_time"
|
||||
android:text="@string/title_time"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Switch
|
||||
android:id="@+id/switch_boot_startup"
|
||||
android:text="@string/title_boot_startup"
|
||||
android:layout_marginTop="10dp"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Switch
|
||||
android:id="@+id/switch_grid"
|
||||
android:text="@string/title_grid"
|
||||
android:layout_marginTop="10dp"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/appreciate"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/appreciate"
|
||||
android:background="@color/white" />
|
||||
android:background="@drawable/rounded_white_right" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/show_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ScrollView
|
||||
android:id="@+id/scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
>
|
||||
<FrameLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/time"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="end|top"
|
||||
android:layout_marginTop="25dp"
|
||||
android:layout_marginEnd="50dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:textSize="32sp" />
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
@@ -1,8 +1,7 @@
|
||||
<resources>
|
||||
<color name="fastlane_background">#30000000</color>
|
||||
<color name="black">#000</color>
|
||||
<color name="white">#FFF</color>
|
||||
<color name="gray_overlay">#7F000000</color>
|
||||
<color name="focus">#0096a6</color>
|
||||
<color name="blur">#FF263238</color>
|
||||
<color name="blur">#223239</color>
|
||||
<color name="ic_launcher_background">#223239</color>
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#223239</color>
|
||||
</resources>
|
||||
@@ -1,8 +1,10 @@
|
||||
<resources>
|
||||
<string name="app_name">我的电视</string>
|
||||
<string name="title_channel_reversal">换台反转</string>
|
||||
<string name="title_channel_num">数字选台</string>
|
||||
<string name="title_channel_num">换台时显示频道号</string>
|
||||
<string name="check_version">检查更新</string>
|
||||
<string name="title_boot_startup">开机自启</string>
|
||||
<string name="title_grid">网格样式</string>
|
||||
<string name="title_grid">频道列表采用网格样式</string>
|
||||
<string name="title_time">显示时间</string>
|
||||
<string name="exit">退出</string>
|
||||
</resources>
|
||||
@@ -1,25 +1,3 @@
|
||||
<resources>
|
||||
|
||||
<style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
|
||||
<item name="cardType">infoUnder</item>
|
||||
<item name="infoVisibility">always</item>
|
||||
<!-- In order to keep backward compatibility we have to create an icon on right. -->
|
||||
<item name="lbImageCardViewType">Title|Content|IconOnRight</item>
|
||||
<!-- Deprecated. Use 'Widget.Leanback.ImageCardView.InfoAreaStyle' instead. -->
|
||||
<item name="infoAreaBackground">@null</item>
|
||||
</style>
|
||||
|
||||
<style name="CustomImageCardViewImageStyle" parent="Widget.Leanback.ImageCardView.ImageStyle">
|
||||
<item name="android:layout_width">160dp</item>
|
||||
<item name="android:layout_height">40dp</item>
|
||||
<item name="android:adjustViewBounds">true</item>
|
||||
<item name="android:contentDescription">@null</item>
|
||||
<item name="android:scaleType">centerCrop</item>
|
||||
<item name="layout_viewType">main</item>
|
||||
</style>
|
||||
|
||||
<style name="CustomImageCardTheme" parent="Theme.Leanback">
|
||||
<item name="imageCardViewStyle">@style/CustomImageCardViewStyle</item>
|
||||
<item name="imageCardViewImageStyle">@style/CustomImageCardViewImageStyle</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,8 +1,6 @@
|
||||
<resources>
|
||||
|
||||
<style name="Theme.MyTV" parent="@style/Theme.Leanback">
|
||||
<item name="browsePaddingStart">11dp</item>
|
||||
<item name="browsePaddingEnd">11dp</item>
|
||||
<item name="browseRowsMarginTop">54dp</item>
|
||||
<item name="android:fitsSystemWindows">false</item>
|
||||
</style>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user