Compare commits

..
10 Commits
Author SHA1 Message Date
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
12 changed files with 342 additions and 248 deletions
+4
View File
@@ -18,6 +18,10 @@
* 性能优化 * 性能优化
### v1.5.0(通用版)
* 修复部分情况下APP切换后无法继续播放的问题
### v1.4.9(高版本专用) ### v1.4.9(高版本专用)
* 同步v1.4.8 * 同步v1.4.8
+1 -1
View File
@@ -101,7 +101,7 @@ dependencies {
implementation "androidx.media3:media3-exoplayer-hls:$media3_version" implementation "androidx.media3:media3-exoplayer-hls:$media3_version"
// 21:2.9.0 17:2.6.4 // 21:2.9.0 17:2.6.4
def retrofit2_version = "2.9.0" def retrofit2_version = "2.6.4"
implementation 'com.google.protobuf:protobuf-kotlin:3.25.1' implementation 'com.google.protobuf:protobuf-kotlin:3.25.1'
implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version" implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version"
@@ -0,0 +1,119 @@
package com.lizongying.mytv
import android.graphics.Color
import android.view.ContextThemeWrapper
import android.view.View
import android.view.ViewGroup
import android.view.animation.ScaleAnimation
import android.widget.ImageView
import androidx.leanback.widget.ImageCardView
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel
class CardAdapter(private val owner: LifecycleOwner, private var tvListViewModel: TVListViewModel) :
RecyclerView.Adapter<CardAdapter.ViewHolder>() {
private var listener: ItemListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = object :
ImageCardView(ContextThemeWrapper(parent.context, R.style.CustomImageCardTheme)) {}
startScaleAnimation(cardView, 1.0f, 0.9f)
cardView.isFocusable = true
cardView.isFocusableInTouchMode = true
return ViewHolder(cardView)
}
private fun startScaleAnimation(view: View, fromScale: Float, toScale: Float) {
val scaleAnimation = ScaleAnimation(
fromScale, toScale,
fromScale, toScale,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
ScaleAnimation.RELATIVE_TO_SELF, 0.5f
)
scaleAnimation.duration = 200
scaleAnimation.fillAfter = true
view.startAnimation(scaleAnimation)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
val item = tvListViewModel.getTVViewModel(position)
val tvViewModel = item as TVViewModel
val cardView = viewHolder.view as ImageCardView
cardView.setOnFocusChangeListener { _, hasFocus ->
listener?.onItemFocusChange(item, hasFocus)
if (hasFocus) {
startScaleAnimation(cardView, 0.9f, 1.0f)
} else {
startScaleAnimation(cardView, 1.0f, 0.9f)
}
}
cardView.setOnClickListener { _ ->
listener?.onItemClicked(item)
}
cardView.titleText = tvViewModel.title.value
cardView.tag = tvViewModel.videoUrl.value
when (tvViewModel.title.value) {
"CCTV8K 超高清" -> Glide.with(viewHolder.view.context)
.load(R.drawable.cctv8k)
.centerInside()
.into(cardView.mainImageView)
"天津卫视" -> Glide.with(viewHolder.view.context)
.load(R.drawable.tianjin)
.centerInside()
.into(cardView.mainImageView)
"新疆卫视" -> Glide.with(viewHolder.view.context)
.load(R.drawable.xinjiang)
.centerInside()
.into(cardView.mainImageView)
else -> Glide.with(viewHolder.view.context)
.load(tvViewModel.logo.value)
.centerInside()
.into(cardView.mainImageView)
}
cardView.mainImageView.setBackgroundColor(Color.WHITE)
cardView.setMainImageScaleType(ImageView.ScaleType.CENTER_INSIDE)
tvViewModel.program.observe(owner) { _ ->
val program = tvViewModel.getProgramOne()
if (program != null) {
cardView.contentText = program.name
}
}
}
override fun getItemCount() = tvListViewModel.size()
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val view = itemView
}
interface ItemListener {
fun onItemFocusChange(tvViewModel: TVViewModel, hasFocus: Boolean)
fun onItemClicked(tvViewModel: TVViewModel)
}
fun setItemListener(listener: ItemListener) {
this.listener = listener
}
companion object {
private const val TAG = "CardAdapter"
}
}
@@ -122,6 +122,7 @@ class MainActivity : FragmentActivity() {
val transaction = supportFragmentManager.beginTransaction() val transaction = supportFragmentManager.beginTransaction()
if (mainFragment.isHidden) { if (mainFragment.isHidden) {
mainFragment.setPosition()
transaction.show(mainFragment) transaction.show(mainFragment)
keepRunnable() keepRunnable()
} else { } else {
@@ -5,30 +5,31 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.leanback.app.BrowseSupportFragment import androidx.fragment.app.Fragment
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.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.lizongying.mytv.Utils.dpToPx
import com.lizongying.mytv.Utils.getDateTimestamp import com.lizongying.mytv.Utils.getDateTimestamp
import com.lizongying.mytv.databinding.RowBinding
import com.lizongying.mytv.databinding.ShowBinding
import com.lizongying.mytv.models.TVListViewModel import com.lizongying.mytv.models.TVListViewModel
import com.lizongying.mytv.models.TVViewModel import com.lizongying.mytv.models.TVViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class MainFragment : BrowseSupportFragment() { class MainFragment : Fragment(), CardAdapter.ItemListener {
private var itemPosition = 0 private var itemPosition = 0
private var rowsAdapter: ArrayObjectAdapter? = null private var rowList: MutableList<View> = mutableListOf()
private var _binding: ShowBinding? = null
private val binding get() = _binding!!
private var request = Request() private var request = Request()
@@ -43,15 +44,12 @@ class MainFragment : BrowseSupportFragment() {
private var ready = 0 private var ready = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreateView(
Log.i(TAG, "onCreate") inflater: LayoutInflater, container: ViewGroup?,
super.onCreate(savedInstanceState) savedInstanceState: Bundle?
headersState = HEADERS_DISABLED ): View {
} _binding = ShowBinding.inflate(inflater, container, false)
return binding.root
override fun onStart() {
Log.i(TAG, "onStart")
super.onStart()
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
@@ -60,171 +58,140 @@ class MainFragment : BrowseSupportFragment() {
activity?.let { request.initYSP(it) } activity?.let { request.initYSP(it) }
sharedPref = (activity as? MainActivity)?.sharedPref!! sharedPref = (activity as? MainActivity)?.sharedPref!!
loadRows() view?.post {
val content = binding.content
setupEventListeners() var idx: Long = 0
for ((k, v) in TVList.list) {
val itemBinding: RowBinding =
RowBinding.inflate(layoutInflater, content, false)
mUpdateProgramRunnable = UpdateProgramRunnable() val tvListViewModelCurrent = TVListViewModel()
handler.post(mUpdateProgramRunnable) for ((idx2, v1) in v.withIndex()) {
val tvViewModel = TVViewModel(v1)
tvListViewModel.tvListViewModel.value?.forEach { tvViewModel -> tvViewModel.setRowPosition(idx.toInt())
tvViewModel.errInfo.observe(viewLifecycleOwner) { _ -> tvViewModel.setItemPosition(idx2)
if (tvViewModel.errInfo.value != null tvListViewModelCurrent.addTVViewModel(tvViewModel)
&& tvViewModel.id.value == itemPosition tvListViewModel.addTVViewModel(tvViewModel)
) {
Toast.makeText(context, tvViewModel.errInfo.value, Toast.LENGTH_SHORT).show()
} }
} tvListViewModel.maxNum.add(v.size)
tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
// not first time && channel not change val adapter = CardAdapter(viewLifecycleOwner, tvListViewModelCurrent)
if (tvViewModel.ready.value != null rowList.add(itemBinding.rowItems)
&& tvViewModel.id.value == itemPosition
&& check(tvViewModel) adapter.setItemListener(this)
) {
Log.i(TAG, "ready ${tvViewModel.title.value}") itemBinding.rowHeader.text = k
(activity as? MainActivity)?.play(tvViewModel) itemBinding.rowItems.adapter = adapter
} itemBinding.rowItems.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
val layoutParams = itemBinding.row.layoutParams as ViewGroup.MarginLayoutParams
layoutParams.topMargin = dpToPx(11F)
itemBinding.row.layoutParams = layoutParams
content.addView(itemBinding.row)
idx++
} }
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.change.value != null) { mUpdateProgramRunnable = UpdateProgramRunnable()
val title = tvViewModel.title.value handler.post(mUpdateProgramRunnable)
if (tvViewModel.pid.value != "") {
Log.i(TAG, "request $title") itemPosition = sharedPref.getInt(POSITION, 0)
lifecycleScope.launch(Dispatchers.IO) { if (itemPosition >= tvListViewModel.size()) {
tvViewModel.let { request.fetchData(it) } itemPosition = 0
} }
(activity as? MainActivity)?.showInfoFragment(tvViewModel) tvListViewModel.setItemPosition(itemPosition)
setSelectedPosition( setPosition()
tvViewModel.getRowPosition(), true, tvListViewModel.tvListViewModel.value?.forEach { tvViewModel ->
SelectItemViewHolderTask(tvViewModel.getItemPosition()) tvViewModel.errInfo.observe(viewLifecycleOwner) { _ ->
) if (tvViewModel.errInfo.value != null
} else { && tvViewModel.id.value == itemPosition
if (check(tvViewModel)) { ) {
(activity as? MainActivity)?.play(tvViewModel) Toast.makeText(context, tvViewModel.errInfo.value, Toast.LENGTH_SHORT)
.show()
}
}
tvViewModel.ready.observe(viewLifecycleOwner) { _ ->
// not first time && channel not change
if (tvViewModel.ready.value != null
&& tvViewModel.id.value == itemPosition
&& check(tvViewModel)
) {
Log.i(TAG, "ready ${tvViewModel.title.value}")
(activity as? MainActivity)?.play(tvViewModel)
}
}
tvViewModel.change.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.change.value != null) {
val title = tvViewModel.title.value
Log.i(TAG, "switch $title")
if (tvViewModel.pid.value != "") {
Log.i(TAG, "request $title ${tvViewModel.pid.value}")
lifecycleScope.launch(Dispatchers.IO) {
tvViewModel.let { request.fetchData(it) }
}
(activity as? MainActivity)?.showInfoFragment(tvViewModel) (activity as? MainActivity)?.showInfoFragment(tvViewModel)
setSelectedPosition( setPosition(
tvViewModel.getRowPosition(), true, tvViewModel.getRowPosition(), tvViewModel.getItemPosition()
SelectItemViewHolderTask(tvViewModel.getItemPosition())
) )
} else {
if (check(tvViewModel)) {
(activity as? MainActivity)?.play(tvViewModel)
(activity as? MainActivity)?.showInfoFragment(tvViewModel)
setPosition(
tvViewModel.getRowPosition(), tvViewModel.getItemPosition()
)
}
} }
} }
} }
} }
}
fragmentReady() fragmentReady()
}
fun toLastPosition() {
setSelectedPosition(
selectedPosition, false,
SelectItemViewHolderTask(tvListViewModel.maxNum[selectedPosition] - 1)
)
}
fun toFirstPosition() {
setSelectedPosition(
selectedPosition, false,
SelectItemViewHolderTask(0)
)
}
override fun startHeadersTransition(withHeaders: Boolean) {
}
private fun loadRows() {
rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
val cardPresenter = CardPresenter(viewLifecycleOwner)
var idx: Long = 0
for ((k, v) in TVList.list) {
val listRowAdapter = ArrayObjectAdapter(cardPresenter)
for ((idx2, v1) in v.withIndex()) {
val tvViewModel = TVViewModel(v1)
tvViewModel.setRowPosition(idx.toInt())
tvViewModel.setItemPosition(idx2)
tvListViewModel.addTVViewModel(tvViewModel)
listRowAdapter.add(tvViewModel)
}
tvListViewModel.maxNum.add(v.size)
val header = HeaderItem(idx, k)
rowsAdapter!!.add(ListRow(header, listRowAdapter))
idx++
}
adapter = rowsAdapter
itemPosition = sharedPref.getInt(POSITION, 0)
if (itemPosition >= tvListViewModel.size()) {
itemPosition = 0
}
tvListViewModel.setItemPosition(itemPosition)
}
fun prevSource() {
view?.post {
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
if (tvViewModel != null) {
if (tvViewModel.videoUrl.value!!.size > 1) {
val videoIndex = tvViewModel.videoIndex.value?.minus(1)
if (videoIndex == -1) {
tvViewModel.setVideoIndex(tvViewModel.videoUrl.value!!.size - 1)
}
tvViewModel.changed()
}
}
} }
} }
fun nextSource() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view?.post { super.onViewCreated(view, savedInstanceState)
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) }
if (tvViewModel != null) {
if (tvViewModel.videoUrl.value!!.size > 1) { override fun onItemFocusChange(tvViewModel: TVViewModel, hasFocus: Boolean) {
val videoIndex = tvViewModel.videoIndex.value?.plus(1) if (hasFocus) {
if (videoIndex == tvViewModel.videoUrl.value!!.size) { tvListViewModel.setItemPositionCurrent(tvViewModel.id.value!!)
tvViewModel.setVideoIndex(0) (activity as MainActivity).keepRunnable()
}
tvViewModel.changed()
}
}
} }
} }
private fun setupEventListeners() { override fun onItemClicked(tvViewModel: TVViewModel) {
onItemViewClickedListener = ItemViewClickedListener() if (this.isHidden) {
onItemViewSelectedListener = ItemViewSelectedListener() (activity as? MainActivity)?.switchMainFragment()
return
}
if (itemPosition != tvViewModel.id.value!!) {
itemPosition = tvViewModel.id.value!!
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
(activity as? MainActivity)?.switchMainFragment()
} }
private inner class ItemViewClickedListener : OnItemViewClickedListener { fun setPosition() {
override fun onItemClicked( val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
itemViewHolder: Presenter.ViewHolder, rowList[tvViewModel!!.getRowPosition()].post {
item: Any, ((rowList[tvViewModel.getRowPosition()] as RecyclerView).layoutManager as LinearLayoutManager).findViewByPosition(
rowViewHolder: RowPresenter.ViewHolder, tvViewModel.getItemPosition()
row: Row )?.requestFocus()
) {
if (item is TVViewModel) {
if (itemPosition != item.id.value!!) {
itemPosition = item.id.value!!
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
(activity as? MainActivity)?.switchMainFragment()
}
} }
} }
private inner class ItemViewSelectedListener : OnItemViewSelectedListener { fun setPosition(rowPosition: Int, itemPosition: Int) {
override fun onItemSelected( rowList[rowPosition].post {
itemViewHolder: Presenter.ViewHolder?, item: Any?, ((rowList[rowPosition] as RecyclerView).layoutManager as LinearLayoutManager).findViewByPosition(
rowViewHolder: RowPresenter.ViewHolder, row: Row itemPosition
) { )?.requestFocus()
if (item is TVViewModel) {
tvListViewModel.setItemPositionCurrent(item.id.value!!)
(activity as MainActivity).keepRunnable()
}
} }
} }
@@ -304,7 +271,7 @@ class MainFragment : BrowseSupportFragment() {
inner class UpdateProgramRunnable : Runnable { inner class UpdateProgramRunnable : Runnable {
override fun run() { override fun run() {
tvListViewModel.tvListViewModel.value?.filter { it.programId.value != null && it.programId.value != "" } tvListViewModel.tvListViewModel.value?.filter { it.programId.value != null }
?.forEach { tvViewModel -> ?.forEach { tvViewModel ->
updateProgram( updateProgram(
tvViewModel tvViewModel
@@ -335,6 +302,12 @@ class MainFragment : BrowseSupportFragment() {
handler.removeCallbacks(mUpdateProgramRunnable) handler.removeCallbacks(mUpdateProgramRunnable)
} }
override fun onDestroyView() {
Log.i(TAG, "onDestroyView")
super.onDestroyView()
_binding = null
}
companion object { companion object {
private const val TAG = "MainFragment" private const val TAG = "MainFragment"
private const val POSITION = "position" private const val POSITION = "position"
@@ -39,8 +39,6 @@ class Request {
private lateinit var btraceRunnable: BtraceRunnable private lateinit var btraceRunnable: BtraceRunnable
private var tokenRunnable: TokenRunnable = TokenRunnable() private var tokenRunnable: TokenRunnable = TokenRunnable()
private val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
private var mapping = mapOf( private var mapping = mapOf(
"CCTV4K" to "CCTV4K 超高清", "CCTV4K" to "CCTV4K 超高清",
"CCTV1" to "CCTV1 综合", "CCTV1" to "CCTV1 综合",
@@ -123,6 +121,7 @@ class Request {
Base64.DEFAULT Base64.DEFAULT
) )
val decodedString = String(decodedBytes) val decodedString = String(decodedBytes)
val regex = Regex("""des_key = "([^"]+).+var des_iv = "([^"]+)""")
val matchResult = regex.find(decodedString) val matchResult = regex.find(decodedString)
if (matchResult != null) { if (matchResult != null) {
val (key, iv) = matchResult.destructured val (key, iv) = matchResult.destructured
@@ -132,7 +131,7 @@ class Request {
keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0), keyBytes + byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0),
ivBytes ivBytes
).uppercase() ).uppercase()
Log.d(TAG, "$title url $url") // Log.d(TAG, "$title url $url")
tvModel.addVideoUrl(url) tvModel.addVideoUrl(url)
tvModel.allReady() tvModel.allReady()
tvModel.retryTimes = 0 tvModel.retryTimes = 0
@@ -143,14 +142,8 @@ class Request {
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
if (tvModel.needToken) { if (tvModel.needToken) {
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { token = ""
if (!tvModel.mustToken) { fetchVideo(tvModel)
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else { } else {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} }
@@ -165,14 +158,8 @@ class Request {
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
if (tvModel.needToken) { if (tvModel.needToken) {
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { token = ""
if (!tvModel.mustToken) { fetchVideo(tvModel)
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else { } else {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} }
@@ -184,14 +171,8 @@ class Request {
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
if (tvModel.needToken) { if (tvModel.needToken) {
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { token = ""
if (!tvModel.mustToken) { fetchVideo(tvModel)
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else { } else {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} }
@@ -204,14 +185,8 @@ class Request {
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
if (tvModel.needToken) { if (tvModel.needToken) {
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) { token = ""
if (!tvModel.mustToken) { fetchVideo(tvModel)
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else { } else {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} }
@@ -233,28 +208,18 @@ class Request {
fetchVideo(tvModel, cookie) fetchVideo(tvModel, cookie)
} else { } else {
Log.e(TAG, "info status error") Log.e(TAG, "info status error")
if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.tokenRetryTimes++ tvModel.retryTimes++
fetchVideo(tvModel) fetchVideo(tvModel)
} else {
if (!tvModel.mustToken) {
val cookie = "vplatform=109"
fetchVideo(tvModel, cookie)
}
} }
} }
} }
override fun onFailure(call: Call<Info>, t: Throwable) { override fun onFailure(call: Call<Info>, t: Throwable) {
Log.e(TAG, "info request error $t") Log.e(TAG, "info request error $t")
if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.tokenRetryTimes++ tvModel.retryTimes++
fetchVideo(tvModel) fetchVideo(tvModel)
} else {
if (!tvModel.mustToken) {
val cookie = "vplatform=109"
fetchVideo(tvModel, cookie)
}
} }
} }
}) })
@@ -71,7 +71,7 @@ object TVList {
) )
private var mappingEPG = mapOf( private var mappingEPG = mapOf(
"CCTV4K 超高清" to "600002264", "CCTV4K 超高清" to "600002264",
// "CCTV8K 超高清" to "600156816", "CCTV8K 超高清" to "600156816",
"CCTV1 综合" to "600001859", "CCTV1 综合" to "600001859",
"CCTV2 财经" to "600001800", "CCTV2 财经" to "600001800",
"CCTV3 综艺" to "600001801", "CCTV3 综艺" to "600001801",
@@ -95,7 +95,7 @@ object TVList {
"CGTN 俄语频道" to "600084758", "CGTN 俄语频道" to "600084758",
"CGTN 阿拉伯语频道" to "600084782", "CGTN 阿拉伯语频道" to "600084782",
"CGTN 西班牙语频道" to "600084744", "CGTN 西班牙语频道" to "600084744",
// "CGTN 记录频道" to "600084781", "CGTN 记录频道" to "600084781",
"风云剧场" to "600099658", "风云剧场" to "600099658",
"第一剧场" to "600099655", "第一剧场" to "600099655",
"怀旧剧场" to "600099620", "怀旧剧场" to "600099620",
@@ -130,8 +130,8 @@ object TVList {
"四川卫视" to "600002516", "四川卫视" to "600002516",
"东南卫视" to "600002484", "东南卫视" to "600002484",
"海南卫视" to "600002506", "海南卫视" to "600002506",
// "天津卫视" to "600152137", "天津卫视" to "600152137",
// "新疆卫视" to "600152138", "新疆卫视" to "600152138",
) )
private var mappingVideo = mapOf( private var mappingVideo = mapOf(
"CCTV4K 超高清" to arrayOf("600002264", "2000266303"), "CCTV4K 超高清" to arrayOf("600002264", "2000266303"),
@@ -2,6 +2,7 @@ package com.lizongying.mytv.api
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Log
import com.lizongying.mytv.Encryptor import com.lizongying.mytv.Encryptor
import com.lizongying.mytv.MainActivity import com.lizongying.mytv.MainActivity
import com.lizongying.mytv.Utils.getDateTimestamp import com.lizongying.mytv.Utils.getDateTimestamp
@@ -19,10 +19,8 @@ class TVViewModel(private var tv: TV) : ViewModel() {
private var rowPosition: Int = 0 private var rowPosition: Int = 0
private var itemPosition: Int = 0 private var itemPosition: Int = 0
var retryTimes = 0 var retryTimes: Int = 0
var tokenRetryTimes = 0 var retryMaxTimes: Int = 8
var retryMaxTimes = 8
var tokenRetryMaxTimes = 2
var programUpdateTime: Long = 0 var programUpdateTime: Long = 0
private val _errInfo = MutableLiveData<String>() private val _errInfo = MutableLiveData<String>()
@@ -79,8 +77,6 @@ class TVViewModel(private var tv: TV) : ViewModel() {
var needToken = false var needToken = false
var mustToken = false
private val channelsNeedToken = arrayOf( private val channelsNeedToken = arrayOf(
"CCTV4K 超高清", "CCTV4K 超高清",
"CCTV2 财经", "CCTV2 财经",
@@ -138,25 +134,6 @@ class TVViewModel(private var tv: TV) : ViewModel() {
"新疆卫视", "新疆卫视",
) )
private val channelsMustToken = arrayOf(
"CCTV3 综艺",
"CCTV6 电影",
"CCTV8 电视剧",
"风云剧场",
"第一剧场",
"怀旧剧场",
"世界地理",
"风云音乐",
"兵器科技",
"风云足球",
"高尔夫网球",
"女性时尚",
"央视文化精品",
"央视台球",
"电视指南",
"卫生健康",
)
fun addVideoUrl(url: String) { fun addVideoUrl(url: String) {
if (_videoUrl.value?.isNotEmpty() == true) { if (_videoUrl.value?.isNotEmpty() == true) {
if (_videoUrl.value!!.last().contains("cctv.cn")) { if (_videoUrl.value!!.last().contains("cctv.cn")) {
@@ -211,9 +188,6 @@ class TVViewModel(private var tv: TV) : ViewModel() {
if (tv.title in channelsNeedToken) { if (tv.title in channelsNeedToken) {
needToken = true needToken = true
} }
if (tv.title in channelsMustToken) {
mustToken = true
}
} }
fun getRowPosition(): Int { fun getRowPosition(): Int {
+25
View File
@@ -0,0 +1,25 @@
<?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="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/row_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="0dp"
android:layout_marginStart="10dp"
android:layout_marginBottom="5dp"
android:gravity="start"
android:textColor="#FFEEEEEE"
android:textSize="22sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/row_items"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/show_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</FrameLayout>
+16 -1
View File
@@ -1,10 +1,25 @@
<resources> <resources>
<style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle"> <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
<item name="lbImageCardViewType">Title|Content</item> <item name="cardType">infoUnder</item>
<item name="infoVisibility">always</item>
<!-- In order to keep backward compatibility we have to create an icon on right. -->
<item name="lbImageCardViewType">Title|Content|IconOnRight</item>
<!-- Deprecated. Use 'Widget.Leanback.ImageCardView.InfoAreaStyle' instead. -->
<item name="infoAreaBackground">@null</item>
</style>
<style name="CustomImageCardViewImageStyle" parent="Widget.Leanback.ImageCardView.ImageStyle">
<item name="android:layout_width">143dp</item>
<item name="android:layout_height">50dp</item>
<item name="android:adjustViewBounds">true</item>
<item name="android:contentDescription">@null</item>
<item name="android:scaleType">centerCrop</item>
<item name="layout_viewType">main</item>
</style> </style>
<style name="CustomImageCardTheme" parent="Theme.Leanback"> <style name="CustomImageCardTheme" parent="Theme.Leanback">
<item name="imageCardViewStyle">@style/CustomImageCardViewStyle</item> <item name="imageCardViewStyle">@style/CustomImageCardViewStyle</item>
<item name="imageCardViewImageStyle">@style/CustomImageCardViewImageStyle</item>
</style> </style>
</resources> </resources>