Compare commits

..
53 Commits
Author SHA1 Message Date
Li ZongYing b157f1eb28 fix time 2024-05-07 11:38:10 +08:00
Li ZongYing 2b172ae615 fix retry bug 2024-05-01 17:00:33 +08:00
Li ZongYing 39e5cc7c1f opitimize request 2024-05-01 16:52:33 +08:00
Li ZongYing df5b006024 optimize style 2024-04-29 12:12:59 +08:00
Li ZongYing fe888c36ac left->start 2024-04-27 19:11:08 +08:00
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
Li ZongYing 3936629af8 add epg 2024-04-02 22:57:39 +08:00
Li ZongYing bb88e754a5 fix 368 2024-03-29 17:04:11 +08:00
Li ZongYing 49e96d18f4 fix version code 2024-03-17 09:38:46 +08:00
Li ZongYing 43343f8faa fix version code 2024-03-17 09:35:34 +08:00
Li ZongYing 0335f58a12 fix version name 2024-03-16 16:17:10 +08:00
Li ZongYing 9450de2c91 menu style 2024-03-16 13:14:14 +08:00
Li ZongYing d03de91c1e request optimize 2024-03-16 10:44:55 +08:00
Li ZongYing 7057e4a8b4 request optimize 2024-03-15 22:32:43 +08:00
Li ZongYing 188431a29f fix 4.0 crash 2024-03-14 22:08:28 +08:00
Li ZongYing 5c0152e36c optimize fetch token 2024-03-13 12:11:51 +08:00
Li ZongYing a63463559f phoenix retry 2024-03-13 11:31:37 +08:00
Li ZongYing d37ada1268 phoenix epg 2024-03-12 16:16:07 +08:00
Li ZongYing 00e6c8d66e cetv1 2024-03-07 19:37:03 +08:00
Li ZongYing 0ebc166acc bootStartup->false 2024-03-06 01:20:44 +08:00
Li ZongYing 2975a61e14 test RecyclerView alpha 2024-02-22 21:26:55 +08:00
Li ZongYing 86b9f5159a readme 2024-02-22 11:16:12 +08:00
Li ZongYing 10f261be8b fix save state 2024-02-21 21:57:05 +08:00
Li ZongYing 52d356817a fix some keys invalid 2024-02-21 19:26:11 +08:00
Li ZongYing 6ede7f76ef auto auth 2024-02-21 16:36:37 +08:00
Li ZongYing a4da01adf4 new memu style 2024-02-21 15:14:06 +08:00
Li ZongYing 8b33c7c94c optimize memu 2024-02-20 20:55:35 +08:00
Li ZongYing 84a6c0f78e menu delay disappears 2024-02-20 11:33:30 +08:00
Li ZongYing 1417bbf57b auto-startup 2024-02-19 15:57:39 +08:00
Li ZongYing ce678b8741 test release api 2024-02-11 10:14:23 +08:00
Li ZongYing 353dc0570f fix ysp 2024-02-09 22:36:48 +08:00
Li ZongYing d7a60531ab fix cctv6 2024-02-08 00:22:24 +08:00
Li ZongYing 764f2a1549 fix time error 2024-02-07 19:21:17 +08:00
Li ZongYing aa4d52681f fix ysp 2024-02-07 03:05:11 +08:00
Li ZongYing 8137515605 fix ysp err 2024-02-05 06:25:31 +08:00
Li ZongYing 6c16408b90 upgrade player 2024-02-04 22:23:28 +08:00
Li ZongYing 44ab3a6669 fix channel number did not disappear 2024-02-03 23:30:37 +08:00
Li ZongYing 917b245bbe readme 2024-02-02 23:34:39 +08:00
Li ZongYing dd4550cf4f replay on resume 2024-02-01 18:10:58 +08:00
Li ZongYing 4bdc625718 add channel 2024-01-26 18:30:31 +08:00
Li ZongYing cda19995b2 optimize channel switch 2024-01-25 15:12:47 +08:00
Li ZongYing 1eb2f257b5 change channel order 2024-01-25 11:04:06 +08:00
Li ZongYing 9feb0dc4bc channel num setting 2024-01-19 12:18:17 +08:00
Li ZongYing 46311036d6 number selection 2024-01-17 16:27:25 +08:00
Li ZongYing 1aade3a48d 17ok 2024-01-14 18:58:37 +08:00
Li ZongYing 2528cf33db test 17 2024-01-14 10:09:59 +08:00
Li ZongYing 4364f66ec5 17 2024-01-14 02:53:09 +08:00
Li ZongYing 49cd7b3af2 help page 2024-01-11 16:32:08 +08:00
58 changed files with 1735 additions and 726 deletions
+43 -1
View File
@@ -1,5 +1,48 @@
## 更新日志
### v1.9.4(通用)
* 修复默认时间不显示问题
* 设置页居中
* 频道号和时间对齐
### v1.9.2(通用)
* 修复一个重试的错误
### v1.9.0(通用)
* 减少视频播放失败情况
* 全面屏手机居中显示
### v1.8.8(通用)
* 样式优化
### v1.8.6(通用)
* 增加错误显示
* 节目预告优化
* 频道列表页单击切换频道
### v1.8.4(通用)
* 解決部分情況下打開後黑屏问题
* 延长频道信息显示时间,增加信息显示长度
* 调整频道列表间距
* 其他样式优化
### v1.8.2(通用)
* 修复无法播放问题
### v1.8.0(通用)
* 修复返回键无法退出问题
* 设置中增加退出按钮
* 修改非电视上的样式
* 修复低版本闪退问题
### v1.7.8(通用)
* 修复播放过程中的卡顿问题
@@ -20,7 +63,6 @@
* 增加时间显示
* 频道号优化
* 设置中增加退出按钮
*
### v1.7.4(通用)
+22 -20
View File
@@ -4,17 +4,16 @@
## 使用
下载安装:
[github](https://github.com/lizongying/my-tv/releases/)
[gitee](https://gitee.com/lizongying/my-tv/releases/)
小米电视可以使用小米电视助手进行安装
如电视可以启用ADB,也可以通过ADB进行安装:
```shell
adb install my-tv.apk
```
1. 下载
* [github](https://github.com/lizongying/my-tv/releases/)
* [gitee](https://gitee.com/lizongying/my-tv/releases/)
2. 安裝
* U盘安装
* 小米电视可以使用小米电视助手进行安装
* 如电视可以启用ADB,也可以通过ADB进行安装
```shell
adb install my-tv.apk
```
![image](./screenshots/img_3.png)
![image](./screenshots/img_2.png)
@@ -32,16 +31,19 @@ adb install my-tv.apk
* 地方频道
* 收藏夹
* 海外
* 节目增加预告
* 频道列表优化
* 自动更新
* 显示时间
* 软解
* 不同分辨率
* “比如:我输了3跳转到3号频道后数字没有消失时,这时我发现按错了频道,然后我想马上换到6频道输到6就变成36了”
* 隐藏频道
* 亮度调节
* 音量调节
* 軟解
* 自動更新
無法自啟的設備:
斐讯N1盒子,[Phicomm] Phicomm p230 (Android 7.1.2)
## 版权说明
本项目仅供学习研究,禁止用于商业用途,请于下载二十四小时内删除。
本项目可能随时终止,请大家谨慎使用,建议使用官方渠道进行观看。
本项目使用的部分代码、图片、文字等资源来源于网络,如有侵权,请联系删除。
## 赞赏
+11 -8
View File
@@ -13,7 +13,7 @@ android {
defaultConfig {
applicationId "com.lizongying.mytv"
minSdk 21
minSdk 17
targetSdk 33
versionCode VersionCode()
versionName VersionName()
@@ -95,7 +95,7 @@ static def VersionName() {
}
dependencies {
def media3_version = "1.3.1"
def media3_version = "1.2.1"
implementation "androidx.media3:media3-ui:$media3_version"
@@ -105,20 +105,21 @@ dependencies {
// For HLS playback support with ExoPlayer
implementation "androidx.media3:media3-exoplayer-hls:$media3_version"
// 21:2.11.0 17:2.6.4
def retrofit2_version = "2.11.0"
// 21:2.9.0 17:2.6.4
def retrofit2_version = "2.6.4"
implementation 'com.google.protobuf:protobuf-kotlin:3.25.1'
implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version"
implementation "com.squareup.retrofit2:converter-protobuf:$retrofit2_version"
implementation "com.squareup.retrofit2:retrofit:$retrofit2_version"
implementation 'androidx.leanback:leanback:1.2.0-alpha04'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'androidx.core:core-ktx:1.13.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0"
implementation 'androidx.core:core-ktx:1.11.0-beta02'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.leanback:leanback:1.2.0-alpha02'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC")
@@ -126,4 +127,6 @@ dependencies {
implementation 'com.google.android.exoplayer:exoplayer-ui:2.13.3'
implementation 'com.google.android.exoplayer:exoplayer-core:2.13.3'
implementation 'com.google.android.exoplayer:exoplayer-hls:2.13.3'
implementation 'javax.annotation:javax.annotation-api:1.3.2'
}
+6 -1
View File
@@ -14,7 +14,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".MyApplication"
android:name=".MyTvApplication"
android:allowBackup="true"
android:banner="@drawable/banner"
android:icon="@drawable/logo"
@@ -36,6 +36,9 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.windowLayoutInDisplayCutoutMode"
android:value="shortEdges" />
</activity>
<receiver
@@ -58,5 +61,7 @@
android:name=".InitializerProvider"
android:authorities="${applicationId}.InitializerProvider"
android:exported="false" />
<meta-data android:name="android.max_aspect" android:value="2.5" />
</application>
</manifest>
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,217 @@
package com.lizongying.mytv
import android.util.Log
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import android.view.animation.ScaleAnimation
import androidx.core.view.marginTop
import androidx.core.view.setPadding
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.lizongying.mytv.databinding.CardBinding
import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel
import kotlin.math.abs
class CardAdapter(
private val recyclerView: RecyclerView,
private val mainFragment: MainFragment,
private var tvListViewModel: TVListViewModel,
) :
RecyclerView.Adapter<CardAdapter.ViewHolder>() {
private var listener: ItemListener? = null
private var focused: View? = null
var focusable = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = CardBinding.inflate(inflater, parent, false)
val application = mainFragment.requireActivity().applicationContext as MyTvApplication
binding.card.layoutParams.width = application.px2Px(binding.card.layoutParams.width)
binding.card.layoutParams.height = application.px2Px(binding.card.layoutParams.height)
binding.icon.layoutParams.height = application.px2Px(binding.icon.layoutParams.height)
binding.icon.setPadding(application.px2Px(binding.icon.paddingTop))
binding.main.layoutParams.height = application.px2Px(binding.main.layoutParams.height)
binding.main.setPadding(application.px2Px(binding.main.paddingTop))
binding.title.textSize = application.px2PxFont(binding.title.textSize)
binding.desc.textSize = application.px2PxFont(binding.desc.textSize)
val layoutParams = binding.desc.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = application.px2Px(binding.desc.marginTop)
binding.desc.layoutParams = layoutParams
val cardView = binding.root
cardView.isClickable = true
cardView.isFocusable = true
cardView.isFocusableInTouchMode = true
return ViewHolder(cardView, binding)
}
fun clear() {
focused?.clearFocus()
recyclerView.invalidate()
}
private fun startScaleAnimation(view: View) {
val scaleAnimation = ScaleAnimation(
0.9f, 1.0f,
0.9f, 1.0f,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f
)
scaleAnimation.duration = 200
scaleAnimation.fillAfter = false
view.startAnimation(scaleAnimation)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
val item = tvListViewModel.getTVViewModel(position)
val tvViewModel = item as TVViewModel
val cardView = viewHolder.view
cardView.tag = tvViewModel
val onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus ->
if (hasFocus) {
listener?.onItemHasFocus(item)
focused = cardView
if (focusable) {
startScaleAnimation(cardView)
viewHolder.focus(true)
}
} else {
viewHolder.focus(false)
}
}
cardView.onFocusChangeListener = onFocusChangeListener
cardView.setOnClickListener { _ ->
listener?.onItemClicked(item)
}
var downX = 0f
var downY = 0f
val touchSlop = ViewConfiguration.get(cardView.context).scaledTouchSlop
cardView.setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
downX = event.x
downY = event.y
true
}
MotionEvent.ACTION_UP -> {
val upX = event.x
val upY = event.y
val deltaX = abs(upX - downX)
val deltaY = abs(upY - downY)
if (deltaX < touchSlop && deltaY < touchSlop) {
// 如果位移量小于阈值,则执行点击操作
cardView.performClick()
}
true
}
else -> false
}
}
cardView.setOnKeyListener { _, keyCode, event: KeyEvent? ->
if (event?.action == KeyEvent.ACTION_DOWN) {
return@setOnKeyListener listener?.onKey(keyCode) ?: false
}
false
}
viewHolder.binding.title.text = tvViewModel.getTV().title
viewHolder.binding.icon.let {
Glide.with(viewHolder.view.context)
.load(tvViewModel.getTV().logo)
.centerInside()
.into(it)
}
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
if (!epg.isNullOrEmpty()) {
viewHolder.binding.desc.text = epg.last().title
} else {
viewHolder.binding.desc.text = ""
}
}
override fun getItemCount() = tvListViewModel.size()
class ViewHolder(itemView: View, var binding: CardBinding) : RecyclerView.ViewHolder(itemView) {
val view = itemView
fun focus(hasFocus: Boolean) {
if (hasFocus) {
binding.main.setBackgroundResource(R.drawable.rounded_light_bottom)
} else {
binding.main.setBackgroundResource(R.drawable.rounded_dark_bottom)
}
}
}
fun updateEPG() {
for (i in 0 until recyclerView.childCount) {
val childView = recyclerView.getChildAt(i)
val viewHolder = recyclerView.getChildViewHolder(childView) as ViewHolder
if (viewHolder.view.tag != null && viewHolder.view.tag is TVViewModel) {
val tvViewModel = viewHolder.view.tag as TVViewModel
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
if (!epg.isNullOrEmpty()) {
val title = epg.last().title
if (viewHolder.binding.desc.text != title) {
viewHolder.binding.desc.text = title
Log.i(TAG, "updateEPG $title")
}
} else {
if (viewHolder.binding.desc.text != "") {
viewHolder.binding.desc.text = ""
Log.i(TAG, "updateEPG")
}
}
}
}
}
fun toPosition(position: Int) {
recyclerView.post {
recyclerView.scrollToPosition(position)
recyclerView.getChildAt(position)?.isSelected
recyclerView.getChildAt(position)?.requestFocus()
}
}
interface ItemListener {
fun onItemHasFocus(tvViewModel: TVViewModel)
fun onItemClicked(tvViewModel: TVViewModel)
fun onKey(keyCode: Int): Boolean
}
fun setItemListener(listener: ItemListener) {
this.listener = listener
}
companion object {
private const val TAG = "CardAdapter"
}
}
@@ -1,76 +0,0 @@
package com.lizongying.mytv
import android.content.Context
import android.graphics.Color
import android.view.ContextThemeWrapper
import android.view.ViewGroup
import android.widget.ImageView
import androidx.leanback.widget.ImageCardView
import androidx.leanback.widget.Presenter
import com.bumptech.glide.Glide
import com.lizongying.mytv.models.TVViewModel
class CardPresenter(
private val context: Context,
) : Presenter() {
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
val cardView = object :
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {}
cardView.isFocusable = true
cardView.isFocusableInTouchMode = true
return ViewHolder(cardView)
}
override fun onBindViewHolder(viewHolder: ViewHolder, item: Any?) {
val tvViewModel = item as TVViewModel
val cardView = viewHolder.view as ImageCardView
cardView.titleText = tvViewModel.getTV().title
cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT)
cardView.tag = tvViewModel.videoUrl.value
cardView.mainImageView?.let {
Glide.with(viewHolder.view.context)
.load(tvViewModel.getTV().logo)
.centerInside()
.into(it)
}
cardView.setBackgroundColor(Color.WHITE)
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
// cardView.setOnFocusChangeListener { v, hasFocus ->
// run {
// if (hasFocus) {
// if (v != null) {
// (v as ImageCardView).setInfoAreaBackgroundColor(context.resources.getColor(R.color.focus))
// }
// } else {
// if (v != null) {
// (v as ImageCardView).setInfoAreaBackgroundColor(context.resources.getColor(R.color.ic_launcher_background))
// }
// }
// }
// }
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
if (!epg.isNullOrEmpty()) {
cardView.contentText = epg.last().title
} else {
cardView.contentText = ""
}
}
override fun onUnbindViewHolder(viewHolder: ViewHolder) {
val cardView = viewHolder.view as ImageCardView
cardView.mainImage = null
}
companion object {
private const val TAG = "CardPresenter"
private const val CARD_WIDTH = 300
private const val CARD_HEIGHT = 101
}
}
@@ -1,11 +1,13 @@
package com.lizongying.mytv
import android.content.res.Resources
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.marginEnd
import androidx.core.view.marginTop
import androidx.fragment.app.Fragment
import com.lizongying.mytv.databinding.ChannelBinding
import com.lizongying.mytv.models.TVViewModel
@@ -15,7 +17,7 @@ class ChannelFragment : Fragment() {
private val binding get() = _binding!!
private val handler = Handler()
private val delay: Long = 3000
private val delay: Long = 5000
private var channel = 0
private var channelCount = 0
@@ -26,45 +28,30 @@ class ChannelFragment : Fragment() {
_binding = ChannelBinding.inflate(inflater, container, false)
_binding!!.root.visibility = View.GONE
val activity = requireActivity()
val application = activity.applicationContext as MyApplication
val displayMetrics = application.getDisplayMetrics()
val application = requireActivity().applicationContext as MyTvApplication
displayMetrics.density
binding.channel.layoutParams.width = application.px2Px(binding.channel.layoutParams.width)
binding.channel.layoutParams.height = application.px2Px(binding.channel.layoutParams.height)
var screenWidth = displayMetrics.widthPixels
var screenHeight = displayMetrics.heightPixels
if (screenHeight > screenWidth) {
screenWidth = displayMetrics.heightPixels
screenHeight = displayMetrics.widthPixels
}
val layoutParams = binding.channel.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = application.px2Px(binding.channel.marginTop)
layoutParams.marginEnd = application.px2Px(binding.channel.marginEnd)
binding.channel.layoutParams = layoutParams
val ratio = 16f / 9f
binding.content.textSize = application.px2PxFont(binding.content.textSize)
binding.time.textSize = application.px2PxFont(binding.time.textSize)
if (screenWidth / screenHeight > ratio) {
val x = ((screenWidth - screenHeight * ratio) / 2).toInt()
val originalLayoutParams =
binding.channelFragment.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.rightMargin += x
binding.channelFragment.layoutParams = originalLayoutParams
}
binding.main.layoutParams.width = application.shouldWidthPx()
binding.main.layoutParams.height = application.shouldHeightPx()
if (screenWidth / screenHeight < ratio) {
val y = ((screenHeight - screenWidth / ratio) / 2).toInt()
val originalLayoutParams =
binding.channelFragment.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.topMargin += y
binding.channelFragment.layoutParams = originalLayoutParams
}
(activity as MainActivity).fragmentReady("ChannelFragment")
(activity as MainActivity).fragmentReady(TAG)
return binding.root
}
fun show(tvViewModel: TVViewModel) {
handler.removeCallbacks(hideRunnable)
handler.removeCallbacks(playRunnable)
binding.channelContent.text = (tvViewModel.getTV().id.plus(1)).toString()
binding.content.text = (tvViewModel.getTV().id.plus(1)).toString()
view?.visibility = View.VISIBLE
handler.postDelayed(hideRunnable, delay)
}
@@ -74,13 +61,11 @@ class ChannelFragment : Fragment() {
return
}
channelCount++
Log.i(TAG, "channelCount ${channelCount}")
this.channel = "${this.channel}$channel".toInt()
Log.i(TAG, "this.channel ${this.channel}")
handler.removeCallbacks(hideRunnable)
handler.removeCallbacks(playRunnable)
if (channelCount < 2) {
binding.channelContent.text = "${this.channel}"
binding.content.text = "${this.channel}"
view?.visibility = View.VISIBLE
handler.postDelayed(playRunnable, delay)
} else {
@@ -102,20 +87,18 @@ class ChannelFragment : Fragment() {
}
private val hideRunnable = Runnable {
binding.channelContent.text = ""
binding.content.text = ""
view?.visibility = View.GONE
channel = 0
channelCount = 0
Log.i(TAG, "hideRunnable")
}
private val playRunnable = Runnable {
(activity as MainActivity).play(channel - 1)
binding.channelContent.text = ""
binding.content.text = ""
view?.visibility = View.GONE
channel = 0
channelCount = 0
Log.i(TAG, "playRunnable")
}
override fun onDestroyView() {
@@ -1,28 +1,49 @@
package com.lizongying.mytv
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.core.content.ContextCompat
import androidx.leanback.app.ErrorSupportFragment
import android.view.ViewGroup
import androidx.core.view.marginStart
import androidx.core.view.marginTop
import androidx.fragment.app.Fragment
import com.lizongying.mytv.databinding.ErrorBinding
class ErrorFragment : ErrorSupportFragment() {
class ErrorFragment : Fragment() {
private var _binding: ErrorBinding? = null
private val binding get() = _binding!!
internal fun setErrorContent(message: String) {
imageDrawable =
ContextCompat.getDrawable(context!!, androidx.leanback.R.drawable.lb_ic_sad_cloud)
this.message = message
setDefaultBackground(TRANSLUCENT)
backgroundDrawable = ContextCompat.getDrawable(
context!!,
R.color.black
)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = ErrorBinding.inflate(inflater, container, false)
buttonText = resources.getString(R.string.dismiss_error)
buttonClickListener = View.OnClickListener {
fragmentManager!!.beginTransaction().remove(this@ErrorFragment).commit()
}
val application = requireActivity().applicationContext as MyTvApplication
binding.logo.layoutParams.width = application.px2Px(binding.logo.layoutParams.width)
binding.logo.layoutParams.height = application.px2Px(binding.logo.layoutParams.height)
val layoutParams = binding.msg.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = application.px2Px(binding.msg.marginTop)
binding.msg.layoutParams = layoutParams
binding.msg.textSize = application.px2PxFont(binding.msg.textSize)
(activity as MainActivity).fragmentReady(TAG)
return binding.root
}
fun show(msg: String) {
binding.msg.text = msg
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
private const val TRANSLUCENT = false
private const val TAG = "ErrorFragment"
}
}
@@ -1,11 +1,16 @@
package com.lizongying.mytv
import android.content.res.Resources
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.marginBottom
import androidx.core.view.marginStart
import androidx.core.view.marginTop
import androidx.core.view.setPadding
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.lizongying.mytv.databinding.InfoBinding
@@ -16,31 +21,64 @@ class InfoFragment : Fragment() {
private val binding get() = _binding!!
private val handler = Handler()
private val delay: Long = 3000
private val delay: Long = 5000
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = InfoBinding.inflate(inflater, container, false)
val application = requireActivity().applicationContext as MyTvApplication
binding.info.layoutParams.width = application.px2Px(binding.info.layoutParams.width)
binding.info.layoutParams.height = application.px2Px(binding.info.layoutParams.height)
val layoutParams = binding.info.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.bottomMargin = application.px2Px(binding.info.marginBottom)
binding.info.layoutParams = layoutParams
binding.logo.layoutParams.width = application.px2Px(binding.logo.layoutParams.width)
binding.logo.setPadding(application.px2Px(binding.logo.paddingTop))
binding.main.layoutParams.width = application.px2Px(binding.main.layoutParams.width)
binding.main.setPadding(application.px2Px(binding.main.paddingTop))
val layoutParamsMain = binding.main.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsMain.marginStart = application.px2Px(binding.main.marginStart)
binding.main.layoutParams = layoutParamsMain
val layoutParamsDesc = binding.desc.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsDesc.topMargin = application.px2Px(binding.desc.marginTop)
binding.desc.layoutParams = layoutParamsDesc
binding.title.textSize = application.px2PxFont(binding.title.textSize)
binding.desc.textSize = application.px2PxFont(binding.desc.textSize)
binding.container.layoutParams.width = application.shouldWidthPx()
binding.container.layoutParams.height = application.shouldHeightPx()
_binding!!.root.visibility = View.GONE
(activity as MainActivity).fragmentReady("InfoFragment")
(activity as MainActivity).fragmentReady(TAG)
return binding.root
}
fun show(tvViewModel: TVViewModel) {
binding.textView.text = tvViewModel.getTV().title
if (_binding == null) {
return
}
binding.title.text = tvViewModel.getTV().title
Glide.with(this)
.load(tvViewModel.getTV().logo)
.into(binding.infoLogo)
.into(binding.logo)
Log.i(TAG, "${tvViewModel.getTV().title} ${tvViewModel.epg.value}")
val epg = tvViewModel.epg.value?.filter { it.beginTime < Utils.getDateTimestamp() }
if (!epg.isNullOrEmpty()) {
binding.infoDesc.text = epg.last().title
binding.desc.text = epg.last().title
} else {
binding.infoDesc.text = ""
binding.desc.text = ""
}
handler.removeCallbacks(removeRunnable)
@@ -0,0 +1,29 @@
package com.lizongying.mytv
import android.content.Context
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class ItemDecoration(context: Context) : RecyclerView.ItemDecoration() {
private var application: MyTvApplication
init {
application = context.applicationContext as MyTvApplication
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
val space = application.dp2Px(5)
outRect.top = space
outRect.bottom = space
outRect.left = space
outRect.right = space
}
}
@@ -0,0 +1,37 @@
package com.lizongying.mytv
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.lizongying.mytv.databinding.LoadingBinding
class LoadingFragment : Fragment() {
private var _binding: LoadingBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = LoadingBinding.inflate(inflater, container, false)
val application = requireActivity().applicationContext as MyTvApplication
binding.bar.layoutParams.width = application.px2Px(binding.bar.layoutParams.width)
binding.bar.layoutParams.height = application.px2Px(binding.bar.layoutParams.height)
(activity as MainActivity).fragmentReady(TAG)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
private const val TAG = "LoadingFragment"
}
}
@@ -1,8 +1,5 @@
package com.lizongying.mytv
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.os.Build
import android.os.Bundle
import android.os.Handler
@@ -11,13 +8,17 @@ import android.util.Log
import android.view.GestureDetector
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.View.SYSTEM_UI_FLAG_FULLSCREEN
import android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
import android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
import android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
import android.view.WindowManager
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import com.lizongying.mytv.models.TVViewModel
import com.lizongying.mytv.requests.Request
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
@@ -28,12 +29,14 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private var ready = 0
private val playerFragment = PlayerFragment()
private val errorFragment = ErrorFragment()
private val loadingFragment = LoadingFragment()
private val mainFragment = MainFragment()
private val infoFragment = InfoFragment()
private val channelFragment = ChannelFragment()
private var timeFragment = TimeFragment()
private val settingFragment = SettingFragment()
private val errorFragment = ErrorFragment()
private var doubleBackToExitPressedOnce = false
@@ -44,64 +47,81 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private val delayHideSetting: Long = 10000
init {
Utils.setRequestListener(this)
}
private fun syncTime() {
lifecycleScope.launch(Dispatchers.IO) {
val utilsJob = async(start = CoroutineStart.LAZY) { Utils.init() }
utilsJob.start()
// utilsJob.await()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Request.setRequestListener(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val lp = window.attributes
lp.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
window.setAttributes(lp)
}
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.decorView.systemUiVisibility = SYSTEM_UI_FLAG_HIDE_NAVIGATION
window.decorView.systemUiVisibility =
SYSTEM_UI_FLAG_FULLSCREEN or
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_HIDE_NAVIGATION or
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
// window.statusBarColor = Color.TRANSPARENT
// window.navigationBarColor = Color.TRANSPARENT
// }
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
val transaction = supportFragmentManager.beginTransaction()
.add(R.id.main_browse_fragment, playerFragment)
.add(R.id.main_browse_fragment, errorFragment)
// .add(R.id.main_browse_fragment, loadingFragment)
.add(R.id.main_browse_fragment, timeFragment)
.add(R.id.main_browse_fragment, infoFragment)
.add(R.id.main_browse_fragment, channelFragment)
.add(R.id.main_browse_fragment, mainFragment)
.hide(mainFragment)
.commit()
.hide(errorFragment)
if (!SP.time) {
transaction.hide(timeFragment)
} else {
transaction.show(timeFragment)
}
transaction.commitNow()
}
gestureDetector = GestureDetector(this, GestureListener())
errorFragment.buttonClickListener = View.OnClickListener {
supportFragmentManager.beginTransaction()
.remove(errorFragment)
.commit()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val connectivityManager =
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.registerDefaultNetworkCallback(object :
ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
if (this@MainActivity.isNetworkConnected) {
Log.i(TAG, "net isNetworkConnected")
ready++
}
}
})
} else {
Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
ready++
}
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// val connectivityManager =
// getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// connectivityManager.registerDefaultNetworkCallback(object :
// ConnectivityManager.NetworkCallback() {
// override fun onAvailable(network: Network) {
// super.onAvailable(network)
// Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
// if (this@MainActivity.isNetworkConnected) {
// Log.i(TAG, "net isNetworkConnected")
// ready++
// }
// }
// })
// } else {
// Log.i(TAG, "net ${Build.VERSION.SDK_INT}")
// ready++
// }
}
fun showInfoFragment(tvViewModel: TVViewModel) {
@@ -111,6 +131,52 @@ 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 showLoadingFragment() {
showFragment(loadingFragment)
}
fun hideLoadingFragment() {
hideFragment(loadingFragment)
}
fun showPlayerFragment() {
if (playerFragment.isVisible) {
return
}
supportFragmentManager.beginTransaction()
.show(playerFragment)
.commit()
}
fun hidePlayerFragment() {
if (!playerFragment.isVisible) {
return
}
supportFragmentManager.beginTransaction()
.hide(playerFragment)
.commit()
}
private fun showChannel(channel: String) {
if (!mainFragment.isHidden) {
return
@@ -127,7 +193,6 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
fun play(tvViewModel: TVViewModel) {
playerFragment.play(tvViewModel)
mainFragment.view?.requestFocus()
}
fun play(itemPosition: Int) {
@@ -142,18 +207,11 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
mainFragment.next()
}
private fun prevSource() {
// mainFragment.prevSource()
}
private fun nextSource() {
// mainFragment.nextSource()
}
fun switchMainFragment() {
val transaction = supportFragmentManager.beginTransaction()
if (mainFragment.isHidden) {
mainFragment.setPosition()
transaction.show(mainFragment)
mainActive()
} else {
@@ -171,12 +229,6 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
fun settingDelayHide() {
handler.removeCallbacks(hideSetting)
handler.postDelayed(hideSetting, delayHideSetting)
showTime()
}
fun settingHideNow() {
handler.removeCallbacks(hideSetting)
handler.postDelayed(hideSetting, 0)
}
fun settingNeverHide() {
@@ -204,27 +256,37 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
fun fragmentReady(tag: String) {
ready++
Log.i(TAG, "ready $tag $ready ")
if (ready == 6) {
if (ready == 7) {
mainFragment.fragmentReady()
showTime()
}
}
private fun showTime() {
Log.i(TAG, "showTime ${SP.time}")
if (SP.time) {
timeFragment.show()
showFragment(timeFragment)
} else {
timeFragment.hide()
hideFragment(timeFragment)
}
}
fun isPlaying() {
if (errorFragment.isVisible) {
supportFragmentManager.beginTransaction()
.remove(errorFragment)
.commit()
private fun showFragment(fragment: Fragment) {
if (!fragment.isHidden) {
return
}
supportFragmentManager.beginTransaction()
.show(fragment)
.commitNow()
}
private fun hideFragment(fragment: Fragment) {
if (fragment.isHidden) {
return
}
supportFragmentManager.beginTransaction()
.hide(fragment)
.commitNow()
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
@@ -237,6 +299,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
Log.i(TAG, "onSingleTapConfirmed")
switchMainFragment()
return true
}
@@ -284,22 +347,26 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
return
}
Log.i(TAG, "settingFragment ${settingFragment.isVisible}")
if (!settingFragment.isVisible) {
settingFragment.show(supportFragmentManager, "setting")
settingDelayHide()
} else {
handler.removeCallbacks(hideSetting)
settingFragment.dismiss()
if (settingFragment.isVisible) {
return
}
settingFragment.show(supportFragmentManager, TAG)
settingDelayHide()
}
private val hideSetting = Runnable {
if (settingFragment.isVisible) {
settingFragment.dismiss()
showTime()
mainFragment.changeMenu()
}
}
private fun hideSettingFragment() {
handler.postDelayed(hideSetting, 0)
}
private fun channelUp() {
if (mainFragment.isHidden) {
if (SP.channelReversal) {
@@ -339,6 +406,11 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
return
}
if (settingFragment.isVisible) {
hideSettingFragment()
return
}
if (doubleBackToExitPressedOnce) {
super.onBackPressed()
return
@@ -352,8 +424,14 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
}, 2000)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
Log.i(TAG, "keyCode $keyCode, event $event")
private fun active() {
if (settingFragment.isVisible) {
settingDelayHide()
}
}
fun onKey(keyCode: Int): Boolean {
Log.i(TAG, "keyCode $keyCode")
when (keyCode) {
KeyEvent.KEYCODE_0 -> {
showChannel("0")
@@ -441,6 +519,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
}
KeyEvent.KEYCODE_ENTER -> {
active()
switchMainFragment()
}
@@ -449,6 +528,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
}
KeyEvent.KEYCODE_DPAD_UP -> {
active()
channelUp()
}
@@ -457,6 +537,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
}
KeyEvent.KEYCODE_DPAD_DOWN -> {
active()
channelDown()
}
@@ -465,19 +546,28 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
}
KeyEvent.KEYCODE_DPAD_LEFT -> {
if (!mainFragment.isVisible && !settingFragment.isVisible) {
active()
if (mainFragment.isHidden && !settingFragment.isVisible) {
switchMainFragment()
return true
}
}
KeyEvent.KEYCODE_DPAD_RIGHT -> {
if (!mainFragment.isVisible && !settingFragment.isVisible) {
active()
if (mainFragment.isHidden && !settingFragment.isVisible) {
showSetting()
return true
}
}
}
return false
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (onKey(keyCode)) {
return true
}
return super.onKeyDown(keyCode, event)
}
@@ -485,13 +575,20 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
private fun getAppSignature() = this.appSignature
override fun onStart() {
Log.i(TAG, "onStart")
Log.i(TAG, "onStart MainActivity")
syncTime()
super.onStart()
}
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
}
override fun onResumeFragments() {
super.onResumeFragments()
mainFragment.changeMenu()
if (!mainFragment.isHidden) {
handler.postDelayed(hideMain, delayHideMain)
}
@@ -509,13 +606,7 @@ class MainActivity : FragmentActivity(), Request.RequestListener {
}
override fun onRequestFinished(message: String?) {
if (message != null && !errorFragment.isVisible) {
supportFragmentManager.beginTransaction()
.add(R.id.main_browse_fragment, errorFragment)
.commitNow()
errorFragment.setErrorContent(message)
}
fragmentReady("Request")
fragmentReady("request")
}
private companion object {
@@ -2,71 +2,68 @@ package com.lizongying.mytv
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.leanback.app.BrowseSupportFragment
import androidx.leanback.widget.ArrayObjectAdapter
import androidx.leanback.widget.HeaderItem
import androidx.leanback.widget.ListRow
import androidx.leanback.widget.ListRowPresenter
import androidx.leanback.widget.ListRowPresenter.SelectItemViewHolderTask
import androidx.leanback.widget.OnItemViewClickedListener
import androidx.leanback.widget.OnItemViewSelectedListener
import androidx.leanback.widget.Presenter
import androidx.leanback.widget.Row
import androidx.leanback.widget.RowPresenter
import androidx.core.view.marginBottom
import androidx.core.view.marginStart
import androidx.core.view.marginTop
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.lizongying.mytv.api.YSP
import com.lizongying.mytv.databinding.MenuBinding
import com.lizongying.mytv.databinding.RowBinding
import com.lizongying.mytv.models.ProgramType
import com.lizongying.mytv.models.TVList
import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel
import com.lizongying.mytv.requests.Request
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainFragment : BrowseSupportFragment() {
class MainFragment : Fragment(), CardAdapter.ItemListener {
private var itemPosition = 0
private var rowsAdapter: ArrayObjectAdapter? = null
private var rowList: MutableList<View> = mutableListOf()
private var _binding: MenuBinding? = null
private val binding get() = _binding!!
var tvListViewModel = TVListViewModel()
private var lastVideoUrl = ""
override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState)
headersState = HEADERS_DISABLED
private lateinit var application: MyTvApplication
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = MenuBinding.inflate(inflater, container, false)
application = requireActivity().applicationContext as MyTvApplication
binding.menu.layoutParams.width = application.shouldWidthPx()
binding.menu.layoutParams.height = application.shouldHeightPx()
binding.container.setOnClickListener {
hideSelf()
}
return binding.root
}
// override fun onCreateView(
// inflater: LayoutInflater,
// container: ViewGroup?,
// savedInstanceState: Bundle?
// ): View? {
// val rootView = super.onCreateView(inflater, container, savedInstanceState)
// rootView?.setOnClickListener {
// Log.i(TAG, "main on click")
// fragmentManager!!.beginTransaction().hide(this).commit()
// }
// mainFragment.view?.setOnClickListener {
// Log.i(TAG, "mainFragment on click")
// fragmentManager!!.beginTransaction().hide(this).commit()
// }
// getRowsSupportFragment().view?.setOnClickListener {
// Log.i(TAG, "getRowsSupportFragment on click")
// fragmentManager!!.beginTransaction().hide(this).commit()
// }
//
//
// return rootView
// }
override fun onStart() {
Log.i(TAG, "onStart")
super.onStart()
private fun hideSelf() {
requireActivity().supportFragmentManager.beginTransaction()
.hide(this)
.commit()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@@ -74,168 +71,232 @@ class MainFragment : BrowseSupportFragment() {
activity?.let { YSP.init(it) }
loadRows()
setupEventListeners()
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.errInfo.value != null
&& tvViewModel.getTV().id == itemPosition
) {
Toast.makeText(context, tvViewModel.errInfo.value, Toast.LENGTH_SHORT).show()
}
}
tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
// not first time && channel not change
if (tvViewModel.ready.value != null
&& tvViewModel.getTV().id == itemPosition
&& check(tvViewModel)
) {
Log.i(TAG, "ready ${tvViewModel.getTV().title}")
(activity as? MainActivity)?.play(tvViewModel)
}
}
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.change.value != null) {
val title = tvViewModel.getTV().title
Log.i(TAG, "switch $title")
if (tvViewModel.getTV().pid != "") {
Log.i(TAG, "request $title")
lifecycleScope.launch(Dispatchers.IO) {
tvViewModel.let { Request.fetchData(it) }
}
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
setSelectedPosition(
tvViewModel.getRowPosition(), true,
SelectItemViewHolderTask(tvViewModel.getItemPosition())
)
} else {
if (check(tvViewModel)) {
(activity as? MainActivity)?.play(tvViewModel)
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
setSelectedPosition(
tvViewModel.getRowPosition(), true,
SelectItemViewHolderTask(tvViewModel.getItemPosition())
)
}
}
}
}
}
(activity as MainActivity).fragmentReady("MainFragment")
}
fun toLastPosition() {
setSelectedPosition(
selectedPosition, false,
SelectItemViewHolderTask(tvListViewModel.maxNum[selectedPosition] - 1)
)
}
fun toFirstPosition() {
setSelectedPosition(
selectedPosition, false,
SelectItemViewHolderTask(0)
)
}
override fun startHeadersTransition(withHeaders: Boolean) {
}
private fun loadRows() {
rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
val cardPresenter = CardPresenter(context!!)
var idx: Long = 0
for ((k, v) in TVList.list) {
val listRowAdapter = ArrayObjectAdapter(cardPresenter)
for ((idx2, v1) in v.withIndex()) {
val tvViewModel = TVViewModel(v1)
tvViewModel.setRowPosition(idx.toInt())
tvViewModel.setItemPosition(idx2)
tvListViewModel.addTVViewModel(tvViewModel)
listRowAdapter.add(tvViewModel)
}
tvListViewModel.maxNum.add(v.size)
val header = HeaderItem(idx, k)
rowsAdapter!!.add(ListRow(header, listRowAdapter))
idx++
}
adapter = rowsAdapter
itemPosition = SP.itemPosition
if (itemPosition >= tvListViewModel.size()) {
itemPosition = 0
}
tvListViewModel.setItemPosition(itemPosition)
}
fun prevSource() {
view?.post {
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
if (tvViewModel != null) {
if (tvViewModel.videoUrl.value!!.size > 1) {
val videoIndex = tvViewModel.videoIndex.value?.minus(1)
if (videoIndex == -1) {
tvViewModel.setVideoIndex(tvViewModel.videoUrl.value!!.size - 1)
val content = binding.content
var idx: Long = 0
for ((k, v) in TVList.list) {
val itemBinding: RowBinding =
RowBinding.inflate(layoutInflater, content, false)
val tvListViewModelCurrent = TVListViewModel()
for ((idx2, v1) in v.withIndex()) {
val tvViewModel = TVViewModel(v1)
tvViewModel.setRowPosition(idx.toInt())
tvViewModel.setItemPosition(idx2)
tvListViewModelCurrent.addTVViewModel(tvViewModel)
tvListViewModel.addTVViewModel(tvViewModel)
}
tvListViewModel.maxNum.add(v.size)
val adapter =
CardAdapter(
itemBinding.items,
this,
tvListViewModelCurrent,
)
rowList.add(itemBinding.items)
adapter.setItemListener(this)
itemBinding.header.text = k
itemBinding.items.tag = idx.toInt()
itemBinding.items.adapter = adapter
itemBinding.items.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
(activity as MainActivity).mainActive()
}
tvViewModel.changed()
}
}
}
}
})
fun nextSource() {
view?.post {
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
if (tvViewModel != null) {
if (tvViewModel.videoUrl.value!!.size > 1) {
val videoIndex = tvViewModel.videoIndex.value?.plus(1)
if (videoIndex == tvViewModel.videoUrl.value!!.size) {
tvViewModel.setVideoIndex(0)
val itemDecoration = context?.let { ItemDecoration(it) }
if (itemDecoration != null) {
itemBinding.items.addItemDecoration(itemDecoration)
}
if (SP.grid) {
itemBinding.items.layoutManager =
GridLayoutManager(context, 6)
itemBinding.items.layoutParams.height =
application.dp2Px(110 * ((tvListViewModelCurrent.size() + 6 - 1) / 6) + 5)
} else {
itemBinding.items.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
}
val layoutParams = itemBinding.row.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = application.dp2Px(11)
itemBinding.row.layoutParams = layoutParams
itemBinding.row.setOnClickListener {
hideSelf()
}
val layoutParamsHeader =
itemBinding.header.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsHeader.topMargin = application.px2Px(itemBinding.header.marginTop)
layoutParamsHeader.bottomMargin = application.px2Px(itemBinding.header.marginBottom)
layoutParamsHeader.marginStart = application.px2Px(itemBinding.header.marginStart)
itemBinding.header.layoutParams = layoutParamsHeader
itemBinding.header.textSize = application.px2PxFont(itemBinding.header.textSize)
content.addView(itemBinding.row)
idx++
}
if (itemPosition >= tvListViewModel.size()) {
itemPosition = 0
}
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.errInfo.value != null
&& tvViewModel.getTV().id == itemPosition
) {
if (tvViewModel.errInfo.value == "") {
(activity as? MainActivity)?.showPlayerFragment()
(activity as? MainActivity)?.hideErrorFragment()
(activity as? MainActivity)?.hideLoadingFragment()
} else {
(activity as? MainActivity)?.hidePlayerFragment()
(activity as? MainActivity)?.hideLoadingFragment()
(activity as? MainActivity)?.showErrorFragment(tvViewModel.errInfo.value.toString())
}
}
tvViewModel.changed()
}
tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
// not first time && channel not change
if (tvViewModel.ready.value != null
&& tvViewModel.getTV().id == itemPosition
&& check(tvViewModel)
) {
Log.i(TAG, "ready ${tvViewModel.getTV().title}")
(activity as? MainActivity)?.play(tvViewModel)
}
}
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.change.value != null) {
val title = tvViewModel.getTV().title
Log.i(TAG, "switch $title")
if (tvViewModel.getTV().pid != "") {
Log.i(TAG, "request $title")
lifecycleScope.launch(Dispatchers.IO) {
tvViewModel.let { Request.fetchData(it) }
}
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
setPosition(
tvViewModel.getRowPosition(), tvViewModel.getItemPosition()
)
} else {
if (check(tvViewModel)) {
// TODO lastVideoUrl
(activity as? MainActivity)?.play(tvViewModel)
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
setPosition(
tvViewModel.getRowPosition(), tvViewModel.getItemPosition()
)
}
}
}
}
}
(activity as MainActivity).fragmentReady(TAG)
}
}
fun changeMenu() {
if (SP.grid) {
for (i in rowList) {
if (i is RecyclerView) {
i.layoutManager = GridLayoutManager(context, 6)
i.layoutParams.height =
application.dp2Px(110 * (((i.adapter as CardAdapter).getItemCount() + 6 - 1) / 6) + 5)
}
}
} else {
for (i in rowList) {
if (i is RecyclerView) {
i.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
i.layoutParams.height = application.dp2Px(110 + 5)
}
}
}
}
private fun setupEventListeners() {
onItemViewClickedListener = ItemViewClickedListener()
onItemViewSelectedListener = ItemViewSelectedListener()
}
override fun onKey(keyCode: Int): Boolean {
if (this.isHidden) {
when (keyCode) {
KeyEvent.KEYCODE_DPAD_UP -> {
(activity as MainActivity).onKey(keyCode)
return true
}
private inner class ItemViewClickedListener : OnItemViewClickedListener {
override fun onItemClicked(
itemViewHolder: Presenter.ViewHolder,
item: Any,
rowViewHolder: RowPresenter.ViewHolder,
row: Row
) {
if (item is TVViewModel) {
if (itemPosition != item.getTV().id) {
itemPosition = item.getTV().id
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
KeyEvent.KEYCODE_DPAD_DOWN -> {
(activity as MainActivity).onKey(keyCode)
return true
}
(activity as? MainActivity)?.switchMainFragment()
}
}
return false
}
private inner class ItemViewSelectedListener : OnItemViewSelectedListener {
override fun onItemSelected(
itemViewHolder: Presenter.ViewHolder?, item: Any?,
rowViewHolder: RowPresenter.ViewHolder, row: Row
) {
if (item is TVViewModel) {
tvListViewModel.setItemPositionCurrent(item.getTV().id)
(activity as MainActivity).mainActive()
override fun onItemHasFocus(tvViewModel: TVViewModel) {
val row = tvViewModel.getRowPosition()
for (i in rowList) {
if (i.tag as Int != row) {
((i as RecyclerView).adapter as CardAdapter).focusable = false
(i.adapter as CardAdapter).clear()
} else {
((i as RecyclerView).adapter as CardAdapter).focusable = true
}
}
(activity as MainActivity).mainActive()
}
override fun onItemClicked(tvViewModel: TVViewModel) {
if (this.isHidden) {
(activity as? MainActivity)?.switchMainFragment()
return
}
if (itemPosition != tvViewModel.getTV().id) {
itemPosition = tvViewModel.getTV().id
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed("menu")
Log.i(TAG, "onItemClicked ${tvViewModel.getTV().id}")
}
(activity as? MainActivity)?.switchMainFragment()
}
fun setPosition() {
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) ?: return
val rowPosition = tvViewModel.getRowPosition()
val itemPosition = tvViewModel.getItemPosition()
setPosition(rowPosition, itemPosition)
}
fun setPosition(rowPosition: Int, itemPosition: Int) {
rowList[rowPosition].post {
when (val layoutManager = (rowList[rowPosition] as RecyclerView).layoutManager) {
is GridLayoutManager -> {
layoutManager.findViewByPosition(
itemPosition
)?.requestFocus()
}
is LinearLayoutManager -> {
layoutManager.findViewByPosition(
itemPosition
)?.requestFocus()
}
}
}
}
@@ -257,7 +318,7 @@ class MainFragment : BrowseSupportFragment() {
}
fun fragmentReady() {
tvListViewModel.getTVViewModel(itemPosition)?.changed()
tvListViewModel.getTVViewModel(itemPosition)?.changed("init")
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
updateEPG(tvViewModel)
@@ -269,7 +330,7 @@ class MainFragment : BrowseSupportFragment() {
if (itemPosition > -1 && itemPosition < tvListViewModel.size()) {
this.itemPosition = itemPosition
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
tvListViewModel.getTVViewModel(itemPosition)?.changed("num")
} else {
Toast.makeText(context, "频道不存在", Toast.LENGTH_SHORT).show()
}
@@ -283,7 +344,7 @@ class MainFragment : BrowseSupportFragment() {
itemPosition = tvListViewModel.size() - 1
}
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
tvListViewModel.getTVViewModel(itemPosition)?.changed("prev")
}
}
@@ -294,7 +355,7 @@ class MainFragment : BrowseSupportFragment() {
itemPosition = 0
}
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
tvListViewModel.getTVViewModel(itemPosition)?.changed("next")
}
}
@@ -314,23 +375,51 @@ class MainFragment : BrowseSupportFragment() {
}
}
fun shouldHasFocus(tvModel: TVViewModel): Boolean {
return tvModel == tvListViewModel.getTVViewModel(itemPosition)
}
override fun onHiddenChanged(hidden: Boolean) {
Log.i(TAG, "onHiddenChanged $hidden")
super.onHiddenChanged(hidden)
if (!hidden) {
val tvModel = tvListViewModel.getTVViewModel(itemPosition)
val rowPosition = tvModel?.getRowPosition()
val itemPosition = tvModel?.getItemPosition()
Log.i(TAG, "toPosition $rowPosition $itemPosition")
for (i in rowList) {
if (i.tag as Int == rowPosition) {
((i as RecyclerView).adapter as CardAdapter).updateEPG()
(i.adapter as CardAdapter).focusable = true
(i.adapter as CardAdapter).toPosition(itemPosition!!)
break
}
}
} else {
view?.post {
for (i in rowList) {
((i as RecyclerView).adapter as CardAdapter).focusable = false
}
}
}
}
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
}
override fun onStop() {
Log.i(TAG, "onStop")
super.onStop()
SP.itemPosition = itemPosition
Log.i(TAG, "$POSITION $itemPosition saved")
}
override fun onDestroy() {
Log.i(TAG, "onDestroy")
super.onDestroy()
}
override fun onDestroyView() {
Log.i(TAG, "onDestroyView")
super.onDestroyView()
_binding = null
}
companion object {
private const val TAG = "MainFragment"
private const val POSITION = "position"
@@ -1,23 +0,0 @@
package com.lizongying.mytv
import android.app.Application
import android.content.Context
import android.util.DisplayMetrics
import android.view.WindowManager
class MyApplication : Application() {
private lateinit var displayMetrics: DisplayMetrics
override fun onCreate() {
super.onCreate()
displayMetrics = DisplayMetrics()
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
// windowManager.defaultDisplay.getRealMetrics(displayMetrics)
windowManager.defaultDisplay.getMetrics(displayMetrics)
}
fun getDisplayMetrics(): DisplayMetrics {
return displayMetrics
}
}
@@ -0,0 +1,117 @@
package com.lizongying.mytv
import android.app.Application
import android.content.Context
import android.content.res.Resources
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.multidex.MultiDex
class MyTvApplication : Application() {
private lateinit var displayMetrics: DisplayMetrics
private lateinit var realDisplayMetrics: DisplayMetrics
private lateinit var windowManager: WindowManager
private var width = 0
private var height = 0
private var shouldWidth = 0
private var shouldHeight = 0
private var deviation = 0
private var ratio = 1.0
private var density = 2.0f
private var scale = 1.0f
override fun onCreate() {
super.onCreate()
displayMetrics = DisplayMetrics()
realDisplayMetrics = DisplayMetrics()
windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay.getMetrics(displayMetrics)
windowManager.defaultDisplay.getRealMetrics(realDisplayMetrics)
if (realDisplayMetrics.heightPixels > realDisplayMetrics.widthPixels) {
width = realDisplayMetrics.heightPixels
height = realDisplayMetrics.widthPixels
} else {
width = realDisplayMetrics.widthPixels
height = realDisplayMetrics.heightPixels
}
density = displayMetrics.density
ratio = if ((width.toDouble() / height) < (16.0 / 9)) {
width * 2 / 1920.0 / density
} else {
height * 2 / 1080.0 / density
}
if ((width.toDouble() / height) < (16.0 / 9.0)) {
ratio = width * 2 / 1920.0 / density
shouldWidth = width
shouldHeight = (width * 9.0 / 16.0).toInt()
} else {
ratio = height * 2 / 1080.0 / density
shouldHeight = height
shouldWidth = (height * 16.0 / 9.0).toInt()
}
deviation = width - Resources.getSystem().displayMetrics.widthPixels
scale = Resources.getSystem().displayMetrics.scaledDensity
}
fun getDisplayMetrics(): DisplayMetrics {
return displayMetrics
}
fun getRealDisplayMetrics(): DisplayMetrics {
return realDisplayMetrics
}
fun shouldWidthPx(): Int {
return shouldWidth
}
fun shouldHeightPx(): Int {
return shouldHeight
}
fun getWidth(): Int {
return width
}
fun getHeight(): Int {
return height
}
fun getDeviation(): Int {
return deviation
}
fun getRatio(): Double {
return ratio
}
fun dp2Px(dp: Int): Int {
return (dp * ratio * density + 0.5f).toInt()
}
fun px2Px(px: Int): Int {
return (px * ratio + 0.5f).toInt()
}
fun px2PxFont(px: Float): Float {
return (px * ratio / scale).toFloat()
}
fun sp2Px(sp: Float): Float {
return (sp * ratio * scale).toFloat()
}
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(base)
}
}
@@ -78,21 +78,22 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback {
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
Log.e(TAG, "PlaybackException $error")
tvViewModel?.changed()
val err = "播放错误"
tvViewModel?.setErrInfo(err)
tvViewModel?.changed("retry")
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
if (isPlaying) {
(activity as MainActivity).isPlaying()
tvViewModel?.setErrInfo("")
}
}
})
}
})
(activity as MainActivity).fragmentReady("PlayerFragment")
(activity as MainActivity).fragmentReady(TAG)
return _binding!!.root
}
@@ -153,7 +154,7 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback {
}
companion object {
private const val TAG = "PlaybackVideoFragment"
private const val TAG = "PlayerFragment"
}
override fun surfaceCreated(holder: SurfaceHolder) {
@@ -1,10 +1,13 @@
package com.lizongying.mytv
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.core.view.marginEnd
import androidx.core.view.marginTop
import androidx.fragment.app.DialogFragment
import com.lizongying.mytv.databinding.SettingBinding
@@ -21,6 +24,11 @@ class SettingFragment : DialogFragment() {
dialog?.window?.apply {
addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
setAttributes(attributes)
}
}
}
@@ -71,6 +79,14 @@ class SettingFragment : DialogFragment() {
}
}
binding.switchGrid.run {
isChecked = SP.grid
setOnCheckedChangeListener { _, isChecked ->
SP.grid = isChecked
(activity as MainActivity).settingDelayHide()
}
}
updateManager = UpdateManager(context, this, context.appVersionCode)
binding.checkVersion.setOnClickListener(
OnClickListenerCheckVersion(
@@ -79,7 +95,67 @@ class SettingFragment : DialogFragment() {
)
)
binding.exit.setOnClickListener{
val application = requireActivity().applicationContext as MyTvApplication
binding.content.layoutParams.width =
application.px2Px(binding.content.layoutParams.width)
binding.content.setPadding(
application.px2Px(binding.content.paddingLeft),
application.px2Px(binding.content.paddingTop),
application.px2Px(binding.content.paddingRight),
application.px2Px(binding.content.paddingBottom)
)
binding.name.textSize = application.px2PxFont(binding.name.textSize)
binding.version.textSize = application.px2PxFont(binding.version.textSize)
val layoutParamsVersion = binding.version.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsVersion.topMargin = application.px2Px(binding.version.marginTop)
binding.version.layoutParams = layoutParamsVersion
binding.checkVersion.textSize = application.px2PxFont(binding.checkVersion.textSize)
val layoutParamsCheckVersion =
binding.checkVersion.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsCheckVersion.marginEnd = application.px2Px(binding.checkVersion.marginEnd)
binding.checkVersion.layoutParams = layoutParamsCheckVersion
binding.versionName.textSize = application.px2PxFont(binding.versionName.textSize)
binding.exit.textSize = application.px2PxFont(binding.exit.textSize)
binding.switchChannelReversal.textSize =
application.px2PxFont(binding.switchChannelReversal.textSize)
val layoutParamsChannelReversal =
binding.switchChannelReversal.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsChannelReversal.topMargin =
application.px2Px(binding.switchChannelReversal.marginTop)
binding.switchChannelReversal.layoutParams = layoutParamsChannelReversal
binding.switchChannelNum.textSize = application.px2PxFont(binding.switchChannelNum.textSize)
val layoutParamsChannelNum =
binding.switchChannelNum.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsChannelNum.topMargin = application.px2Px(binding.switchChannelNum.marginTop)
binding.switchChannelNum.layoutParams = layoutParamsChannelNum
binding.switchTime.textSize = application.px2PxFont(binding.switchTime.textSize)
val layoutParamsTime = binding.switchTime.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsTime.topMargin = application.px2Px(binding.switchTime.marginTop)
binding.switchTime.layoutParams = layoutParamsTime
binding.switchBootStartup.textSize =
application.px2PxFont(binding.switchBootStartup.textSize)
val layoutParamsBootStartup =
binding.switchBootStartup.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsBootStartup.topMargin = application.px2Px(binding.switchBootStartup.marginTop)
binding.switchBootStartup.layoutParams = layoutParamsBootStartup
binding.switchGrid.textSize = application.px2PxFont(binding.switchGrid.textSize)
val layoutParamsGrid = binding.switchGrid.layoutParams as ViewGroup.MarginLayoutParams
layoutParamsGrid.topMargin = application.px2Px(binding.switchGrid.marginTop)
binding.switchGrid.layoutParams = layoutParamsGrid
binding.appreciate.layoutParams.width =
application.px2Px(binding.appreciate.layoutParams.width)
binding.exit.setOnClickListener {
requireActivity().finishAffinity()
}
@@ -2,10 +2,11 @@ package com.lizongying.mytv
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.marginEnd
import androidx.core.view.marginTop
import androidx.fragment.app.Fragment
import com.lizongying.mytv.Utils.getDateFormat
import com.lizongying.mytv.databinding.TimeBinding
@@ -21,74 +22,57 @@ class TimeFragment : Fragment() {
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
Log.i(TAG, "onCreateView")
_binding = TimeBinding.inflate(inflater, container, false)
val activity = requireActivity()
val application = activity.applicationContext as MyApplication
val displayMetrics = application.getDisplayMetrics()
val application = requireActivity().applicationContext as MyTvApplication
displayMetrics.density
binding.time.layoutParams.width = application.px2Px(binding.time.layoutParams.width)
binding.time.layoutParams.height = application.px2Px(binding.time.layoutParams.height)
var screenWidth = displayMetrics.widthPixels
var screenHeight = displayMetrics.heightPixels
if (screenHeight > screenWidth) {
screenWidth = displayMetrics.heightPixels
screenHeight = displayMetrics.widthPixels
val layoutParams = binding.time.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = application.px2Px(binding.time.marginTop)
layoutParams.marginEnd = application.px2Px(binding.time.marginEnd)
binding.time.layoutParams = layoutParams
binding.content.textSize = application.px2PxFont(binding.content.textSize)
binding.channel.textSize = application.px2PxFont(binding.channel.textSize)
binding.main.layoutParams.width = application.shouldWidthPx()
binding.main.layoutParams.height = application.shouldHeightPx()
if (SP.time) {
handler.removeCallbacks(showRunnable)
handler.postDelayed(showRunnable, 0)
} else {
handler.removeCallbacks(showRunnable)
}
val ratio = 16f / 9f
if (screenWidth / screenHeight > ratio) {
val x = ((screenWidth - screenHeight * ratio) / 2).toInt()
val originalLayoutParams = binding.time.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.rightMargin += x
binding.time.layoutParams = originalLayoutParams
}
if (screenWidth / screenHeight < ratio) {
val y = ((screenHeight - screenWidth / ratio) / 2).toInt()
val originalLayoutParams = binding.time.layoutParams as ViewGroup.MarginLayoutParams
originalLayoutParams.topMargin += y
binding.time.layoutParams = originalLayoutParams
}
(activity as MainActivity).fragmentReady("TimeFragment")
(activity as MainActivity).fragmentReady(TAG)
return binding.root
}
override fun onResume() {
super.onResume()
if (view?.visibility == View.VISIBLE) {
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
if (!hidden) {
handler.removeCallbacks(showRunnable)
handler.postDelayed(showRunnable, 0)
} else {
handler.removeCallbacks(showRunnable)
}
}
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
}
@@ -18,7 +18,7 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.PermissionChecker
import androidx.core.content.PermissionChecker.checkSelfPermission
import com.lizongying.mytv.api.ReleaseV2
import com.lizongying.mytv.requests.MyRequest
import com.lizongying.mytv.requests.ReleaseRequest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -32,7 +32,7 @@ class UpdateManager(
) :
ConfirmationFragment.ConfirmationListener {
private var myRequest = MyRequest()
private var myRequest = ReleaseRequest()
private var release: ReleaseV2? = null
private var downloadReceiver: DownloadReceiver? = null
@@ -62,7 +62,7 @@ class UpdateManager(
private fun updateUI(text: String) {
val dialog = ConfirmationFragment(this@UpdateManager, text)
dialog.show(settingFragment.fragmentManager, TAG)
settingFragment.fragmentManager?.let { dialog.show(it, TAG) }
}
private fun haveStoragePermission(): Boolean {
@@ -5,6 +5,7 @@ import android.os.Build
import android.util.TypedValue
import com.google.gson.Gson
import com.lizongying.mytv.api.TimeResponse
import com.lizongying.mytv.requests.Request
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
@@ -15,6 +16,8 @@ import java.util.Locale
object Utils {
private var between: Long = 0
private var listener: Request.RequestListener? = null
fun getDateFormat(format: String): String {
return SimpleDateFormat(
format,
@@ -38,6 +41,10 @@ object Utils {
println("Failed to retrieve timestamp from server: ${e.message}")
}
between = System.currentTimeMillis() - currentTimeMillis
withContext(Dispatchers.Main) {
listener?.onRequestFinished(null)
}
}
/**
@@ -72,6 +79,8 @@ object Utils {
}
fun dpToPx(dp: Int): Int {
// val density = Resources.getSystem().displayMetrics.density
// return (dp * density + 0.5f).toInt()
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), Resources.getSystem().displayMetrics
).toInt()
@@ -88,4 +97,8 @@ object Utils {
}
fun isTmallDevice() = Build.MANUFACTURER.equals("Tmall", ignoreCase = true)
fun setRequestListener(listener: Request.RequestListener) {
this.listener = listener
}
}
@@ -1,4 +1,4 @@
package com.lizongying.mytv
package com.lizongying.mytv.api
import android.content.Context
@@ -7,6 +7,6 @@ import retrofit2.http.Path
interface ReleaseService {
@GET("my-tv/v2/release/{name}")
fun getRelease(
@Path("name") date: String = "1",
@Path("name") date: String = "2",
): Call<ReleaseV2>
}
@@ -1,7 +1,6 @@
package com.lizongying.mytv.api
import android.content.Context
import com.lizongying.mytv.Encryptor
import com.lizongying.mytv.SP
import com.lizongying.mytv.Utils.getDateTimestamp
import com.lizongying.mytv.models.TVViewModel
@@ -1,4 +1,4 @@
package com.lizongying.mytv
package com.lizongying.mytv.models
import com.lizongying.mytv.models.ProgramType
import java.io.Serializable
@@ -15,18 +15,21 @@ data class TV(
var programType: ProgramType,
var needToken: Boolean = false,
var mustToken: Boolean = false,
) : Serializable {
) : Serializable {
override fun toString(): String {
return "TV{" +
"id=" + id +
", title='" + title + '\'' +
", alias='" + alias + '\'' +
", videoUrl='" + videoUrl + '\'' +
", channel='" + channel + '\'' +
", logo='" + logo + '\'' +
", pid='" + pid + '\'' +
", sid='" + sid + '\'' +
", programType='" + programType + '\'' +
", needToken='" + needToken + '\'' +
", mustToken='" + mustToken + '\'' +
'}'
}
}
@@ -1,6 +1,6 @@
package com.lizongying.mytv
package com.lizongying.mytv.models
import com.lizongying.mytv.models.ProgramType
import com.lizongying.mytv.R
object TVList {
val list: Map<String, List<TV>> by lazy {
@@ -900,6 +900,7 @@ object TVList {
v.forEach { v1 ->
if (!v1.mustToken) {
v1.id = id
v1.needToken = false
id++
group.add(v1)
}
@@ -3,6 +3,7 @@ package com.lizongying.mytv.models
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.lizongying.mytv.SP
class TVListViewModel : ViewModel() {
@@ -16,10 +17,6 @@ class TVListViewModel : ViewModel() {
val itemPosition: LiveData<Int>
get() = _itemPosition
private val _itemPositionCurrent = MutableLiveData<Int>()
val itemPositionCurrent: LiveData<Int>
get() = _itemPositionCurrent
fun addTVViewModel(tvViewModel: TVViewModel) {
if (_tvListViewModel.value == null) {
_tvListViewModel.value = mutableListOf(tvViewModel)
@@ -32,23 +29,16 @@ class TVListViewModel : ViewModel() {
return _tvListViewModel.value?.get(id)
}
fun getTVViewModelCurrent(): TVViewModel? {
return _itemPositionCurrent.value?.let { _tvListViewModel.value?.get(it) }
}
fun setItemPosition(position: Int) {
_itemPosition.value = position
_itemPositionCurrent.value = position
}
fun setItemPositionCurrent(position: Int) {
_itemPositionCurrent.value = position
SP.itemPosition = position
}
fun size(): Int {
if (_tvListViewModel.value == null) {
return 0
}
return _tvListViewModel.value!!.size
}
}
@@ -1,10 +1,8 @@
package com.lizongying.mytv.models
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.lizongying.mytv.TV
import com.lizongying.mytv.api.FEPG
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.programModel.Program
import com.tencent.videolite.android.datamodel.cctvjce.TVProgram
@@ -18,6 +16,8 @@ class TVViewModel(private var tv: TV) : ViewModel() {
var retryTimes = 0
var retryMaxTimes = 8
var authYSPRetryTimes = 0
var authYSPRetryMaxTimes = 3
var tokenYSPRetryTimes = 0
var tokenYSPRetryMaxTimes = 0
var tokenFHRetryTimes = 0
@@ -41,8 +41,8 @@ class TVViewModel(private var tv: TV) : ViewModel() {
val videoIndex: LiveData<Int>
get() = _videoIndex
private val _change = MutableLiveData<Boolean>()
val change: LiveData<Boolean>
private val _change = MutableLiveData<String>()
val change: LiveData<String>
get() = _change
private val _ready = MutableLiveData<Boolean>()
@@ -56,36 +56,27 @@ class TVViewModel(private var tv: TV) : ViewModel() {
if (_videoUrl.value!!.last().contains("cctv.cn")) {
tv.videoUrl = tv.videoUrl.subList(0, tv.videoUrl.lastIndex) + listOf(url)
} else {
tv.videoUrl = tv.videoUrl + listOf(url)
tv.videoUrl += listOf(url)
}
} else {
tv.videoUrl = tv.videoUrl + listOf(url)
tv.videoUrl += listOf(url)
}
_videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoUrl.lastIndex
}
fun firstSource() {
if (_videoUrl.value!!.isNotEmpty()) {
setVideoIndex(0)
allReady()
} else {
Log.e(TAG, "no first")
}
}
fun changed() {
_change.value = true
fun changed(from: String) {
retryTimes = 0
authYSPRetryTimes = 0
tokenYSPRetryTimes = 0
tokenFHRetryTimes = 0
_change.value = from
}
fun allReady() {
_ready.value = true
}
fun setVideoIndex(videoIndex: Int) {
_videoIndex.value = videoIndex
}
init {
_videoUrl.value = tv.videoUrl
_videoIndex.value = tv.videoUrl.lastIndex
@@ -10,7 +10,7 @@ import retrofit2.Response
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class MyRequest {
class ReleaseRequest {
private var releaseService = ApiClient().releaseService
suspend fun getRelease(): ReleaseV2? {
@@ -39,6 +39,6 @@ class MyRequest {
}
companion object {
private const val TAG = "MyRequest"
private const val TAG = "ReleaseRequest"
}
}
@@ -1,11 +1,10 @@
package com.lizongying.mytv
package com.lizongying.mytv.requests
import android.os.Handler
import android.os.Looper
import android.util.Base64
import android.util.Log
import com.google.gson.Gson
import java.net.URLDecoder
import com.lizongying.mytv.Utils
import com.lizongying.mytv.Utils.getDateFormat
import com.lizongying.mytv.api.ApiClient
import com.lizongying.mytv.api.Auth
@@ -25,6 +24,7 @@ import com.lizongying.mytv.api.YSPBtraceService
import com.lizongying.mytv.api.YSPJceService
import com.lizongying.mytv.api.YSPProtoService
import com.lizongying.mytv.api.YSPTokenService
import com.lizongying.mytv.models.TVList
import com.lizongying.mytv.models.TVViewModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.oms.common.proto.pageModel
import com.lizongying.mytv.proto.Ysp.cn.yangshipin.omstv.common.proto.epgProgramModel
@@ -54,7 +54,7 @@ object Request {
private var token = ""
private var tokenFH = ""
private var needAuth = false
private var needAuth = true
private var needToken = false
private val handler = Handler(Looper.getMainLooper())
@@ -118,11 +118,19 @@ object Request {
} else {
if (!tvModel.getTV().mustToken) {
fetchAuth(tvModel, cookie)
} else {
val err = "错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
} else {
fetchAuth(tvModel, cookie)
}
} else {
val err = "认证结果错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
} else {
@@ -137,11 +145,19 @@ object Request {
} else {
if (!tvModel.getTV().mustToken) {
fetchAuth(tvModel, cookie)
} else {
val err = "错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
} else {
fetchAuth(tvModel, cookie)
}
} else {
val err = "认证状态错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
}
@@ -158,11 +174,19 @@ object Request {
} else {
if (!tvModel.getTV().mustToken) {
fetchAuth(tvModel, cookie)
} else {
val err = "错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
} else {
fetchAuth(tvModel, cookie)
}
} else {
val err = "认证请求错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
})
@@ -171,7 +195,7 @@ object Request {
private fun fetchVideo(tvModel: TVViewModel, cookie: String) {
cancelCall()
if (::btraceRunnable.isInitialized) {
if (Request::btraceRunnable.isInitialized) {
handler.removeCallbacks(btraceRunnable)
}
@@ -223,24 +247,42 @@ object Request {
if (needToken && tvModel.tokenYSPRetryTimes < tvModel.tokenYSPRetryMaxTimes) {
tvModel.tokenYSPRetryTimes++
tvModel.needGetToken = true
fetchVideo(tvModel)
// fetchAuth(tvModel)
if (needAuth) {
fetchAuth(tvModel)
} else {
fetchVideo(tvModel)
}
} else {
if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie)
if (needAuth) {
fetchAuth(tvModel, cookie)
} else {
fetchVideo(tvModel, cookie)
}
} else {
val err = "错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
} else {
fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie)
if (needAuth) {
fetchAuth(tvModel, cookie)
} else {
fetchVideo(tvModel, cookie)
}
}
} else {
val err = "结果错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
} else {
if (liveInfo?.data?.errinfo != null && liveInfo.data.errinfo == "应版权方要求,暂停提供直播信号,请点击观看其他精彩节目") {
Log.e(TAG, "$title error ${liveInfo.data.errinfo}")
tvModel.setErrInfo(liveInfo.data.errinfo)
val err = "版权错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
} else {
Log.e(TAG, "$title url error $request $liveInfo")
if (tvModel.retryTimes < tvModel.retryMaxTimes) {
@@ -249,18 +291,35 @@ object Request {
if (needToken && tvModel.tokenYSPRetryTimes < tvModel.tokenYSPRetryMaxTimes) {
tvModel.tokenYSPRetryTimes++
tvModel.needGetToken = true
fetchVideo(tvModel)
// fetchAuth(tvModel)
if (needAuth) {
fetchAuth(tvModel)
} else {
fetchVideo(tvModel)
}
} else {
if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie)
if (needAuth) {
fetchAuth(tvModel, cookie)
} else {
fetchVideo(tvModel, cookie)
}
} else {
val err = "错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
} else {
fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie)
if (needAuth) {
fetchAuth(tvModel, cookie)
} else {
fetchVideo(tvModel, cookie)
}
}
} else {
val err = "其他错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
}
@@ -272,18 +331,35 @@ object Request {
if (needToken && tvModel.tokenYSPRetryTimes < tvModel.tokenYSPRetryMaxTimes) {
tvModel.tokenYSPRetryTimes++
tvModel.needGetToken = true
fetchVideo(tvModel)
// fetchAuth(tvModel)
if (needAuth) {
fetchAuth(tvModel, cookie)
} else {
fetchVideo(tvModel, cookie)
}
} else {
if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie)
if (needAuth) {
fetchAuth(tvModel, cookie)
} else {
fetchVideo(tvModel, cookie)
}
} else {
val err = "错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
} else {
fetchVideo(tvModel, cookie)
// fetchAuth(tvModel, cookie)
if (needAuth) {
fetchAuth(tvModel, cookie)
} else {
fetchVideo(tvModel, cookie)
}
}
} else {
val err = "状态错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
}
@@ -300,11 +376,17 @@ object Request {
} else {
if (!tvModel.getTV().mustToken) {
fetchVideo(tvModel, cookie)
} else {
// TODO
}
}
} else {
fetchVideo(tvModel, cookie)
}
} else {
val err = "请求错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
})
@@ -340,6 +422,8 @@ object Request {
val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
fetchAuth(tvModel, cookie)
} else {
// TODO
}
}
}
@@ -355,6 +439,8 @@ object Request {
val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
fetchAuth(tvModel, cookie)
} else {
// TODO
}
}
}
@@ -397,6 +483,8 @@ object Request {
val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
fetchVideo(tvModel, cookie)
} else {
// TODO
}
}
}
@@ -412,6 +500,8 @@ object Request {
val cookie =
"versionName=99.99.99; versionCode=999999; vplatform=109; platformVersion=Chrome; deviceModel=120; appid=1400421205; yspappid=519748109"
fetchVideo(tvModel, cookie)
} else {
// TODO
}
}
}
@@ -447,6 +537,10 @@ object Request {
if (tvModel.tokenFHRetryTimes < tvModel.tokenFHRetryMaxTimes) {
tvModel.tokenFHRetryTimes++
fetchFAuth(tvModel)
} else {
val err = "状态错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
}
@@ -456,6 +550,10 @@ object Request {
if (tvModel.tokenFHRetryTimes < tvModel.tokenFHRetryMaxTimes) {
tvModel.tokenFHRetryTimes++
fetchFAuth(tvModel)
} else {
val err = "请求错误"
Log.e(TAG, "$title $err")
tvModel.setErrInfo(err)
}
}
})
@@ -590,7 +688,7 @@ object Request {
val randomNumber = Random.nextDouble()
val url = tvModel.getTV().videoUrl.first()
var r = KvcollectRequest(
val r = KvcollectRequest(
guid = guid,
prog = sid,
viewid = sid,
@@ -605,27 +703,10 @@ object Request {
_dc = randomNumber,
)
var e =
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}"
println(e)
r.signature = YSP.getAuthSignature(e)
println(r.signature)
// val ttt = """{"BossId":2727,"Pwd":1424084450,"prog":"2022576801","playno":"BEoZw3cpZh","guid":"luccvoyr_58va8i3cqw6","hh_ua":"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","cdn":"waibao","sdtfrom":"ysp_pc_01","prd":60000,"platform":"5910204","errcode":"","durl":"https://outlivecloud-cdn.ysp.cctv.cn/906028A94D533483FE8BAC5CF4DB6CC70753E25A93EF3EC50988538DEDBF10161D1EA8F67C916EE41BA9D891D7893C69500B3F1A37CA814D2EE19341197360196CD9CCF327949104097C904643D493EB5A9B588EC28A2FAC1C8E30F8EE330DB2364B0DEC0124F6990A83773FF283489A/2022576803.m3u8?from=player&svrtime=1713365896&pid=600001859&cdn=5401&revoi=6E400EC7436C79D49BA06E61333D31FB8915F3484D9426FE4CCC4DE01CB8FD1DF473A8F416EF1107B669B0A9447F913272789A827EDD3DC0FE88534D9B59A35669678946589AA4D246F8F248F7C33AB08CE72FA0A73FE7F1F9FD2091D30176895CB8BD44A5B85FAD9579C700CB2AFD8D0939574B2A3EFF4C1B237DFF3E0CD3E194662F9FDEE48D1ACD7BDFAB01868B220743021C40E0EA5514ECCF396B509DA1&app_id=519748109&guid=luccvoyr_58va8i3cqw6&ysign=bb79232986c5429600d76cfedb1d5978&ytime=1713365896&ytype=1","firstreport":1,"sUrl":"https://www.yangshipin.cn/#/tv/home?pid=600001859","sRef":"","fplayerver":"100","livepid":"600001859","viewid":"2022576801","seq":0,"cmd":263,"login_type":"","geturltime":6,"downspeed":1.75,"hc_openid":"","open_id":"","defn":"fhd","fact1":"ysp_pc_live_b","openid":"","_dc":0.7140416315131102,"live_type":"","ftime":"2024-04-17 22:58:31","url":"https://outlivecloud-cdn.ysp.cctv.cn/906028A94D533483FE8BAC5CF4DB6CC70753E25A93EF3EC50988538DEDBF10161D1EA8F67C916EE41BA9D891D7893C69500B3F1A37CA814D2EE19341197360196CD9CCF327949104097C904643D493EB5A9B588EC28A2FAC1C8E30F8EE330DB2364B0DEC0124F6990A83773FF283489A/2022576803.m3u8?from=player&svrtime=1713365896&pid=600001859&cdn=5401&revoi=6E400EC7436C79D49BA06E61333D31FB8915F3484D9426FE4CCC4DE01CB8FD1DF473A8F416EF1107B669B0A9447F913272789A827EDD3DC0FE88534D9B59A35669678946589AA4D246F8F248F7C33AB08CE72FA0A73FE7F1F9FD2091D30176895CB8BD44A5B85FAD9579C700CB2AFD8D0939574B2A3EFF4C1B237DFF3E0CD3E194662F9FDEE48D1ACD7BDFAB01868B220743021C40E0EA5514ECCF396B509DA1&app_id=519748109&guid=luccvoyr_58va8i3cqw6&ysign=bb79232986c5429600d76cfedb1d5978&ytime=1713365896&ytype=1","rand_str":"oKkkZs8lON"}"""
// val gson = Gson()
// r = gson.fromJson(ttt, KvcollectRequest::class.java)
//
// 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}"
// println(e)
// e = "BossId=2727&Pwd=1424084450&_dc=0.7140416315131102&cdn=waibao&cmd=263&defn=fhd&downspeed=1.75&durl=https://outlivecloud-cdn.ysp.cctv.cn/906028A94D533483FE8BAC5CF4DB6CC70753E25A93EF3EC50988538DEDBF10161D1EA8F67C916EE41BA9D891D7893C69500B3F1A37CA814D2EE19341197360196CD9CCF327949104097C904643D493EB5A9B588EC28A2FAC1C8E30F8EE330DB2364B0DEC0124F6990A83773FF283489A/2022576803.m3u8?from=player&svrtime=1713365896&pid=600001859&cdn=5401&revoi=6E400EC7436C79D49BA06E61333D31FB8915F3484D9426FE4CCC4DE01CB8FD1DF473A8F416EF1107B669B0A9447F913272789A827EDD3DC0FE88534D9B59A35669678946589AA4D246F8F248F7C33AB08CE72FA0A73FE7F1F9FD2091D30176895CB8BD44A5B85FAD9579C700CB2AFD8D0939574B2A3EFF4C1B237DFF3E0CD3E194662F9FDEE48D1ACD7BDFAB01868B220743021C40E0EA5514ECCF396B509DA1&app_id=519748109&guid=luccvoyr_58va8i3cqw6&ysign=bb79232986c5429600d76cfedb1d5978&ytime=1713365896&ytype=1&errcode=&fact1=ysp_pc_live_b&firstreport=1&fplayerver=100&ftime=2024-04-17 22:58:31&geturltime=6&guid=luccvoyr_58va8i3cqw6&hc_openid=&hh_ua=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&live_type=&livepid=600001859&login_type=&open_id=&openid=&platform=5910204&playno=BEoZw3cpZh&prd=60000&prog=2022576801&rand_str=oKkkZs8lON&sRef=&sUrl=https://www.yangshipin.cn/#/tv/home?pid=600001859&sdtfrom=ysp_pc_01&seq=0&url=https://outlivecloud-cdn.ysp.cctv.cn/906028A94D533483FE8BAC5CF4DB6CC70753E25A93EF3EC50988538DEDBF10161D1EA8F67C916EE41BA9D891D7893C69500B3F1A37CA814D2EE19341197360196CD9CCF327949104097C904643D493EB5A9B588EC28A2FAC1C8E30F8EE330DB2364B0DEC0124F6990A83773FF283489A/2022576803.m3u8?from=player&svrtime=1713365896&pid=600001859&cdn=5401&revoi=6E400EC7436C79D49BA06E61333D31FB8915F3484D9426FE4CCC4DE01CB8FD1DF473A8F416EF1107B669B0A9447F913272789A827EDD3DC0FE88534D9B59A35669678946589AA4D246F8F248F7C33AB08CE72FA0A73FE7F1F9FD2091D30176895CB8BD44A5B85FAD9579C700CB2AFD8D0939574B2A3EFF4C1B237DFF3E0CD3E194662F9FDEE48D1ACD7BDFAB01868B220743021C40E0EA5514ECCF396B509DA1&app_id=519748109&guid=luccvoyr_58va8i3cqw6&ysign=bb79232986c5429600d76cfedb1d5978&ytime=1713365896&ytype=1&viewid=2022576801"
// println(e)
// r.signature = YSP.getAuthSignature(e)
// println(r.signature)
callBtracePage = yspBtraceService2.kvcollect2(
r
)
callBtracePage = yspBtraceService2.kvcollect2(r)
callBtracePage?.enqueue(object : Callback<Void> {
override fun onResponse(call: Call<Void>, response: Response<Void>) {
if (response.isSuccessful) {
@@ -834,6 +915,6 @@ object Request {
}
fun setRequestListener(listener: RequestListener) {
this.listener = listener
Request.listener = listener
}
}
@@ -1,4 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FF263238" />
<corners android:radius="4dp" /> <!-- Adjust the radius as needed -->
</shape>
@@ -0,0 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/blur" />
<corners
android:bottomLeftRadius="4dp"
android:bottomRightRadius="4dp" />
</shape>
@@ -0,0 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/blur" />
<corners
android:topLeftRadius="4dp"
android:bottomLeftRadius="4dp" />
</shape>
@@ -0,0 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/blur" />
<corners
android:topRightRadius="4dp"
android:bottomRightRadius="4dp" />
</shape>
@@ -0,0 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/focus" />
<corners
android:bottomLeftRadius="4dp"
android:bottomRightRadius="4dp" />
</shape>
@@ -0,0 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners
android:topLeftRadius="4dp"
android:bottomLeftRadius="4dp" />
</shape>
@@ -0,0 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners
android:topRightRadius="4dp"
android:bottomRightRadius="4dp" />
</shape>
@@ -1,4 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="4dp" /> <!-- Adjust the radius as needed -->
<corners
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
</shape>
+23
View File
@@ -0,0 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="50"
android:viewportHeight="50"
android:width="62.5dp"
android:height="62.5dp">
<group
android:scaleX="0.1"
android:scaleY="-0.1"
android:translateY="50">
<path
android:pathData="M205 377c-27 -27 -40 -33 -65 -29 -30 4 -70 -19 -70 -40 0 -5 -16 -24 -35 -42 -38 -36 -44 -66 -23 -111 22 -49 51 -55 248 -55l182 0 29 29c46 46 37 106 -20 136 -20 10 -31 25 -35 49 -4 22 -20 46 -42 65 -29 26 -43 31 -85 31 -44 0 -55 -4 -84 -33zm138 2c29 -13 57 -58 57 -93 0 -17 5 -26 15 -26 24 0 65 -46 65 -71 0 -13 -10 -33 -22 -46 -21 -22 -26 -23 -205 -23 -182 0 -184 0 -208 25 -33 32 -32 70 3 105 16 15 36 40 45 54 18 27 54 35 74 15 8 -8 16 -2 29 19 9 17 27 35 38 41 28 13 80 13 109 0z"
android:fillColor="#FFEEEEEE" />
<path
android:pathData="M174 269c-10 -17 13 -36 27 -22 12 12 4 33 -11 33 -5 0 -12 -5 -16 -11z"
android:fillColor="#FFEEEEEE" />
<path
android:pathData="M294 269c-10 -17 13 -36 27 -22 12 12 4 33 -11 33 -5 0 -12 -5 -16 -11z"
android:fillColor="#FFEEEEEE" />
<path
android:pathData="M194 196c-32 -14 -50 -33 -41 -42 3 -3 20 4 38 15 43 26 75 26 118 0 34 -20 49 -20 36 1 -10 16 -69 40 -97 40 -13 -1 -37 -7 -54 -14z"
android:fillColor="#FFEEEEEE" />
</group>
</vector>
+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>
+42 -17
View File
@@ -1,19 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/channel_fragment"
android:layout_width="90dp"
android:layout_height="60dp"
android:layout_gravity="end|top"
android:layout_marginTop="20dp"
android:layout_marginEnd="170dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<FrameLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/channel_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="0dp"
android:textColor="#FFEEEEEE"
android:textStyle="bold"
android:textSize="40sp" />
</FrameLayout>
<RelativeLayout
android:id="@+id/channel"
android:layout_width="90dp"
android:layout_height="60dp"
android:layout_gravity="end|top"
android:layout_marginTop="15dp"
android:layout_marginEnd="170dp"
android:gravity="end|bottom" >
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="40sp"
tools:text="99"
android:layout_alignBaseline="@+id/time"/>
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="32sp"
tools:text="12:34" />
</RelativeLayout>
</FrameLayout>
</LinearLayout>
+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"
android:background="@color/black" >
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/logo"
android:src="@drawable/sad_cloud"
android:layout_width="50dp"
android:layout_height="50dp" />
<TextView
android:id="@+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="#FFEEEEEE"
android:text="Message"
android:textSize="20sp" />
</LinearLayout>
</FrameLayout>
+55 -48
View File
@@ -1,53 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/info_fragment"
android:layout_width="320dp"
android:layout_height="80dp"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="20dp"
android:background="@drawable/rounded_background">
<ImageView
android:id="@+id/info_logo"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:padding="10dp" />
<LinearLayout
android:layout_width="216dp"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="100dp"
android:background="#FF263238"
android:gravity="start|center_vertical"
android:orientation="vertical"
android:padding="7dp">
>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="0dp"
android:gravity="start"
android:textColor="#FFEEEEEE"
android:textSize="22sp" />
<LinearLayout
android:id="@+id/info"
android:layout_width="400dp"
android:layout_height="80dp"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="20dp"
android:orientation="horizontal">
<TextView
android:id="@+id/info_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="8dp"
android:gravity="start"
android:singleLine="true"
android:textColor="#B3EEEEEE"
android:textSize="16sp" />
</LinearLayout>
<ImageView
android:id="@+id/logo"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="@drawable/rounded_white_left"
android:padding="10dp" />
<View
android:layout_width="8dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/rounded_background2" />
</FrameLayout>
<LinearLayout
android:id="@+id/main"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_marginStart="0dp"
android:background="@drawable/rounded_dark_right"
android:gravity="start|center_vertical"
android:orientation="vertical"
android:padding="7dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:gravity="start"
android:textColor="#FFEEEEEE"
android:textSize="20sp" />
<TextView
android:id="@+id/desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="8dp"
android:gravity="start"
android:singleLine="true"
android:textColor="#B3EEEEEE"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</LinearLayout>
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/loading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
>
<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" >
<ProgressBar
android:id="@+id/bar"
android:layout_width="50dp"
android:layout_height="50dp"
android:indeterminateTint="@color/white"/>
</LinearLayout>
</FrameLayout>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:clickable="true"
android:focusable="false"
android:gravity="center"
>
<FrameLayout
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ScrollView
android:id="@+id/scroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<LinearLayout
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</FrameLayout>
</LinearLayout>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/player_fragment"
android:id="@+id/player"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@android:color/transparent"
android:clickable="true"
android:focusable="false"
>
<TextView
android:id="@+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="0dp"
android:layout_marginStart="20dp"
android:layout_marginBottom="5dp"
android:gravity="start"
android:textColor="#FFEEEEEE"
android:textSize="22sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/items"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
+24 -8
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog"
android:id="@+id/setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
@@ -8,15 +8,17 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/content"
android:layout_width="300dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="20dp"
android:paddingLeft="7dp"
android:paddingRight="7dp"
android:background="#FF263238"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:background="@drawable/rounded_dark_left"
>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
@@ -27,9 +29,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/"
android:textSize="14sp"
/>
<LinearLayout
android:layout_width="300dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
@@ -37,7 +40,7 @@
android:id="@+id/check_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:textSize="14sp"
android:text="@string/check_version"
/>
<ProgressBar
@@ -48,6 +51,7 @@
<TextView
android:id="@+id/version_name"
android:text=""
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
@@ -55,38 +59,50 @@
android:id="@+id/exit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:textSize="14sp"
android:text="@string/exit"
/>
<Switch
android:id="@+id/switch_channel_reversal"
android:text="@string/title_channel_reversal"
android:textSize="14sp"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Switch
android:id="@+id/switch_channel_num"
android:text="@string/title_channel_num"
android:textSize="14sp"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Switch
android:id="@+id/switch_time"
android:text="@string/title_time"
android:textSize="14sp"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Switch
android:id="@+id/switch_boot_startup"
android:text="@string/title_boot_startup"
android:textSize="14sp"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Switch
android:id="@+id/switch_grid"
android:text="@string/title_grid"
android:textSize="14sp"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView
android:id="@+id/appreciate"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:src="@drawable/appreciate"
android:background="@color/white" />
android:background="@drawable/rounded_white_right" />
</LinearLayout>
</FrameLayout>
+43 -17
View File
@@ -1,19 +1,45 @@
<?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">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<FrameLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="0dp"
android:textColor="#FFEEEEEE"
android:textStyle="bold"
android:textSize="32sp" />
</FrameLayout>
<RelativeLayout
android:id="@+id/time"
android:layout_width="120dp"
android:layout_height="60dp"
android:layout_gravity="end|top"
android:layout_marginTop="15dp"
android:layout_marginEnd="50dp"
android:gravity="end|bottom" >
<TextView
android:id="@+id/channel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="40sp"
tools:text="99"
android:layout_alignBaseline="@+id/content"/>
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="32sp"
tools:text="12:34" />
</RelativeLayout>
</FrameLayout>
</LinearLayout>
+2 -2
View File
@@ -1,7 +1,7 @@
<resources>
<color name="focus">#0096a6</color>
<color name="fastlane_background">#30000000</color>
<color name="black">#000</color>
<color name="white">#FFF</color>
<color name="focus">#0096a6</color>
<color name="blur">#223239</color>
<color name="ic_launcher_background">#223239</color>
</resources>
@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="server_url">http://localhost:8080</string>
</resources>
+2 -2
View File
@@ -4,7 +4,7 @@
<string name="title_channel_num">换台时显示频道号</string>
<string name="check_version">检查更新</string>
<string name="title_boot_startup">开机自启</string>
<string name="dismiss_error">好的</string>
<string name="title_grid">频道列表采用网格样式</string>
<string name="title_time">显示时间</string>
<string name="exit">退出</string>
<string name="exit">退出应用</string>
</resources>
-7
View File
@@ -1,10 +1,3 @@
<resources>
<style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
<item name="lbImageCardViewType">Title|Content</item>
</style>
<style name="CustomImageCardTheme" parent="Theme.Leanback">
<item name="imageCardViewStyle">@style/CustomImageCardViewStyle</item>
</style>
</resources>
+1 -3
View File
@@ -1,8 +1,6 @@
<resources>
<style name="Theme.MyTV" parent="@style/Theme.Leanback">
<item name="browsePaddingStart">11dp</item>
<item name="browsePaddingEnd">11dp</item>
<item name="browseRowsMarginTop">54dp</item>
<item name="android:fitsSystemWindows">false</item>
</style>
</resources>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 0 B