Compare commits

..
6 Commits
Author SHA1 Message Date
Li ZongYing 5aa08f51a1 error page 2024-04-26 19:57:12 +08:00
Li ZongYing fe92fa5853 optimize style 2024-04-25 22:36:37 +08:00
Li ZongYing 3d8ab65955 fix ysp 2024-04-23 19:41:30 +08:00
Li ZongYing 5485a686bc fix crash on 4.2 2024-04-19 20:13:43 +08:00
Li ZongYing 716efb5a83 change style 2024-04-19 19:26:06 +08:00
Li ZongYing ca82d5409f add time 2024-04-18 10:21:54 +08:00
56 changed files with 1587 additions and 476 deletions
+51
View File
@@ -1,10 +1,61 @@
## 更新日志 ## 更新日志
### v1.8.6(通用)
* 增加错误显示
* 节目预告优化
* 频道列表页单击切换频道
### v1.8.4(通用)
* 解決部分情況下打開後黑屏问题
* 延长频道信息显示时间,增加信息显示长度
* 调整频道列表间距
* 其他样式优化
### v1.8.2(通用)
* 修复无法播放问题
### v1.8.0(通用)
* 修复返回键无法退出问题
* 设置中增加退出按钮
* 修改非电视上的样式
* 修复低版本闪退问题
### v1.7.8(通用)
* 修复播放过程中的卡顿问题
* 增加时间显示
* 频道号优化
* 遥控器左键打开频道列表
* 遥控器右键打开设置菜单
* 频道列表空白处点击隐藏频道列表
### v1.7.6(通用) ### v1.7.6(通用)
* 临时去掉部分频道,提高频道的可用性 * 临时去掉部分频道,提高频道的可用性
* 部分频道增加节目单 * 部分频道增加节目单
### v1.7.5(安卓5及以上专用)
* 修复播放过程中的卡顿问题
* 增加时间显示
* 频道号优化
* 设置中增加退出按钮
### v1.7.4(通用)
* 修复368
### v1.7.3(安卓5及以上专用)
* 临时去掉部分频道,提高频道的可用性
* 部分频道增加节目单
* 遥控器左键进入节目列表
* 遥控器右键进入菜单
### v1.7.2(通用) ### v1.7.2(通用)
* 支持节目列表网格样式和行样式切换,软件重启后生效 * 支持节目列表网格样式和行样式切换,软件重启后生效
+23 -17
View File
@@ -4,17 +4,16 @@
## 使用 ## 使用
下载安装: 1. 下载
[github](https://github.com/lizongying/my-tv/releases/) * [github](https://github.com/lizongying/my-tv/releases/)
[gitee](https://gitee.com/lizongying/my-tv/releases/) * [gitee](https://gitee.com/lizongying/my-tv/releases/)
2. 安裝
小米电视可以使用小米电视助手进行安装 * U盘安装
* 小米电视可以使用小米电视助手进行安装
如电视可以启用ADB,也可以通过ADB进行安装: * 如电视可以启用ADB,也可以通过ADB进行安装
```shell
```shell adb install my-tv.apk
adb install my-tv.apk ```
```
![image](./screenshots/img_3.png) ![image](./screenshots/img_3.png)
![image](./screenshots/img_2.png) ![image](./screenshots/img_2.png)
@@ -31,13 +30,20 @@ adb install my-tv.apk
* CHC高清三个电影频道 * CHC高清三个电影频道
* 地方频道 * 地方频道
* 收藏夹 * 收藏夹
* 自定义源
* 海外 * 海外
* 1.5.0 无法安装,1.5.1 可以安装 * 隐藏频道
* 节目增加预告 * 亮度调节
* 频道列表优化 * 音量调节
* epg更新后菜单更新 * 軟解
* 防止菜单第一个获取焦点 * 自動更新
## 版权说明
本项目仅供学习研究,禁止用于商业用途,请于下载二十四小时内删除。
本项目可能随时终止,请大家谨慎使用,建议使用官方渠道进行观看。
本项目使用的部分代码、图片、文字等资源来源于网络,如有侵权,请联系删除。
## 赞赏 ## 赞赏
+8 -3
View File
@@ -112,11 +112,16 @@ dependencies {
implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version" implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version"
implementation "com.squareup.retrofit2:converter-protobuf:$retrofit2_version" implementation "com.squareup.retrofit2:converter-protobuf:$retrofit2_version"
implementation "com.squareup.retrofit2:retrofit:$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 '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.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("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC")
implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.3' implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.3'
+18 -10
View File
@@ -1,37 +1,43 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature <uses-feature
android:name="android.hardware.touchscreen" android:name="android.hardware.touchscreen"
android:required="false" /> android:required="false" />
<uses-feature <uses-feature
android:name="android.software.leanback" android:name="android.software.leanback"
android:required="true" /> android:required="true" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <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" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application <application
android:name=".MyTvApplication"
android:allowBackup="true" android:allowBackup="true"
android:largeHeap="true"
android:icon="@drawable/logo"
android:logo="@drawable/logo"
android:banner="@drawable/banner" android:banner="@drawable/banner"
android:icon="@drawable/logo"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:largeHeap="true"
android:logo="@drawable/logo"
android:networkSecurityConfig="@xml/network" android:networkSecurityConfig="@xml/network"
android:usesCleartextTraffic="true" android:supportsRtl="true"
android:theme="@style/Theme.MyTV"> android:theme="@style/Theme.MyTV"
android:usesCleartextTraffic="true">
<activity <activity
android:keepScreenOn="true"
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:keepScreenOn="true"
android:screenOrientation="landscape"> android:screenOrientation="landscape">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<receiver <receiver
android:name=".BootReceiver" android:name=".BootReceiver"
android:exported="true"> android:exported="true">
@@ -40,15 +46,17 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".NetworkChangeReceiver" <receiver
android:name=".NetworkChangeReceiver"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<provider <provider
android:name=".InitializerProvider" android:name=".InitializerProvider"
android:authorities="${applicationId}.InitializerProvider" android:authorities="${applicationId}.InitializerProvider"
android:exported="false"/> android:exported="false" />
</application> </application>
</manifest> </manifest>
Binary file not shown.
Binary file not shown.
@@ -1,18 +1,21 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.graphics.Color import android.util.Log
import android.view.ContextThemeWrapper
import android.view.KeyEvent import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.ScaleAnimation import android.view.animation.ScaleAnimation
import android.widget.ImageView import androidx.core.view.marginTop
import androidx.core.view.updatePadding import androidx.core.view.setPadding
import androidx.leanback.widget.ImageCardView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.lizongying.mytv.databinding.CardBinding
import com.lizongying.mytv.models.TVListViewModel import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import kotlin.math.abs
class CardAdapter( class CardAdapter(
@@ -25,15 +28,31 @@ class CardAdapter(
private var listener: ItemListener? = null private var listener: ItemListener? = null
private var focused: View? = null private var focused: View? = null
var visiable = false var focusable = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = object : val inflater = LayoutInflater.from(parent.context)
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {} 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.isFocusable = true
cardView.isFocusableInTouchMode = true cardView.isFocusableInTouchMode = true
cardView.updatePadding(1, 1, 1, 1) return ViewHolder(cardView, binding)
return ViewHolder(cardView)
} }
fun clear() { fun clear() {
@@ -41,14 +60,14 @@ class CardAdapter(
recyclerView.invalidate() recyclerView.invalidate()
} }
private fun startScaleAnimation(view: View, fromScale: Float, toScale: Float, duration: Long) { private fun startScaleAnimation(view: View) {
val scaleAnimation = ScaleAnimation( val scaleAnimation = ScaleAnimation(
fromScale, toScale, 0.9f, 1.0f,
fromScale, toScale, 0.9f, 1.0f,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f ScaleAnimation.RELATIVE_TO_SELF, 0.5f
) )
scaleAnimation.duration = duration scaleAnimation.duration = 200
scaleAnimation.fillAfter = false scaleAnimation.fillAfter = false
view.startAnimation(scaleAnimation) view.startAnimation(scaleAnimation)
} }
@@ -57,7 +76,7 @@ class CardAdapter(
val item = tvListViewModel.getTVViewModel(position) val item = tvListViewModel.getTVViewModel(position)
val tvViewModel = item as TVViewModel val tvViewModel = item as TVViewModel
val cardView = viewHolder.view as ImageCardView val cardView = viewHolder.view
cardView.tag = tvViewModel cardView.tag = tvViewModel
val onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus -> val onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus ->
@@ -65,16 +84,12 @@ class CardAdapter(
listener?.onItemHasFocus(item) listener?.onItemHasFocus(item)
focused = cardView focused = cardView
startScaleAnimation(cardView, 0.9f, 1.0f, 200) if (focusable) {
// startScaleAnimation(cardView)
// if (mainFragment.shouldHasFocus(view.tag as TVViewModel)) { viewHolder.focus(true)
// } }
} else {
// if (visiable) { viewHolder.focus(false)
// startScaleAnimation(cardView, 0.9f, 1.0f, 200)
// } else {
// visiable = true
// }
} }
} }
@@ -84,6 +99,34 @@ class CardAdapter(
listener?.onItemClicked(item) 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? -> cardView.setOnKeyListener { _, keyCode, event: KeyEvent? ->
if (event?.action == KeyEvent.ACTION_DOWN) { if (event?.action == KeyEvent.ACTION_DOWN) {
return@setOnKeyListener listener?.onKey(keyCode) ?: false return@setOnKeyListener listener?.onKey(keyCode) ?: false
@@ -91,28 +134,62 @@ class CardAdapter(
false false
} }
cardView.titleText = tvViewModel.getTV().title viewHolder.binding.title.text = tvViewModel.getTV().title
Glide.with(viewHolder.view.context) viewHolder.binding.icon.let {
.load(tvViewModel.getTV().logo) Glide.with(viewHolder.view.context)
.centerInside() .load(tvViewModel.getTV().logo)
.into(cardView.mainImageView) .centerInside()
.into(it)
cardView.mainImageView.setBackgroundColor(Color.WHITE) }
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() } val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
if (!epg.isNullOrEmpty()) { if (!epg.isNullOrEmpty()) {
cardView.contentText = epg.last().title viewHolder.binding.desc.text = epg.last().title
} else { } else {
cardView.contentText = "" viewHolder.binding.desc.text = ""
} }
} }
override fun getItemCount() = tvListViewModel.size() 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 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) { fun toPosition(position: Int) {
@@ -1,10 +1,13 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.res.Resources
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.marginRight
import androidx.core.view.marginTop
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.lizongying.mytv.databinding.ChannelBinding import com.lizongying.mytv.databinding.ChannelBinding
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
@@ -14,8 +17,9 @@ class ChannelFragment : Fragment() {
private val binding get() = _binding!! private val binding get() = _binding!!
private val handler = Handler() private val handler = Handler()
private val delay: Long = 3000 private val delay: Long = 5000
private var channel = 0 private var channel = 0
private var channelCount = 0
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@@ -23,24 +27,62 @@ class ChannelFragment : Fragment() {
): View { ): View {
_binding = ChannelBinding.inflate(inflater, container, false) _binding = ChannelBinding.inflate(inflater, container, false)
_binding!!.root.visibility = View.GONE _binding!!.root.visibility = View.GONE
(activity as MainActivity).fragmentReady()
val application = requireActivity().applicationContext as MyTvApplication
val width = application.getWidth()
val height = application.getHeight()
val ratio = 16f / 9f
if (width.toFloat() / height > ratio) {
val x =
((Resources.getSystem().displayMetrics.widthPixels - height * ratio) / 2).toInt()
val originalLayoutParams =
binding.channel.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.rightMargin += x
binding.channel.layoutParams = originalLayoutParams
}
if (width.toFloat() / height < ratio) {
val y =
((height - Resources.getSystem().displayMetrics.widthPixels / ratio) / 2).toInt()
val originalLayoutParams =
binding.channel.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.topMargin += y
binding.channel.layoutParams = originalLayoutParams
}
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.rightMargin = application.px2Px(binding.channel.marginRight)
binding.channel.layoutParams = layoutParams
binding.content.textSize = application.px2PxFont(binding.content.textSize)
(activity as MainActivity).fragmentReady("ChannelFragment")
return binding.root return binding.root
} }
fun show(tvViewModel: TVViewModel) { fun show(tvViewModel: TVViewModel) {
handler.removeCallbacks(hideRunnable) handler.removeCallbacks(hideRunnable)
handler.removeCallbacks(playRunnable) 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 view?.visibility = View.VISIBLE
handler.postDelayed(hideRunnable, delay) handler.postDelayed(hideRunnable, delay)
} }
fun show(channel: String) { 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(hideRunnable)
handler.removeCallbacks(playRunnable) handler.removeCallbacks(playRunnable)
if (binding.channelContent.text == "") { if (channelCount < 2) {
binding.channelContent.text = channel binding.content.text = "${this.channel}"
view?.visibility = View.VISIBLE view?.visibility = View.VISIBLE
handler.postDelayed(playRunnable, delay) handler.postDelayed(playRunnable, delay)
} else { } else {
@@ -62,14 +104,18 @@ class ChannelFragment : Fragment() {
} }
private val hideRunnable = Runnable { private val hideRunnable = Runnable {
binding.channelContent.text = "" binding.content.text = ""
view?.visibility = View.GONE view?.visibility = View.GONE
channel = 0
channelCount = 0
} }
private val playRunnable = Runnable { private val playRunnable = Runnable {
(activity as MainActivity).play(channel - 1) (activity as MainActivity).play(channel - 1)
binding.channelContent.text = "" binding.content.text = ""
view?.visibility = View.GONE view?.visibility = View.GONE
channel = 0
channelCount = 0
} }
override fun onDestroyView() { override fun onDestroyView() {
@@ -0,0 +1,53 @@
package com.lizongying.mytv
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.marginLeft
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 layoutParamsMain = binding.main.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsMain.leftMargin = application.px2Px(binding.main.marginLeft)
binding.main.layoutParams = layoutParamsMain
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"
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ val Context.appVersionCode: Long
* Return the version name of the app which is defined in build.gradle. * Return the version name of the app which is defined in build.gradle.
* eg:1.0.0 * eg:1.0.0
*/ */
val Context.appVersionName: String get() = packageInfo.versionName ?: "" val Context.appVersionName: String get() = packageInfo.versionName
val Context.appSignature: String val Context.appSignature: String
get() { 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 package com.lizongying.mytv
import android.content.res.Resources
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.marginBottom
import androidx.core.view.marginLeft
import androidx.core.view.marginTop
import androidx.core.view.setPadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.lizongying.mytv.databinding.InfoBinding import com.lizongying.mytv.databinding.InfoBinding
@@ -16,31 +21,68 @@ class InfoFragment : Fragment() {
private val binding get() = _binding!! private val binding get() = _binding!!
private val handler = Handler() private val handler = Handler()
private val delay: Long = 3000 private val delay: Long = 5000
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
_binding = InfoBinding.inflate(inflater, container, false) _binding = InfoBinding.inflate(inflater, container, false)
val application = requireActivity().applicationContext as MyTvApplication
val width = application.getWidth()
val height = application.getHeight()
val ratio = 16f / 9f
if (width.toFloat() / height < ratio) {
val y =
((height - Resources.getSystem().displayMetrics.widthPixels / ratio) / 2).toInt()
val originalLayoutParams = binding.info.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.bottomMargin += y
binding.info.layoutParams = originalLayoutParams
}
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.leftMargin = application.px2Px(binding.main.marginLeft)
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!!.root.visibility = View.GONE _binding!!.root.visibility = View.GONE
(activity as MainActivity).fragmentReady()
(activity as MainActivity).fragmentReady("InfoFragment")
return binding.root return binding.root
} }
fun show(tvViewModel: TVViewModel) { fun show(tvViewModel: TVViewModel) {
binding.textView.text = tvViewModel.getTV().title binding.title.text = tvViewModel.getTV().title
Glide.with(this) Glide.with(this)
.load(tvViewModel.getTV().logo) .load(tvViewModel.getTV().logo)
.into(binding.infoLogo) .into(binding.logo)
Log.i(TAG, "${tvViewModel.getTV().title} ${tvViewModel.epg.value}") Log.i(TAG, "${tvViewModel.getTV().title} ${tvViewModel.epg.value}")
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() } val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
if (!epg.isNullOrEmpty()) { if (!epg.isNullOrEmpty()) {
binding.infoDesc.text = epg.last().title binding.desc.text = epg.last().title
} else { } else {
binding.infoDesc.text = "" binding.desc.text = ""
} }
handler.removeCallbacks(removeRunnable) 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
}
}
@@ -11,13 +11,13 @@ import android.util.Log
import android.view.GestureDetector import android.view.GestureDetector
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View
import android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION import android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import com.lizongying.mytv.requests.Request
import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
@@ -28,9 +28,11 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private var ready = 0 private var ready = 0
private val playerFragment = PlayerFragment() private val playerFragment = PlayerFragment()
private val errorFragment = ErrorFragment()
private val mainFragment = MainFragment() private val mainFragment = MainFragment()
private val infoFragment = InfoFragment() private val infoFragment = InfoFragment()
private val channelFragment = ChannelFragment() private val channelFragment = ChannelFragment()
private var timeFragment = TimeFragment()
private val settingFragment = SettingFragment() private val settingFragment = SettingFragment()
private var doubleBackToExitPressedOnce = false private var doubleBackToExitPressedOnce = false
@@ -42,17 +44,17 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private val delayHideSetting: Long = 10000 private val delayHideSetting: Long = 10000
init { init {
Utils.setRequestListener(this)
}
private fun syncTime() {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() } val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() }
utilsJob.start() utilsJob.start()
// utilsJob.await()
} }
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
@@ -66,10 +68,13 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
if (savedInstanceState == null) { if (savedInstanceState == null) {
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.add(R.id.main_browse_fragment, playerFragment) .add(R.id.main_browse_fragment, playerFragment)
.add(R.id.main_browse_fragment, errorFragment)
.add(R.id.main_browse_fragment, timeFragment)
.add(R.id.main_browse_fragment, infoFragment) .add(R.id.main_browse_fragment, infoFragment)
.add(R.id.main_browse_fragment, channelFragment) .add(R.id.main_browse_fragment, channelFragment)
.add(R.id.main_browse_fragment, mainFragment) .add(R.id.main_browse_fragment, mainFragment)
.hide(mainFragment) .hide(mainFragment)
.hide(errorFragment)
.commit() .commit()
} }
gestureDetector = GestureDetector(this, GestureListener()) gestureDetector = GestureDetector(this, GestureListener())
@@ -92,7 +97,6 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
Log.i(TAG, "net ${Build.VERSION.SDK_INT}") Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
ready++ ready++
} }
} }
fun showInfoFragment(tvViewModel: TVViewModel) { fun showInfoFragment(tvViewModel: TVViewModel) {
@@ -102,6 +106,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) { private fun showChannel(channel: String) {
if (!mainFragment.isHidden) { if (!mainFragment.isHidden) {
return return
@@ -118,7 +160,6 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
fun play(tvViewModel: TVViewModel) { fun play(tvViewModel: TVViewModel) {
playerFragment.play(tvViewModel) playerFragment.play(tvViewModel)
mainFragment.view?.requestFocus()
} }
fun play(itemPosition: Int) { fun play(itemPosition: Int) {
@@ -133,14 +174,6 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
mainFragment.next() mainFragment.next()
} }
private fun prevSource() {
// mainFragment.prevSource()
}
private fun nextSource() {
// mainFragment.nextSource()
}
fun switchMainFragment() { fun switchMainFragment() {
val transaction = supportFragmentManager.beginTransaction() val transaction = supportFragmentManager.beginTransaction()
@@ -163,6 +196,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
fun settingDelayHide() { fun settingDelayHide() {
handler.removeCallbacks(hideSetting) handler.removeCallbacks(hideSetting)
handler.postDelayed(hideSetting, delayHideSetting) handler.postDelayed(hideSetting, delayHideSetting)
showTime()
} }
fun settingNeverHide() { fun settingNeverHide() {
@@ -187,14 +221,25 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
} }
} }
fun fragmentReady() { fun fragmentReady(tag: String) {
ready++ ready++
Log.i(TAG, "ready $ready") Log.i(TAG, "ready $tag $ready ")
if (ready == 5) { if (ready == 8) {
mainFragment.fragmentReady() mainFragment.fragmentReady()
showTime()
} }
} }
private fun showTime() {
Log.i(TAG, "showTime ${SP.time}")
if (SP.time) {
timeFragment.show()
} else {
timeFragment.hide()
}
mainFragment.changeStyle()
}
override fun onTouchEvent(event: MotionEvent?): Boolean { override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event != null) { if (event != null) {
gestureDetector.onTouchEvent(event) gestureDetector.onTouchEvent(event)
@@ -266,9 +311,14 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private val hideSetting = Runnable { private val hideSetting = Runnable {
if (settingFragment.isVisible) { if (settingFragment.isVisible) {
settingFragment.dismiss() settingFragment.dismiss()
showTime()
} }
} }
private fun hideSettingFragment() {
handler.postDelayed(hideSetting, 0)
}
private fun channelUp() { private fun channelUp() {
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
if (SP.channelReversal) { if (SP.channelReversal) {
@@ -308,6 +358,11 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
return return
} }
if (settingFragment.isVisible) {
hideSettingFragment()
return
}
if (doubleBackToExitPressedOnce) { if (doubleBackToExitPressedOnce) {
super.onBackPressed() super.onBackPressed()
return return
@@ -435,30 +490,17 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
} }
KeyEvent.KEYCODE_DPAD_LEFT -> { KeyEvent.KEYCODE_DPAD_LEFT -> {
channelUp() if (mainFragment.isHidden && !settingFragment.isVisible) {
// if (mainFragment.isHidden) { switchMainFragment()
// prevSource() return true
// } else { }
//// if (mainFragment.tvListViewModel.getTVViewModelCurrent()
//// ?.getItemPosition() == 0
//// ) {
////// mainFragment.toLastPosition()
//// hideMainFragment()
//// }
// }
} }
KeyEvent.KEYCODE_DPAD_RIGHT -> { KeyEvent.KEYCODE_DPAD_RIGHT -> {
channelDown() if (mainFragment.isHidden && !settingFragment.isVisible) {
// if (mainFragment.isHidden) { showSetting()
// nextSource() return true
// } else { }
//// if (mainFragment.tvListViewModel.getTVViewModelCurrent()
//// ?.getItemPosition() == mainFragment.tvListViewModel.maxNum[mainFragment.selectedPosition] - 1
//// ) {
//// mainFragment.toFirstPosition()
//// }
// }
} }
} }
return false return false
@@ -475,7 +517,8 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private fun getAppSignature() = this.appSignature private fun getAppSignature() = this.appSignature
override fun onStart() { override fun onStart() {
Log.i(TAG, "onStart") Log.i(TAG, "onStart MainActivity")
syncTime()
super.onStart() super.onStart()
} }
@@ -499,7 +542,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
} }
override fun onRequestFinished(message: String?) { override fun onRequestFinished(message: String?) {
fragmentReady() fragmentReady("request")
} }
private companion object { private companion object {
@@ -1,5 +1,6 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.res.Resources
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
@@ -7,18 +8,22 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.view.marginBottom
import androidx.core.view.marginLeft
import androidx.core.view.marginTop
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lizongying.mytv.Utils.dpToPx
import com.lizongying.mytv.api.YSP import com.lizongying.mytv.api.YSP
import com.lizongying.mytv.databinding.MenuBinding
import com.lizongying.mytv.databinding.RowBinding import com.lizongying.mytv.databinding.RowBinding
import com.lizongying.mytv.databinding.ShowBinding
import com.lizongying.mytv.models.ProgramType import com.lizongying.mytv.models.ProgramType
import com.lizongying.mytv.models.TVList
import com.lizongying.mytv.models.TVListViewModel import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import com.lizongying.mytv.requests.Request
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -29,21 +34,66 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
private var rowList: MutableList<View> = mutableListOf() private var rowList: MutableList<View> = mutableListOf()
private var _binding: ShowBinding? = null private var _binding: MenuBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
var tvListViewModel = TVListViewModel() var tvListViewModel = TVListViewModel()
private var lastVideoUrl = "" private var lastVideoUrl = ""
private lateinit var application: MyTvApplication
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
_binding = ShowBinding.inflate(inflater, container, false) _binding = MenuBinding.inflate(inflater, container, false)
application = requireActivity().applicationContext as MyTvApplication
val width = application.getWidth()
val height = application.getHeight()
val ratio = 16f / 9f
if (width.toFloat() / height > ratio) {
val x =
((Resources.getSystem().displayMetrics.widthPixels - height * ratio) / 2).toInt()
val originalLayoutParams =
binding.scroll.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.leftMargin += x
originalLayoutParams.rightMargin += x
binding.scroll.layoutParams = originalLayoutParams
Log.i(
TAG,
"binding.scroll ${Resources.getSystem().displayMetrics.widthPixels} ${height * ratio}"
)
}
if (width.toFloat() / height < ratio) {
val y =
((height - Resources.getSystem().displayMetrics.widthPixels / ratio) / 2).toInt()
val originalLayoutParams =
binding.scroll.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.topMargin += y
originalLayoutParams.bottomMargin += y
binding.scroll.layoutParams = originalLayoutParams
}
binding.menu.setOnClickListener {
hideSelf()
}
return binding.root return binding.root
} }
private fun hideSelf() {
requireActivity().supportFragmentManager.beginTransaction()
.hide(this)
.commit()
}
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
@@ -71,43 +121,55 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
val adapter = val adapter =
CardAdapter( CardAdapter(
itemBinding.rowItems, itemBinding.items,
this, this,
tvListViewModelCurrent, tvListViewModelCurrent,
) )
rowList.add(itemBinding.rowItems) rowList.add(itemBinding.items)
adapter.setItemListener(this) adapter.setItemListener(this)
itemBinding.rowHeader.text = k itemBinding.header.text = k
itemBinding.rowItems.tag = idx.toInt() itemBinding.items.tag = idx.toInt()
itemBinding.rowItems.adapter = adapter 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) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
(activity as MainActivity).mainActive() (activity as MainActivity).mainActive()
} }
}) })
val itemDecoration = context?.let { GrayOverlayItemDecoration(it) } val itemDecoration = context?.let { ItemDecoration(it) }
if (itemDecoration != null) { if (itemDecoration != null) {
itemBinding.rowItems.addItemDecoration(itemDecoration) itemBinding.items.addItemDecoration(itemDecoration)
} }
if (SP.grid) { if (SP.grid) {
itemBinding.rowItems.layoutManager = itemBinding.items.layoutManager =
GridLayoutManager(context, 6) GridLayoutManager(context, 6)
itemBinding.rowItems.layoutParams.height = itemBinding.items.layoutParams.height =
dpToPx(92 * ((tvListViewModelCurrent.size() + 6 - 1) / 6)) application.dp2Px(110 * ((tvListViewModelCurrent.size() + 6 - 1) / 6) + 5)
} else { } else {
itemBinding.rowItems.layoutManager = itemBinding.items.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
} }
val layoutParams = itemBinding.row.layoutParams as ViewGroup.MarginLayoutParams val layoutParams = itemBinding.row.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = dpToPx(11F) layoutParams.topMargin = application.dp2Px(11)
itemBinding.row.layoutParams = layoutParams 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.leftMargin = application.px2Px(itemBinding.header.marginLeft)
itemBinding.header.layoutParams = layoutParamsHeader
itemBinding.header.textSize = application.px2PxFont(itemBinding.header.textSize)
content.addView(itemBinding.row) content.addView(itemBinding.row)
idx++ idx++
@@ -123,8 +185,13 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
if (tvViewModel.errInfo.value != null if (tvViewModel.errInfo.value != null
&& tvViewModel.getTV().id == itemPosition && tvViewModel.getTV().id == itemPosition
) { ) {
Toast.makeText(context, tvViewModel.errInfo.value, Toast.LENGTH_SHORT) if (tvViewModel.errInfo.value == "") {
.show() (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) { _ -> tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
@@ -164,12 +231,28 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
} }
} }
} }
(activity as MainActivity).fragmentReady() (activity as MainActivity).fragmentReady("MainFragment")
} }
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { fun changeStyle() {
super.onViewCreated(view, savedInstanceState) 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 { override fun onKey(keyCode: Int): Boolean {
@@ -190,13 +273,14 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
} }
override fun onItemHasFocus(tvViewModel: TVViewModel) { override fun onItemHasFocus(tvViewModel: TVViewModel) {
tvListViewModel.setItemPositionCurrent(tvViewModel.getTV().id)
val row = tvViewModel.getRowPosition() val row = tvViewModel.getRowPosition()
for (i in rowList) { for (i in rowList) {
if (i.tag as Int != row) { if (i.tag as Int != row) {
((i as RecyclerView).adapter as CardAdapter).focusable = false
((i as RecyclerView).adapter as CardAdapter).clear() ((i as RecyclerView).adapter as CardAdapter).clear()
} else {
((i as RecyclerView).adapter as CardAdapter).focusable = true
} }
} }
@@ -204,7 +288,6 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
} }
override fun onItemClicked(tvViewModel: TVViewModel) { override fun onItemClicked(tvViewModel: TVViewModel) {
Log.i(TAG, "onItemClicked")
if (this.isHidden) { if (this.isHidden) {
(activity as? MainActivity)?.switchMainFragment() (activity as? MainActivity)?.switchMainFragment()
return return
@@ -213,14 +296,15 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
if (itemPosition != tvViewModel.getTV().id) { if (itemPosition != tvViewModel.getTV().id) {
itemPosition = tvViewModel.getTV().id itemPosition = tvViewModel.getTV().id
tvListViewModel.setItemPosition(itemPosition) tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed() tvListViewModel.getTVViewModel(itemPosition)?.changed("menu")
Log.i(TAG, "onItemClicked ${tvViewModel.getTV().id}")
} }
(activity as? MainActivity)?.switchMainFragment() (activity as? MainActivity)?.switchMainFragment()
} }
fun setPosition() { fun setPosition() {
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) ?: return
val rowPosition = tvViewModel!!.getRowPosition() val rowPosition = tvViewModel.getRowPosition()
val itemPosition = tvViewModel.getItemPosition() val itemPosition = tvViewModel.getItemPosition()
setPosition(rowPosition, itemPosition) setPosition(rowPosition, itemPosition)
} }
@@ -260,8 +344,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
} }
fun fragmentReady() { fun fragmentReady() {
// request.fetchPage() tvListViewModel.getTVViewModel(itemPosition)?.changed("init")
tvListViewModel.getTVViewModel(itemPosition)?.changed()
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel -> tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
updateEPG(tvViewModel) updateEPG(tvViewModel)
@@ -273,7 +356,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
if (itemPosition > -1 && itemPosition < tvListViewModel.size()) { if (itemPosition > -1 && itemPosition < tvListViewModel.size()) {
this.itemPosition = itemPosition this.itemPosition = itemPosition
tvListViewModel.setItemPosition(itemPosition) tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed() tvListViewModel.getTVViewModel(itemPosition)?.changed("num")
} else { } else {
Toast.makeText(context, "频道不存在", Toast.LENGTH_SHORT).show() Toast.makeText(context, "频道不存在", Toast.LENGTH_SHORT).show()
} }
@@ -287,7 +370,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
itemPosition = tvListViewModel.size() - 1 itemPosition = tvListViewModel.size() - 1
} }
tvListViewModel.setItemPosition(itemPosition) tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed() tvListViewModel.getTVViewModel(itemPosition)?.changed("prev")
} }
} }
@@ -298,7 +381,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
itemPosition = 0 itemPosition = 0
} }
tvListViewModel.setItemPosition(itemPosition) tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed() tvListViewModel.getTVViewModel(itemPosition)?.changed("next")
} }
} }
@@ -323,6 +406,7 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
} }
override fun onHiddenChanged(hidden: Boolean) { override fun onHiddenChanged(hidden: Boolean) {
Log.i(TAG, "onHiddenChanged $hidden")
super.onHiddenChanged(hidden) super.onHiddenChanged(hidden)
if (!hidden) { if (!hidden) {
val tvModel = tvListViewModel.getTVViewModel(itemPosition) val tvModel = tvListViewModel.getTVViewModel(itemPosition)
@@ -331,15 +415,17 @@ class MainFragment : Fragment(), CardAdapter.ItemListener {
Log.i(TAG, "toPosition $rowPosition $itemPosition") Log.i(TAG, "toPosition $rowPosition $itemPosition")
for (i in rowList) { for (i in rowList) {
if (i.tag as Int == rowPosition) { 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 break
} }
} }
} else { } else {
view?.post { view?.post {
// for (i in rowList) { for (i in rowList) {
// ((i as RecyclerView).adapter as CardAdapter).visiable = false ((i as RecyclerView).adapter as CardAdapter).focusable = false
// } }
} }
} }
} }
@@ -0,0 +1,95 @@
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 var width = 0
private var height = 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()
val 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
}
deviation = width - Resources.getSystem().displayMetrics.widthPixels
scale = Resources.getSystem().displayMetrics.scaledDensity
}
fun getDisplayMetrics(): DisplayMetrics {
return displayMetrics
}
fun getRealDisplayMetrics(): DisplayMetrics {
return realDisplayMetrics
}
fun getWidth(): Int {
return width
}
fun getDeviation(): Int {
return deviation
}
fun getHeight(): Int {
return height
}
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) { override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error) super.onPlayerError(error)
Log.e(TAG, "PlaybackException $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("PlayerFragment")
return _binding!!.root return _binding!!.root
} }
@@ -13,6 +13,8 @@ object SP {
// If use channel num to select channel or not // If use channel num to select channel or not
private const val KEY_CHANNEL_NUM = "channel_num" private const val KEY_CHANNEL_NUM = "channel_num"
private const val KEY_TIME = "time"
// If start app on device boot or not // If start app on device boot or not
private const val KEY_BOOT_STARTUP = "boot_startup" private const val KEY_BOOT_STARTUP = "boot_startup"
@@ -41,6 +43,10 @@ object SP {
get() = sp.getBoolean(KEY_CHANNEL_NUM, true) get() = sp.getBoolean(KEY_CHANNEL_NUM, true)
set(value) = sp.edit().putBoolean(KEY_CHANNEL_NUM, value).apply() 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 var bootStartup: Boolean
get() = sp.getBoolean(KEY_BOOT_STARTUP, false) get() = sp.getBoolean(KEY_BOOT_STARTUP, false)
set(value) = sp.edit().putBoolean(KEY_BOOT_STARTUP, value).apply() 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.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager
import androidx.core.view.marginEnd
import androidx.core.view.marginTop
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.lizongying.mytv.databinding.SettingBinding import com.lizongying.mytv.databinding.SettingBinding
@@ -15,6 +18,14 @@ class SettingFragment : DialogFragment() {
private lateinit var updateManager: UpdateManager 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?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, 0) 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 { binding.switchBootStartup.run {
isChecked = SP.bootStartup isChecked = SP.bootStartup
setOnCheckedChangeListener { _, isChecked -> 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 layoutParamsDesc = binding.version.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsDesc.topMargin = application.px2Px(binding.version.marginTop)
binding.version.layoutParams = layoutParamsDesc
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 return binding.root
} }
@@ -0,0 +1,106 @@
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.marginRight
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
val width = application.getWidth()
val height = application.getHeight()
val ratio = 16f / 9f
if (width.toFloat() / height > ratio) {
val x =
((Resources.getSystem().displayMetrics.widthPixels - height * ratio) / 2).toInt()
val originalLayoutParams = binding.time.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.rightMargin += x
binding.time.layoutParams = originalLayoutParams
}
if (width.toFloat() / height < ratio) {
val y =
((height - Resources.getSystem().displayMetrics.widthPixels / ratio) / 2).toInt()
val originalLayoutParams = binding.time.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.topMargin += y
binding.time.layoutParams = originalLayoutParams
}
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.rightMargin = application.px2Px(binding.time.marginRight)
binding.time.layoutParams = layoutParams
binding.content.textSize = application.px2PxFont(binding.content.textSize)
(activity as MainActivity).fragmentReady("TimeFragment")
return binding.root
}
override fun onResume() {
super.onResume()
if (view?.visibility == View.VISIBLE) {
handler.removeCallbacks(showRunnable)
handler.postDelayed(showRunnable, 0)
}
}
override fun onPause() {
super.onPause()
handler.removeCallbacks(showRunnable)
}
private val showRunnable: Runnable = Runnable {
run {
if (_binding == null) {
return@Runnable
}
binding.content.text = getDateFormat("HH:mm")
handler.postDelayed(showRunnable, delay)
}
}
fun show() {
view?.visibility = View.VISIBLE
handler.removeCallbacks(showRunnable)
handler.postDelayed(showRunnable, 0)
}
fun hide() {
view?.visibility = View.GONE
handler.removeCallbacks(showRunnable)
}
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
import androidx.core.content.PermissionChecker.checkSelfPermission import androidx.core.content.PermissionChecker.checkSelfPermission
import com.lizongying.mytv.api.ReleaseV2 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.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -32,7 +32,7 @@ class UpdateManager(
) : ) :
ConfirmationFragment.ConfirmationListener { ConfirmationFragment.ConfirmationListener {
private var myRequest = MyRequest() private var myRequest = ReleaseRequest()
private var release: ReleaseV2? = null private var release: ReleaseV2? = null
private var downloadReceiver: DownloadReceiver? = null private var downloadReceiver: DownloadReceiver? = null
@@ -62,7 +62,7 @@ class UpdateManager(
private fun updateUI(text: String) { private fun updateUI(text: String) {
val dialog = ConfirmationFragment(this@UpdateManager, text) val dialog = ConfirmationFragment(this@UpdateManager, text)
dialog.show(settingFragment.fragmentManager, TAG) settingFragment.fragmentManager?.let { dialog.show(it, TAG) }
} }
private fun haveStoragePermission(): Boolean { private fun haveStoragePermission(): Boolean {
@@ -5,6 +5,7 @@ import android.os.Build
import android.util.TypedValue import android.util.TypedValue
import com.google.gson.Gson import com.google.gson.Gson
import com.lizongying.mytv.api.TimeResponse import com.lizongying.mytv.api.TimeResponse
import com.lizongying.mytv.requests.Request
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.IOException import java.io.IOException
@@ -15,6 +16,8 @@ import java.util.Locale
object Utils { object Utils {
private var between: Long = 0 private var between: Long = 0
private var listener: Request.RequestListener? = null
fun getDateFormat(format: String): String { fun getDateFormat(format: String): String {
return SimpleDateFormat( return SimpleDateFormat(
format, format,
@@ -38,6 +41,10 @@ object Utils {
println("Failed to retrieve timestamp from server: ${e.message}") println("Failed to retrieve timestamp from server: ${e.message}")
} }
between = System.currentTimeMillis() - currentTimeMillis between = System.currentTimeMillis() - currentTimeMillis
withContext(Dispatchers.Main) {
listener?.onRequestFinished(null)
}
} }
/** /**
@@ -72,10 +79,26 @@ object Utils {
} }
fun dpToPx(dp: Int): Int { fun dpToPx(dp: Int): Int {
// val density = Resources.getSystem().displayMetrics.density
// return (dp * density + 0.5f).toInt()
return TypedValue.applyDimension( return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), Resources.getSystem().displayMetrics TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), Resources.getSystem().displayMetrics
).toInt() ).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 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 myUrl = "https://lyrics.run/"
private val protoUrl = "https://capi.yangshipin.cn/" private val protoUrl = "https://capi.yangshipin.cn/"
private val traceUrl = "https://btrace.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 jceUrl = "https://jacc.ysp.cctv.cn/"
private val fUrl = "https://m.fengshows.com/" private val fUrl = "https://m.fengshows.com/"
@@ -67,6 +69,22 @@ class ApiClient {
.build().create(YSPBtraceService::class.java) .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 { val yspJceService: YSPJceService by lazy {
Retrofit.Builder() Retrofit.Builder()
.baseUrl(jceUrl) .baseUrl(jceUrl)
@@ -1,4 +1,4 @@
package com.lizongying.mytv package com.lizongying.mytv.api
import android.content.Context 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 package com.lizongying.mytv.api
import android.content.Context import android.content.Context
import com.lizongying.mytv.Encryptor
import com.lizongying.mytv.MainActivity
import com.lizongying.mytv.SP import com.lizongying.mytv.SP
import com.lizongying.mytv.Utils.getDateTimestamp import com.lizongying.mytv.Utils.getDateTimestamp
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
@@ -142,4 +140,9 @@ object YSP {
val hashedData = encryptor.hash2(e) ?: return "" val hashedData = encryptor.hash2(e) ?: return ""
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } } 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 package com.lizongying.mytv.api
import retrofit2.Call import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.Field import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded import retrofit2.http.FormUrlEncoded
import retrofit2.http.Headers import retrofit2.http.Headers
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
@@ -68,4 +68,58 @@ interface YSPBtraceService {
@Field("c_guid") c_guid: String = "", @Field("c_guid") c_guid: String = "",
@Field("c_vuid") c_vuid: String = "-", @Field("c_vuid") c_vuid: String = "-",
): Call<Void> ): 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>
} }
@@ -1,4 +1,4 @@
package com.lizongying.mytv package com.lizongying.mytv.models
import com.lizongying.mytv.models.ProgramType import com.lizongying.mytv.models.ProgramType
import java.io.Serializable import java.io.Serializable
@@ -15,18 +15,21 @@ data class TV(
var programType: ProgramType, var programType: ProgramType,
var needToken: Boolean = false, var needToken: Boolean = false,
var mustToken: Boolean = false, var mustToken: Boolean = false,
) : Serializable {
) : Serializable {
override fun toString(): String { override fun toString(): String {
return "TV{" + return "TV{" +
"id=" + id + "id=" + id +
", title='" + title + '\'' + ", title='" + title + '\'' +
", alias='" + alias + '\'' +
", videoUrl='" + videoUrl + '\'' + ", videoUrl='" + videoUrl + '\'' +
", channel='" + channel + '\'' +
", logo='" + logo + '\'' + ", logo='" + logo + '\'' +
", pid='" + pid + '\'' + ", pid='" + pid + '\'' +
", sid='" + sid + '\'' + ", sid='" + sid + '\'' +
", programType='" + programType + '\'' + ", programType='" + programType + '\'' +
", needToken='" + needToken + '\'' +
", mustToken='" + mustToken + '\'' +
'}' '}'
} }
} }
@@ -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 { object TVList {
val list: Map<String, List<TV>> by lazy { val list: Map<String, List<TV>> by lazy {
@@ -14,7 +14,7 @@ object TVList {
0, 0,
"CCTV1 综合", "CCTV1 综合",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/d57905b93540bd15f0c48230dbbbff7ee0d645ff539e38866e2d15c8b9f7dfcd.png?imageMogr2/format/webp",
"600001859", "600001859",
@@ -27,7 +27,7 @@ object TVList {
0, 0,
"CCTV2 财经", "CCTV2 财经",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/20115388de0207131af17eac86c33049b95d69eaff064e55653a1b941810a006.png?imageMogr2/format/webp",
"600001800", "600001800",
@@ -40,7 +40,7 @@ object TVList {
0, 0,
"CCTV3 综艺", "CCTV3 综艺",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/7b7a65c712450da3deb6ca66fbacf4f9aee00d3f20bd80eafb5ada01ec63eb3a.png?imageMogr2/format/webp",
"600001801", "600001801",
@@ -53,10 +53,7 @@ object TVList {
0, 0,
"CCTV4 中文国际", "CCTV4 中文国际",
"CCTV4", "CCTV4",
listOf( 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"
),
"央视", "央视",
"https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/f357e58fdbcc076a3d65e1f958c942b2e14f14342c60736ceed98b092d35356a.png?imageMogr2/format/webp",
"600001814", "600001814",
@@ -69,7 +66,7 @@ object TVList {
0, 0,
"CCTV5 体育", "CCTV5 体育",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/0a6a7138952675983a3d854df7688557b286d59aa06166edae51506f9204d655.png?imageMogr2/format/webp",
"600001818", "600001818",
@@ -82,7 +79,7 @@ object TVList {
0, 0,
"CCTV6 电影", "CCTV6 电影",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/741515efda91f03f455df8a7da4ee11fa9329139c276435cf0a9e2af398d5bf2.png?imageMogr2/format/webp",
"600108442", "600108442",
@@ -95,7 +92,7 @@ object TVList {
0, 0,
"CCTV7 国防军事", "CCTV7 国防军事",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/b29af94e295ebdf646cefb68122c429b9cd921f498ca20d2d8070252536f9ff9.png?imageMogr2/format/webp",
"600004092", "600004092",
@@ -108,7 +105,7 @@ object TVList {
0, 0,
"CCTV8 电视剧", "CCTV8 电视剧",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/ad51de94426a0ba039e6dd6a8534ea98ecc813a6176bde87b4f18cc34d6d7590.png?imageMogr2/format/webp",
"600001803", "600001803",
@@ -121,7 +118,7 @@ object TVList {
0, 0,
"CCTV9 纪录", "CCTV9 纪录",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/2ed1b4deeca179d5db806bb941790f82eb92a1b7299c1c38fe027f95a5caee5e.png?imageMogr2/format/webp",
"600004078", "600004078",
@@ -134,7 +131,7 @@ object TVList {
0, 0,
"CCTV10 科教", "CCTV10 科教",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/aa6157ec65188cd41826e5a2f088c3d6d153205f5f6428258d12c59999e221aa.png?imageMogr2/format/webp",
"600001805", "600001805",
@@ -147,7 +144,7 @@ object TVList {
0, 0,
"CCTV11 戏曲", "CCTV11 戏曲",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/ed12ed7c7a1034dae4350011fe039284c5d5a836506b28c9e32e3c75299625c0.png?imageMogr2/format/webp",
"600001806", "600001806",
@@ -160,7 +157,7 @@ object TVList {
0, 0,
"CCTV12 社会与法", "CCTV12 社会与法",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/484083cffaa40df7e659565e8cb4d1cc740158a185512114167aa21fa0c59240.png?imageMogr2/format/webp",
"600001807", "600001807",
@@ -173,9 +170,7 @@ object TVList {
0, 0,
"CCTV13 新闻", "CCTV13 新闻",
"CCTV13", "CCTV13",
listOf( listOf(),
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226233/index.m3u8"
),
"央视", "央视",
"https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/266da7b43c03e2312186b4a999e0f060e8f15b10d2cc2c9aa32171819254cf1a.png?imageMogr2/format/webp",
"600001811", "600001811",
@@ -188,7 +183,7 @@ object TVList {
0, 0,
"CCTV14 少儿", "CCTV14 少儿",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/af6b603896938dc346fbb16abfc63c12cba54b0ec9d18770a15d347d115f12d5.png?imageMogr2/format/webp",
"600001809", "600001809",
@@ -201,7 +196,7 @@ object TVList {
0, 0,
"CCTV15 音乐", "CCTV15 音乐",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/2ceee92188ef684efe0d8b90839c4f3ad450d179dc64d59beff417059453af47.png?imageMogr2/format/webp",
"600001815", "600001815",
@@ -214,10 +209,7 @@ object TVList {
0, 0,
"CCTV16 奥林匹克", "CCTV16 奥林匹克",
"CCTV16-HD", "CCTV16-HD",
listOf( 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"
),
"央视", "央视",
"https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/53793fa7bacd3a93ff6dc5d2758418985e1f952a316c335d663b572d8bdcd74d.png?imageMogr2/format/webp",
"600098637", "600098637",
@@ -230,7 +222,7 @@ object TVList {
0, 0,
"CCTV17 农业农村", "CCTV17 农业农村",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/ddef563072f8bad2bea5b9e52674cb7b4ed50efb20c26e61994dfbdf05c1e3c0.png?imageMogr2/format/webp",
"600001810", "600001810",
@@ -243,7 +235,7 @@ object TVList {
0, 0,
"CCTV5+ 体育赛事", "CCTV5+ 体育赛事",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/649ad76a90bfef55b05db9fe52e006487280f619089099d5dc971e387fc6eff0.png?imageMogr2/format/webp",
"600001817", "600001817",
@@ -282,7 +274,7 @@ object TVList {
0, 0,
"风云剧场", "风云剧场",
"CCTV风云剧场频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/4d549e53e6d0f632d5a633d1945280797b153e588f919221a07faa869812cc89.png?imageMogr2/format/webp",
"600099658", "600099658",
@@ -295,7 +287,7 @@ object TVList {
0, 0,
"第一剧场", "第一剧场",
"CCTV第一剧场频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/a556bd7d93ce65e18f243a8892b5604f4faa994a4897315914216a710a706208.png?imageMogr2/format/webp",
"600099655", "600099655",
@@ -308,7 +300,7 @@ object TVList {
0, 0,
"怀旧剧场", "怀旧剧场",
"CCTV怀旧剧场频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/5661bd04fecdb6e899f801147a22ab5d3a475bf2b62e30aec2c0023190ebc9b1.png?imageMogr2/format/webp",
"600099620", "600099620",
@@ -334,7 +326,7 @@ object TVList {
0, 0,
"风云音乐", "风云音乐",
"CCTV风云音乐频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/bbf1d024c5228b8dd128b0e3cb1717d173fab4ee84c3a4c8a57b1a215362ca3b.png?imageMogr2/format/webp",
"600099660", "600099660",
@@ -347,7 +339,7 @@ object TVList {
0, 0,
"兵器科技", "兵器科技",
"CCTV兵器科技频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/4c6b6a6d3839889f34d33db3c2f80233b26b74d3489b393487635f8704e70796.png?imageMogr2/format/webp",
"600099649", "600099649",
@@ -360,7 +352,7 @@ object TVList {
0, 0,
"风云足球", "风云足球",
"CCTV风云足球频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/cd1e2bb52b06a991de168733e5ff0f1d85adc8042d40c8f393f723543e5dd08a.png?imageMogr2/format/webp",
"600099636", "600099636",
@@ -373,7 +365,7 @@ object TVList {
0, 0,
"高尔夫网球", "高尔夫网球",
"CCTV高尔夫·网球频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/cdd1b31ede7a5ad049ed53d9a072422f829e72dd062ed2c19e077fdd01699071.png?imageMogr2/format/webp",
"600099659", "600099659",
@@ -386,7 +378,7 @@ object TVList {
0, 0,
"女性时尚", "女性时尚",
"CCTV女性时尚频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/fa28955ce8b2539d728bf4c6a13a46ff57ad76eae46627f7bcfb1ed8a613d3fc.png?imageMogr2/format/webp",
"600099650", "600099650",
@@ -399,7 +391,7 @@ object TVList {
0, 0,
"央视文化精品", "央视文化精品",
"CCTV央视文化精品频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/14ac5ce40482cacd3d4b37435222bfe86af2b452a2f04ecbfc1d13d76edd7c57.png?imageMogr2/format/webp",
"600099653", "600099653",
@@ -412,7 +404,7 @@ object TVList {
0, 0,
"央视台球", "央视台球",
"CCTV央视台球频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/10e14a92478011aa6c3c8562e62127f3b1908e29fcd78e4b2b24b9e6d3ec2fbc.png?imageMogr2/format/webp",
"600099652", "600099652",
@@ -425,7 +417,7 @@ object TVList {
0, 0,
"电视指南", "电视指南",
"CCTV电视指南频道", "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", "https://resources.yangshipin.cn/assets/oms/image/202306/244d72c0eb1615ed7d51c2f5db5a67f306aa3f58c05bc2d34de3aa7e956dc8c9.png?imageMogr2/format/webp",
"600099656", "600099656",
@@ -453,7 +445,7 @@ object TVList {
0, 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", "https://resources.yangshipin.cn/assets/oms/image/202306/9bd372ca292a82ce3aa08772b07efc4af1f85c21d1f268ea33440c49e9a0a488.png?imageMogr2/format/webp",
"600002483", "600002483",
@@ -466,10 +458,7 @@ object TVList {
0, 0,
"湖南卫视", "湖南卫视",
"湖南卫视", "湖南卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226307/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226211/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/4120e89d3079d08aa17d382f69a2308ec70839b278367763c34a34666c75cb88.png?imageMogr2/format/webp",
"600002475", "600002475",
@@ -482,10 +471,7 @@ object TVList {
0, 0,
"湖北卫视", "湖北卫视",
"湖北卫视", "湖北卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226477/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226194/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/7a6be5a2bb1dc53a945c016ff1f525dc4a84c51db371c15c89aa55404b0ba784.png?imageMogr2/format/webp",
"600002508", "600002508",
@@ -498,10 +484,7 @@ object TVList {
0, 0,
"辽宁卫视", "辽宁卫视",
"辽宁卫视", "辽宁卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226546/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226201/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/ac4ed6058a87c101ae7147ebc38905d0cae047fb73fd277ee5049b84f52bda36.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/ac4ed6058a87c101ae7147ebc38905d0cae047fb73fd277ee5049b84f52bda36.png?imageMogr2/format/webp",
"600002505", "600002505",
@@ -514,10 +497,7 @@ object TVList {
0, 0,
"江苏卫视", "江苏卫视",
"江苏卫视", "江苏卫视",
listOf( 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"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/380ad685c0c1d5b2c902246b8d2df6d3f9b45e2837abcfe493075bbded597a31.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/380ad685c0c1d5b2c902246b8d2df6d3f9b45e2837abcfe493075bbded597a31.png?imageMogr2/format/webp",
"600002521", "600002521",
@@ -530,10 +510,7 @@ object TVList {
0, 0,
"江西卫视", "江西卫视",
"江西卫视", "江西卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226344/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225764/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/3c760d0d00463855890e8a1864ea4a6b6dd66b90c29b4ac714a4b17c16519871.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/3c760d0d00463855890e8a1864ea4a6b6dd66b90c29b4ac714a4b17c16519871.png?imageMogr2/format/webp",
"600002503", "600002503",
@@ -546,7 +523,7 @@ object TVList {
0, 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", "https://resources.yangshipin.cn/assets/oms/image/202306/22d403f07a7cf5410b3ad3ddb65a11aa229a32475fac213f5344c9f0ec330ca1.png?imageMogr2/format/webp",
"600002513", "600002513",
@@ -559,7 +536,7 @@ object TVList {
0, 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", "https://resources.yangshipin.cn/assets/oms/image/202306/28886880a4dc0f06fb7e0a528a1def0591d61a65870e29176ede0cc92033bbfd.png?imageMogr2/format/webp",
"600002485", "600002485",
@@ -572,10 +549,7 @@ object TVList {
0, 0,
"广西卫视", "广西卫视",
"广西卫视", "广西卫视",
listOf( listOf(),
"http://live.gxrb.com.cn/tv/gxtvlive03/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225770/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/54b7e97cb816bb223fe05f3fc44da2c7820eb66e8550c19d23100f2c414ecc38.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/54b7e97cb816bb223fe05f3fc44da2c7820eb66e8550c19d23100f2c414ecc38.png?imageMogr2/format/webp",
"600002509", "600002509",
@@ -588,10 +562,7 @@ object TVList {
0, 0,
"重庆卫视", "重庆卫视",
"重庆卫视", "重庆卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226409/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226202/index.m3u8"
),
"地方", "地方",
R.drawable.chongqing, R.drawable.chongqing,
"600002531", "600002531",
@@ -604,10 +575,7 @@ object TVList {
0, 0,
"河南卫视", "河南卫视",
"河南卫视", "河南卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226480/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225767/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/74925962148a6d31c85808b6cd4e444c2a54bab393d2c5fc85e960b50e22fa86.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/74925962148a6d31c85808b6cd4e444c2a54bab393d2c5fc85e960b50e22fa86.png?imageMogr2/format/webp",
"600002525", "600002525",
@@ -620,10 +588,7 @@ object TVList {
0, 0,
"河北卫视", "河北卫视",
"河北卫视", "河北卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226406/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225750/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/d545becdc81c60197b08c7f47380705e4665ed3fe55efc8b855e486f6e655378.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/d545becdc81c60197b08c7f47380705e4665ed3fe55efc8b855e486f6e655378.png?imageMogr2/format/webp",
"600002493", "600002493",
@@ -636,10 +601,7 @@ object TVList {
0, 0,
"贵州卫视", "贵州卫视",
"贵州卫视", "贵州卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226474/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225793/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/4eb45f4781d33d872af027dc01c941559aab55667dd99cc5c22bef7037807b13.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/4eb45f4781d33d872af027dc01c941559aab55667dd99cc5c22bef7037807b13.png?imageMogr2/format/webp",
"600002490", "600002490",
@@ -652,10 +614,7 @@ object TVList {
0, 0,
"北京卫视", "北京卫视",
"北京卫视", "北京卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221225728/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226222/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/f4f23633c578beea49a3841d88d3490100f029ee349059fa532869db889872c5.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/f4f23633c578beea49a3841d88d3490100f029ee349059fa532869db889872c5.png?imageMogr2/format/webp",
"600002309", "600002309",
@@ -668,10 +627,7 @@ object TVList {
0, 0,
"黑龙江卫视", "黑龙江卫视",
"黑龙江卫视", "黑龙江卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226327/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226215/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/d8273ae9be698ce2db21f5b886ecac95a73429593f93713c60ed8c12c38bf0d3.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/d8273ae9be698ce2db21f5b886ecac95a73429593f93713c60ed8c12c38bf0d3.png?imageMogr2/format/webp",
"600002498", "600002498",
@@ -684,10 +640,7 @@ object TVList {
0, 0,
"浙江卫视", "浙江卫视",
"浙江卫视", "浙江卫视",
listOf( listOf(),
"http://hw-m-l.cztv.com/channels/lantian/channel01/1080p.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226199/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/a66c836bd98ba3e41a2e9a570d4b9c50dedc6839e9de333e2e78212ad505f37e.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/a66c836bd98ba3e41a2e9a570d4b9c50dedc6839e9de333e2e78212ad505f37e.png?imageMogr2/format/webp",
"600002520", "600002520",
@@ -700,10 +653,7 @@ object TVList {
0, 0,
"安徽卫视", "安徽卫视",
"安徽卫视", "安徽卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226391/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226203/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/f35fa04b51b1ee4984b03578b65403570868ebca03c6c01e11b097f999a58d9b.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/f35fa04b51b1ee4984b03578b65403570868ebca03c6c01e11b097f999a58d9b.png?imageMogr2/format/webp",
"600002532", "600002532",
@@ -716,10 +666,7 @@ object TVList {
0, 0,
"深圳卫视", "深圳卫视",
"深圳卫视", "深圳卫视",
listOf( 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"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/d59fec04c902e3581c617136d02d4b9b8c4cbe64272781ddd3525e80c823edb7.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/d59fec04c902e3581c617136d02d4b9b8c4cbe64272781ddd3525e80c823edb7.png?imageMogr2/format/webp",
"600002481", "600002481",
@@ -732,10 +679,7 @@ object TVList {
0, 0,
"四川卫视", "四川卫视",
"四川卫视", "四川卫视",
listOf( 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"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/3276a414ae0eaa0f116f2045cd913367967d0c7c1e978e8621ac3879436c6ed7.png?imageMogr2/format/webp",
"600002516", "600002516",
@@ -748,10 +692,7 @@ object TVList {
0, 0,
"东南卫视", "东南卫视",
"福建东南卫视", "福建东南卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226341/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225766/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/3208fe6564a293c21b711333fb3edb05bb5b406cff840573c9a8d839680a1579.png?imageMogr2/format/webp",
"600002484", "600002484",
@@ -764,10 +705,7 @@ object TVList {
0, 0,
"海南卫视", "海南卫视",
"海南卫视", "海南卫视",
listOf( listOf(),
"http://ottrrs.hl.chinamobile.com/PLTV/88888888/224/3221226465/index.m3u8",
"http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225769/index.m3u8"
),
"地方", "地方",
"https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202306/6e060391fde0469801fc3d84dbf204b4f8d650d251f17d7595a6964c0bb99e81.png?imageMogr2/format/webp",
"600002506", "600002506",
@@ -875,7 +813,7 @@ object TVList {
0, 0,
"CGTN", "CGTN",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/a72dff758ca1c17cd0ecc8cedc11b893d208f409d5e6302faa0e9d298848abc3.png?imageMogr2/format/webp",
"600014550", "600014550",
@@ -888,7 +826,7 @@ object TVList {
0, 0,
"CGTN 法语频道", "CGTN 法语频道",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/a8d0046a47433d952bf6ed17062deb8bd2184ba9aec0f7781df6bf9487a3ffcf.png?imageMogr2/format/webp",
"600084704", "600084704",
@@ -901,7 +839,7 @@ object TVList {
0, 0,
"CGTN 俄语频道", "CGTN 俄语频道",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/bf0a820893cbaf20dd0333e27042e1ef9c8806e5b602b6a8c95af399db0bc77a.png?imageMogr2/format/webp",
"600084758", "600084758",
@@ -914,7 +852,7 @@ object TVList {
0, 0,
"CGTN 阿拉伯语频道", "CGTN 阿拉伯语频道",
"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", "https://resources.yangshipin.cn/assets/oms/image/202306/2e44e2aa3e7a1cedf07fd0ae59fe69e86a60a2632660a006e3e9e7397b2d107e.png?imageMogr2/format/webp",
"600084782", "600084782",
@@ -927,10 +865,7 @@ object TVList {
0, 0,
"CGTN 西班牙语频道", "CGTN 西班牙语频道",
"CGTN西班牙语频道", "CGTN西班牙语频道",
listOf( listOf(),
"http://livees.cgtn.com/500e/prog_index.m3u8",
"http://livees.cgtn.com/1000e/prog_index.m3u8"
),
"国际", "国际",
"https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp", "https://resources.yangshipin.cn/assets/oms/image/202309/7c337e3dbe64402ec7e4678a619a4a6d95144e42f35161181ff78e143b7cf67a.png?imageMogr2/format/webp",
"600084744", "600084744",
@@ -943,7 +878,7 @@ object TVList {
0, 0,
"CGTN 纪录频道", "CGTN 纪录频道",
"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", "https://resources.yangshipin.cn/assets/oms/image/202309/74d3ac436a7e374879578de1d87a941fbf566d39d5632b027c5097891ed32bd5.png?imageMogr2/format/webp",
"600084781", "600084781",
@@ -965,6 +900,7 @@ object TVList {
v.forEach { v1 -> v.forEach { v1 ->
if (!v1.mustToken) { if (!v1.mustToken) {
v1.id = id v1.id = id
v1.needToken = false
id++ id++
group.add(v1) group.add(v1)
} }
@@ -16,10 +16,6 @@ class TVListViewModel : ViewModel() {
val itemPosition: LiveData<Int> val itemPosition: LiveData<Int>
get() = _itemPosition get() = _itemPosition
private val _itemPositionCurrent = MutableLiveData<Int>()
val itemPositionCurrent: LiveData<Int>
get() = _itemPositionCurrent
fun addTVViewModel(tvViewModel: TVViewModel) { fun addTVViewModel(tvViewModel: TVViewModel) {
if (_tvListViewModel.value == null) { if (_tvListViewModel.value == null) {
_tvListViewModel.value = mutableListOf(tvViewModel) _tvListViewModel.value = mutableListOf(tvViewModel)
@@ -32,23 +28,15 @@ class TVListViewModel : ViewModel() {
return _tvListViewModel.value?.get(id) return _tvListViewModel.value?.get(id)
} }
fun getTVViewModelCurrent(): TVViewModel? {
return _itemPositionCurrent.value?.let { _tvListViewModel.value?.get(it) }
}
fun setItemPosition(position: Int) { fun setItemPosition(position: Int) {
_itemPosition.value = position _itemPosition.value = position
_itemPositionCurrent.value = position
}
fun setItemPositionCurrent(position: Int) {
_itemPositionCurrent.value = position
} }
fun size(): Int { fun size(): Int {
if (_tvListViewModel.value == null) { if (_tvListViewModel.value == null) {
return 0 return 0
} }
return _tvListViewModel.value!!.size return _tvListViewModel.value!!.size
} }
} }
@@ -1,10 +1,8 @@
package com.lizongying.mytv.models package com.lizongying.mytv.models
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.lizongying.mytv.TV
import com.lizongying.mytv.api.FEPG import com.lizongying.mytv.api.FEPG
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.programModel.Program import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.programModel.Program
import com.tencent.videolite.android.datamodel.cctvjce.TVProgram import com.tencent.videolite.android.datamodel.cctvjce.TVProgram
@@ -41,8 +39,8 @@ class TVViewModel(private var tv: TV) : ViewModel() {
val videoIndex: LiveData<Int> val videoIndex: LiveData<Int>
get() = _videoIndex get() = _videoIndex
private val _change = MutableLiveData<Boolean>() private val _change = MutableLiveData<String>()
val change: LiveData<Boolean> val change: LiveData<String>
get() = _change get() = _change
private val _ready = MutableLiveData<Boolean>() private val _ready = MutableLiveData<Boolean>()
@@ -56,36 +54,23 @@ class TVViewModel(private var tv: TV) : ViewModel() {
if (_videoUrl.value!!.last().contains("cctv.cn")) { if (_videoUrl.value!!.last().contains("cctv.cn")) {
tv.videoUrl = tv.videoUrl.subList(0, tv.videoUrl.lastIndex) + listOf(url) tv.videoUrl = tv.videoUrl.subList(0, tv.videoUrl.lastIndex) + listOf(url)
} else { } else {
tv.videoUrl = tv.videoUrl + listOf(url) tv.videoUrl += listOf(url)
} }
} else { } else {
tv.videoUrl = tv.videoUrl + listOf(url) tv.videoUrl += listOf(url)
} }
_videoUrl.value = tv.videoUrl _videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoUrl.lastIndex _videoIndex.value = tv.videoUrl.lastIndex
} }
fun firstSource() { fun changed(from: String) {
if (_videoUrl.value!!.isNotEmpty()) { _change.value = from
setVideoIndex(0)
allReady()
} else {
Log.e(TAG, "no first")
}
}
fun changed() {
_change.value = true
} }
fun allReady() { fun allReady() {
_ready.value = true _ready.value = true
} }
fun setVideoIndex(videoIndex: Int) {
_videoIndex.value = videoIndex
}
init { init {
_videoUrl.value = tv.videoUrl _videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoUrl.lastIndex _videoIndex.value = tv.videoUrl.lastIndex
@@ -10,7 +10,7 @@ import retrofit2.Response
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
class MyRequest { class ReleaseRequest {
private var releaseService = ApiClient().releaseService private var releaseService = ApiClient().releaseService
suspend fun getRelease(): ReleaseV2? { suspend fun getRelease(): ReleaseV2? {
@@ -39,6 +39,6 @@ class MyRequest {
} }
companion object { companion object {
private const val TAG = "MyRequest" private const val TAG = "ReleaseRequest"
} }
} }
@@ -1,9 +1,10 @@
package com.lizongying.mytv package com.lizongying.mytv.requests
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import com.lizongying.mytv.Utils
import com.lizongying.mytv.Utils.getDateFormat import com.lizongying.mytv.Utils.getDateFormat
import com.lizongying.mytv.api.ApiClient import com.lizongying.mytv.api.ApiClient
import com.lizongying.mytv.api.Auth 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.FEPG
import com.lizongying.mytv.api.Info import com.lizongying.mytv.api.Info
import com.lizongying.mytv.api.InfoV2 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.LiveInfo
import com.lizongying.mytv.api.LiveInfoRequest import com.lizongying.mytv.api.LiveInfoRequest
import com.lizongying.mytv.api.YSP 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.YSPJceService
import com.lizongying.mytv.api.YSPProtoService import com.lizongying.mytv.api.YSPProtoService
import com.lizongying.mytv.api.YSPTokenService import com.lizongying.mytv.api.YSPTokenService
import com.lizongying.mytv.models.TVList
import com.lizongying.mytv.models.TVViewModel 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.oms.common.proto.pageModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.epgProgramModel 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.Cipher
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
import kotlin.random.Random
object Request { object Request {
@@ -39,6 +44,8 @@ object Request {
private var yspTokenService: YSPTokenService = ApiClient().yspTokenService private var yspTokenService: YSPTokenService = ApiClient().yspTokenService
private var yspApiService: YSPApiService = ApiClient().yspApiService private var yspApiService: YSPApiService = ApiClient().yspApiService
private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService 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 yspProtoService: YSPProtoService = ApiClient().yspProtoService
private var yspJceService: YSPJceService = ApiClient().yspJceService private var yspJceService: YSPJceService = ApiClient().yspJceService
private var fAuthService: FAuthService = ApiClient().fAuthService private var fAuthService: FAuthService = ApiClient().fAuthService
@@ -47,7 +54,7 @@ object Request {
private var token = "" private var token = ""
private var tokenFH = "" private var tokenFH = ""
private var needAuth = false private var needAuth = true
private var needToken = false private var needToken = false
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
@@ -111,11 +118,17 @@ object Request {
} else { } else {
if (!tvModel.getTV().mustToken) { if (!tvModel.getTV().mustToken) {
fetchAuth(tvModel, cookie) fetchAuth(tvModel, cookie)
} else {
// TODO
} }
} }
} else { } else {
fetchAuth(tvModel, cookie) fetchAuth(tvModel, cookie)
} }
} else {
val err = "认证结果错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
} else { } else {
@@ -130,11 +143,17 @@ object Request {
} else { } else {
if (!tvModel.getTV().mustToken) { if (!tvModel.getTV().mustToken) {
fetchAuth(tvModel, cookie) fetchAuth(tvModel, cookie)
} else {
// TODO
} }
} }
} else { } else {
fetchAuth(tvModel, cookie) fetchAuth(tvModel, cookie)
} }
} else {
val err = "认证状态错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
} }
@@ -151,11 +170,17 @@ object Request {
} else { } else {
if (!tvModel.getTV().mustToken) { if (!tvModel.getTV().mustToken) {
fetchAuth(tvModel, cookie) fetchAuth(tvModel, cookie)
} else {
// TODO
} }
} }
} else { } else {
fetchAuth(tvModel, cookie) fetchAuth(tvModel, cookie)
} }
} else {
val err = "认证请求错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
}) })
@@ -164,7 +189,7 @@ object Request {
private fun fetchVideo(tvModel: TVViewModel, cookie: String) { private fun fetchVideo(tvModel: TVViewModel, cookie: String) {
cancelCall() cancelCall()
if (::btraceRunnable.isInitialized) { if (Request::btraceRunnable.isInitialized) {
handler.removeCallbacks(btraceRunnable) handler.removeCallbacks(btraceRunnable)
} }
@@ -222,18 +247,25 @@ object Request {
if (!tvModel.getTV().mustToken) { if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie) // fetchAuth(tvModel, cookie)
} else {
// TODO
} }
} }
} else { } else {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie) // fetchAuth(tvModel, cookie)
} }
} else {
val err = "结果错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
} else { } else {
if (liveInfo?.data?.errinfo != null && liveInfo.data.errinfo == "应版权方要求,暂停提供直播信号,请点击观看其他精彩节目") { if (liveInfo?.data?.errinfo != null && liveInfo.data.errinfo == "应版权方要求,暂停提供直播信号,请点击观看其他精彩节目") {
Log.e(TAG, "$title error ${liveInfo.data.errinfo}") val err = "版权错误"
tvModel.setErrInfo(liveInfo.data.errinfo) Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} else { } else {
Log.e(TAG, "$title url error $request $liveInfo") Log.e(TAG, "$title url error $request $liveInfo")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
@@ -248,12 +280,18 @@ object Request {
if (!tvModel.getTV().mustToken) { if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie) // fetchAuth(tvModel, cookie)
} else {
// TODO
} }
} }
} else { } else {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie) // fetchAuth(tvModel, cookie)
} }
} else {
val err = "其他错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
} }
@@ -271,12 +309,18 @@ object Request {
if (!tvModel.getTV().mustToken) { if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie) // fetchAuth(tvModel, cookie)
} else {
// TODO
} }
} }
} else { } else {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie) // fetchAuth(tvModel, cookie)
} }
} else {
val err = "状态错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
} }
@@ -293,11 +337,17 @@ object Request {
} else { } else {
if (!tvModel.getTV().mustToken) { if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} else {
// TODO
} }
} }
} else { } else {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} }
} else {
val err = "请求错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
}) })
@@ -333,6 +383,8 @@ object Request {
val cookie = val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109" "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
fetchAuth(tvModel, cookie) fetchAuth(tvModel, cookie)
} else {
// TODO
} }
} }
} }
@@ -348,6 +400,8 @@ object Request {
val cookie = val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109" "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
fetchAuth(tvModel, cookie) fetchAuth(tvModel, cookie)
} else {
// TODO
} }
} }
} }
@@ -390,6 +444,8 @@ object Request {
val cookie = val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109" "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} else {
// TODO
} }
} }
} }
@@ -405,6 +461,8 @@ object Request {
val cookie = val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109" "versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} else {
// TODO
} }
} }
} }
@@ -440,6 +498,10 @@ object Request {
if (tvModel.tokenFHRetryTimes < tvModel.tokenFHRetryMaxTimes) { if (tvModel.tokenFHRetryTimes < tvModel.tokenFHRetryMaxTimes) {
tvModel.tokenFHRetryTimes++ tvModel.tokenFHRetryTimes++
fetchFAuth(tvModel) fetchFAuth(tvModel)
} else {
val err = "状态错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
} }
@@ -449,6 +511,10 @@ object Request {
if (tvModel.tokenFHRetryTimes < tvModel.tokenFHRetryMaxTimes) { if (tvModel.tokenFHRetryTimes < tvModel.tokenFHRetryMaxTimes) {
tvModel.tokenFHRetryTimes++ tvModel.tokenFHRetryTimes++
fetchFAuth(tvModel) fetchFAuth(tvModel)
} else {
val err = "请求错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} }
} }
}) })
@@ -531,7 +597,7 @@ object Request {
class BtraceRunnable(private val tvModel: TVViewModel) : Runnable { class BtraceRunnable(private val tvModel: TVViewModel) : Runnable {
override fun run() { override fun run() {
fetchBtrace(tvModel) fetchBtrace3(tvModel)
} }
} }
@@ -558,7 +624,116 @@ object Request {
callBtracePage?.enqueue(object : Callback<Void> { callBtracePage?.enqueue(object : Callback<Void> {
override fun onResponse(call: Call<Void>, response: Response<Void>) { override fun onResponse(call: Call<Void>, response: Response<Void>) {
if (response.isSuccessful) { 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 { } else {
Log.e(TAG, "$title kvcollect status error") Log.e(TAG, "$title kvcollect status error")
} }
@@ -701,6 +876,6 @@ object Request {
} }
fun setRequestListener(listener: RequestListener) { 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>
@@ -1,4 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" /> <solid android:color="@color/white" />
<corners android:radius="4dp" /> <!-- Adjust the radius as needed --> <corners
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
</shape> </shape>
+23
View File
@@ -0,0 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
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>
+60
View File
@@ -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>
+6 -7
View File
@@ -1,19 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/channel_fragment" android:id="@+id/channel"
android:layout_width="80dp" android:layout_width="90dp"
android:layout_height="80dp" android:layout_height="60dp"
android:layout_gravity="end|top" android:layout_gravity="end|top"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"
android:layout_marginEnd="50dp"> android:layout_marginEnd="170dp">
<TextView <TextView
android:id="@+id/channel_content" android:id="@+id/content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:layout_marginTop="0dp"
android:textColor="#FFEEEEEE" android:textColor="#FFEEEEEE"
android:textStyle="bold" android:textStyle="bold"
android:textSize="50sp" /> android:textSize="40sp" />
</FrameLayout> </FrameLayout>
+30
View File
@@ -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" >
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:gravity="center_vertical|center_horizontal"
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>
+13 -20
View File
@@ -1,40 +1,39 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/info_fragment" android:id="@+id/info"
android:layout_width="320dp" android:layout_width="400dp"
android:layout_height="80dp" android:layout_height="80dp"
android:layout_gravity="center_horizontal|bottom" android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="20dp" android:layout_marginBottom="20dp">
android:background="@drawable/rounded_background">
<ImageView <ImageView
android:id="@+id/info_logo" android:id="@+id/logo"
android:layout_width="100dp" android:layout_width="100dp"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginTop="8dp" android:background="@drawable/rounded_white_left"
android:padding="10dp" /> android:padding="10dp" />
<LinearLayout <LinearLayout
android:layout_width="216dp" android:id="@+id/main"
android:layout_width="300dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginStart="100dp" android:layout_marginStart="100dp"
android:background="#FF263238" android:background="@drawable/rounded_dark_right"
android:gravity="start|center_vertical" android:gravity="start|center_vertical"
android:orientation="vertical" android:orientation="vertical"
android:padding="7dp"> android:padding="7dp">
<TextView <TextView
android:id="@+id/textView" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="start" android:layout_gravity="start"
android:layout_marginTop="0dp"
android:gravity="start" android:gravity="start"
android:textColor="#FFEEEEEE" android:textColor="#FFEEEEEE"
android:textSize="22sp" /> android:textSize="20sp" />
<TextView <TextView
android:id="@+id/info_desc" android:id="@+id/desc"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="start" android:layout_gravity="start"
@@ -42,12 +41,6 @@
android:gravity="start" android:gravity="start"
android:singleLine="true" android:singleLine="true"
android:textColor="#B3EEEEEE" android:textColor="#B3EEEEEE"
android:textSize="16sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
<View
android:layout_width="8dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/rounded_background2" />
</FrameLayout> </FrameLayout>
@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/show_fragment" android:id="@+id/menu"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="@android:color/transparent"
android:clickable="true"
android:focusable="false"
>
<ScrollView <ScrollView
android:id="@+id/scroll_view" android:id="@+id/scroll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout <LinearLayout
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" 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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black" android:background="@color/black"
+8 -4
View File
@@ -3,22 +3,26 @@
android:id="@+id/row" android:id="@+id/row"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical"
android:background="@android:color/transparent"
android:clickable="true"
android:focusable="false"
>
<TextView <TextView
android:id="@+id/row_header" android:id="@+id/header"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="start" android:layout_gravity="start"
android:layout_marginTop="0dp" android:layout_marginTop="0dp"
android:layout_marginStart="10dp" android:layout_marginStart="20dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:gravity="start" android:gravity="start"
android:textColor="#FFEEEEEE" android:textColor="#FFEEEEEE"
android:textSize="22sp" /> android:textSize="22sp" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/row_items" android:id="@+id/items"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
+32 -9
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <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_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
> >
@@ -8,15 +8,17 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<LinearLayout <LinearLayout
android:id="@+id/content"
android:layout_width="300dp" android:layout_width="300dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="20dp" android:paddingTop="20dp"
android:paddingLeft="7dp" android:paddingLeft="7dp"
android:paddingRight="7dp" android:paddingRight="7dp"
android:background="#FF263238" android:background="@drawable/rounded_dark_left"
> >
<TextView <TextView
android:id="@+id/name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/app_name" android:text="@string/app_name"
@@ -27,9 +29,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:text="当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/" android:text="当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/"
android:textSize="14sp"
/> />
<LinearLayout <LinearLayout
android:layout_width="300dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
> >
@@ -38,6 +41,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:textSize="14sp"
android:text="@string/check_version" android:text="@string/check_version"
/> />
<ProgressBar <ProgressBar
@@ -48,39 +52,58 @@
<TextView <TextView
android:id="@+id/version_name" android:id="@+id/version_name"
android:text="" android:text=""
android:textSize="14sp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
<Button
android:id="@+id/exit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="@string/exit"
/>
<Switch <Switch
android:id="@+id/switch_channel_reversal" android:id="@+id/switch_channel_reversal"
android:text="@string/title_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_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<Switch <Switch
android:id="@+id/switch_channel_num" android:id="@+id/switch_channel_num"
android:text="@string/title_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_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<Switch <Switch
android:id="@+id/switch_boot_startup" android:id="@+id/switch_boot_startup"
android:text="@string/title_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_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<Switch <Switch
android:id="@+id/switch_grid" android:id="@+id/switch_grid"
android:text="@string/title_grid" android:text="@string/title_grid"
android:layout_marginTop="10dp" android:textSize="14sp"
android:layout_marginTop="5dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
<ImageView <ImageView
android:id="@+id/appreciate"
android:layout_width="300dp" android:layout_width="300dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/appreciate" android:src="@drawable/appreciate"
android:background="@color/white" /> android:background="@drawable/rounded_white_right" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
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="#FFEEEEEE"
android:textStyle="bold"
android:textSize="32sp" />
</FrameLayout>
+2 -3
View File
@@ -1,8 +1,7 @@
<resources> <resources>
<color name="fastlane_background">#30000000</color>
<color name="black">#000</color> <color name="black">#000</color>
<color name="white">#FFF</color> <color name="white">#FFF</color>
<color name="gray_overlay">#7F000000</color>
<color name="focus">#0096a6</color> <color name="focus">#0096a6</color>
<color name="blur">#FF263238</color> <color name="blur">#223239</color>
<color name="ic_launcher_background">#223239</color>
</resources> </resources>
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#223239</color>
</resources>
+4 -2
View File
@@ -1,8 +1,10 @@
<resources> <resources>
<string name="app_name">我的电视</string> <string name="app_name">我的电视</string>
<string name="title_channel_reversal">换台反转</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="check_version">检查更新</string>
<string name="title_boot_startup">开机自启</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> </resources>
-22
View File
@@ -1,25 +1,3 @@
<resources> <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> </resources>
-3
View File
@@ -1,8 +1,5 @@
<resources> <resources>
<style name="Theme.MyTV" parent="@style/Theme.Leanback"> <style name="Theme.MyTV" parent="@style/Theme.Leanback">
<item name="browsePaddingStart">11dp</item>
<item name="browsePaddingEnd">11dp</item>
<item name="browseRowsMarginTop">54dp</item>
</style> </style>
</resources> </resources>