Compare commits

..
9 Commits
Author SHA1 Message Date
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
19 changed files with 428 additions and 472 deletions
+5 -13
View File
@@ -1,6 +1,6 @@
# 我的电视 # 我的电视
电视直播软件,安装即可使用 安卓电视直播软件,内置直播源
## 使用 ## 使用
@@ -14,24 +14,16 @@
## 更新日志 ## 更新日志
### v1.5.1(高版本专用)
* 性能优化
### v1.4.9(高版本专用)
* 同步v1.4.8
### v1.4.8(通用版) ### v1.4.8(通用版)
* 频道号从1开始,CCTV5+为18 * 频道号从1开始,cctv5+为18
* 提高CCTV6清晰度 * 提高cctv6清晰度
* 增加天津卫视、新疆卫视 * 增加天津卫视、新疆卫视
### v1.4.7(高版本专用) ### v1.4.7(高版本专用)
* 修复部分用户CCTV13播放过程中卡住的问题 * 修复部分用户cctv13播放过程中卡住的问题
* 调整CCTV的频道顺序 * 调整cctv的频道顺序
### v1.4.6(通用版) ### v1.4.6(通用版)
+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"
+3 -5
View File
@@ -9,19 +9,17 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:largeHeap="true"
android:icon="@drawable/logo" android:icon="@drawable/logo"
android:logo="@drawable/logo"
android:banner="@drawable/banner"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:networkSecurityConfig="@xml/network"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.MyTV"> android:theme="@style/Theme.MyTV">
<activity <activity
android:keepScreenOn="true" android:keepScreenOn="true"
android:name=".MainActivity" android:name=".MainActivity"
android:banner="@drawable/banner"
android:exported="true" android:exported="true"
android:icon="@drawable/logo"
android:logo="@drawable/logo"
android:screenOrientation="landscape"> android:screenOrientation="landscape">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
Binary file not shown.
Binary file not shown.
@@ -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"
}
}
@@ -48,6 +48,7 @@ class InfoFragment : Fragment() {
.into(binding.infoLogo) .into(binding.infoLogo)
} }
val program = tvViewModel.getProgramOne() val program = tvViewModel.getProgramOne()
if (program != null) { if (program != null) {
binding.infoDesc.text = program.name binding.infoDesc.text = program.name
@@ -35,17 +35,16 @@ class MainActivity : FragmentActivity() {
private lateinit var gestureDetector: GestureDetector private lateinit var gestureDetector: GestureDetector
private val handler = Handler() private val handler = Handler()
private val delayHideMain: Long = 5000 private val delay: Long = 4000
private val delayHideSetting: Long = 10000 private val delayHideHelp: Long = 10000
lateinit var sharedPref: SharedPreferences private lateinit var sharedPref: SharedPreferences
private var channelReversal = false private var channelReversal = false
private var channelNum = true private var channelNum = true
private var versionName = "" private var versionName = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
@@ -61,6 +60,7 @@ class MainActivity : FragmentActivity() {
.add(R.id.main_browse_fragment, mainFragment) .add(R.id.main_browse_fragment, mainFragment)
.hide(mainFragment) .hide(mainFragment)
.commit() .commit()
mainFragment.view?.requestFocus()
} }
gestureDetector = GestureDetector(this, GestureListener()) gestureDetector = GestureDetector(this, GestureListener())
@@ -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 {
@@ -132,11 +133,11 @@ class MainActivity : FragmentActivity() {
} }
fun keepRunnable() { fun keepRunnable() {
handler.removeCallbacks(hideMain) handler.removeCallbacks(hideRunnable)
handler.postDelayed(hideMain, delayHideMain) handler.postDelayed(hideRunnable, delay)
} }
private val hideMain = Runnable { private val hideRunnable = Runnable {
if (!mainFragment.isHidden) { if (!mainFragment.isHidden) {
supportFragmentManager.beginTransaction().hide(mainFragment).commit() supportFragmentManager.beginTransaction().hide(mainFragment).commit()
} }
@@ -222,7 +223,7 @@ class MainActivity : FragmentActivity() {
this.channelNum = channelNum this.channelNum = channelNum
} }
private fun showSetting() { private fun showHelp() {
if (!mainFragment.isHidden) { if (!mainFragment.isHidden) {
return return
} }
@@ -230,15 +231,15 @@ class MainActivity : FragmentActivity() {
Log.i(TAG, "settingFragment ${settingFragment.isVisible}") Log.i(TAG, "settingFragment ${settingFragment.isVisible}")
if (!settingFragment.isVisible) { if (!settingFragment.isVisible) {
settingFragment.show(supportFragmentManager, "setting") settingFragment.show(supportFragmentManager, "setting")
handler.removeCallbacks(hideSetting) handler.removeCallbacks(hideHelp)
handler.postDelayed(hideSetting, delayHideSetting) handler.postDelayed(hideHelp, delayHideHelp)
} else { } else {
handler.removeCallbacks(hideSetting) handler.removeCallbacks(hideHelp)
settingFragment.dismiss() settingFragment.dismiss()
} }
} }
private val hideSetting = Runnable { private val hideHelp = Runnable {
if (settingFragment.isVisible) { if (settingFragment.isVisible) {
settingFragment.dismiss() settingFragment.dismiss()
} }
@@ -359,27 +360,27 @@ class MainActivity : FragmentActivity() {
} }
KeyEvent.KEYCODE_BOOKMARK -> { KeyEvent.KEYCODE_BOOKMARK -> {
showSetting() showHelp()
return true return true
} }
KeyEvent.KEYCODE_UNKNOWN -> { KeyEvent.KEYCODE_UNKNOWN -> {
showSetting() showHelp()
return true return true
} }
KeyEvent.KEYCODE_HELP -> { KeyEvent.KEYCODE_HELP -> {
showSetting() showHelp()
return true return true
} }
KeyEvent.KEYCODE_SETTINGS -> { KeyEvent.KEYCODE_SETTINGS -> {
showSetting() showHelp()
return true return true
} }
KeyEvent.KEYCODE_MENU -> { KeyEvent.KEYCODE_MENU -> {
showSetting() showHelp()
return true return true
} }
@@ -477,7 +478,7 @@ class MainActivity : FragmentActivity() {
private fun hashSignature(signature: Signature): String { private fun hashSignature(signature: Signature): String {
return try { return try {
val md = MessageDigest.getInstance("MD5") val md = MessageDigest.getInstance("SHA-256")
md.update(signature.toByteArray()) md.update(signature.toByteArray())
val digest = md.digest() val digest = md.digest()
digest.let { it -> it.joinToString("") { "%02x".format(it) } } digest.let { it -> it.joinToString("") { "%02x".format(it) } }
@@ -487,22 +488,6 @@ class MainActivity : FragmentActivity() {
} }
} }
override fun onStart() {
Log.i(TAG, "onStart")
super.onStart()
}
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
}
override fun onPause() {
Log.i(TAG, "onPause")
super.onPause()
handler.removeCallbacks(hideMain)
}
companion object { companion object {
private const val TAG = "MainActivity" private const val TAG = "MainActivity"
private const val CHANNEL_REVERSAL = "channel_reversal" private const val CHANNEL_REVERSAL = "channel_reversal"
@@ -1,40 +1,42 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle 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()
var tvListViewModel = TVListViewModel() var tvListViewModel = TVListViewModel()
private lateinit var sharedPref: SharedPreferences private var sharedPref: SharedPreferences? = null
private var lastVideoUrl = "" private var lastVideoUrl = ""
@@ -43,188 +45,154 @@ 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?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
activity?.let { request.initYSP(it) } activity?.let { request.initYSP(it) }
sharedPref = (activity as? MainActivity)?.sharedPref!! sharedPref = activity?.getPreferences(Context.MODE_PRIVATE)
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.getTVListViewModel().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()
}
} }
} }
@@ -294,7 +262,7 @@ class MainFragment : BrowseSupportFragment() {
tvViewModel.programUpdateTime = timestamp tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel) request.fetchProgram(tvViewModel)
} else { } else {
if (tvViewModel.program.value!!.last().et - timestamp < 600) { if (timestamp - tvViewModel.program.value!!.last().et < 600) {
tvViewModel.programUpdateTime = timestamp tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel) request.fetchProgram(tvViewModel)
} }
@@ -304,7 +272,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.getTVListViewModel().value?.filter { it.programId.value != null }
?.forEach { tvViewModel -> ?.forEach { tvViewModel ->
updateProgram( updateProgram(
tvViewModel tvViewModel
@@ -314,19 +282,14 @@ class MainFragment : BrowseSupportFragment() {
} }
} }
override fun onResume() {
Log.i(TAG, "onResume")
super.onResume()
}
override fun onStop() { override fun onStop() {
Log.i(TAG, "onStop") Log.i(TAG, "onStop")
super.onStop() super.onStop()
with(sharedPref.edit()) { with(sharedPref!!.edit()) {
putInt(POSITION, itemPosition) putInt(POSITION, itemPosition)
apply() apply()
} }
Log.i(TAG, "$POSITION saved") Log.i(TAG, "POSITION saved")
} }
override fun onDestroy() { override fun onDestroy() {
@@ -335,6 +298,17 @@ class MainFragment : BrowseSupportFragment() {
handler.removeCallbacks(mUpdateProgramRunnable) handler.removeCallbacks(mUpdateProgramRunnable)
} }
override fun onDestroyView() {
Log.i(TAG, "onDestroyView")
super.onDestroyView()
_binding = null
}
override fun onResume() {
super.onResume()
view?.post { view?.requestFocus() }
}
companion object { companion object {
private const val TAG = "MainFragment" private const val TAG = "MainFragment"
private const val POSITION = "position" private const val POSITION = "position"
@@ -74,31 +74,23 @@ class PlayerFragment : Fragment() {
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
fun play(tvViewModel: TVViewModel) { fun play(tvViewModel: TVViewModel) {
this.tvViewModel = tvViewModel this.tvViewModel = tvViewModel
val videoUrlCurrent = tvViewModel.getVideoUrlCurrent()
playerView?.player?.run { playerView?.player?.run {
setMediaItem(MediaItem.fromUri(tvViewModel.getVideoUrlCurrent())) setMediaItem(MediaItem.fromUri(videoUrlCurrent))
prepare() prepare()
} }
} }
override fun onStart() { override fun onStart() {
Log.i(TAG, "onStart")
super.onStart() super.onStart()
if (playerView != null && playerView!!.player?.isPlaying == false) { if (playerView != null) {
Log.i(TAG, "replay") playerView!!.player?.play()
playerView!!.player?.prepare()
} else {
Log.i(TAG, "playing")
} }
} }
override fun onResume() { override fun onStop() {
Log.i(TAG, "onResume") super.onStop()
super.onResume() if (playerView != null) {
}
override fun onPause() {
super.onPause()
if (playerView != null && playerView!!.player?.isPlaying == true) {
playerView!!.player?.stop() playerView!!.player?.stop()
} }
} }
+36 -130
View File
@@ -32,14 +32,10 @@ class Request {
private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService private var yspBtraceService: YSPBtraceService = ApiClient().yspBtraceService
private var yspProtoService: YSPProtoService = ApiClient().yspProtoService private var yspProtoService: YSPProtoService = ApiClient().yspProtoService
private var ysp: YSP? = null private var ysp: YSP? = null
private var token = ""
// TODO onDestroy // TODO onDestroy
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private lateinit var btraceRunnable: BtraceRunnable private lateinit var myRunnable: MyRunnable
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 超高清",
@@ -89,10 +85,6 @@ class Request {
"新疆卫视" to "新疆卫视", "新疆卫视" to "新疆卫视",
) )
init {
handler.post(tokenRunnable)
}
fun initYSP(context: Context) { fun initYSP(context: Context) {
ysp = YSP(context) ysp = YSP(context)
} }
@@ -101,8 +93,8 @@ class Request {
fun fetchVideo(tvModel: TVViewModel, cookie: String) { fun fetchVideo(tvModel: TVViewModel, cookie: String) {
call?.cancel() call?.cancel()
if (::btraceRunnable.isInitialized) { if (::myRunnable.isInitialized) {
handler.removeCallbacks(btraceRunnable) handler.removeCallbacks(myRunnable)
} }
val title = tvModel.title.value val title = tvModel.title.value
@@ -123,6 +115,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,28 +125,17 @@ 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.i(TAG, "$title url $url")
tvModel.addVideoUrl(url) tvModel.addVideoUrl(url)
tvModel.allReady() tvModel.allReady()
tvModel.retryTimes = 0 tvModel.retryTimes = 0
btraceRunnable = BtraceRunnable(tvModel) myRunnable = MyRunnable(tvModel)
handler.post(btraceRunnable) handler.post(myRunnable)
} else { } else {
Log.e(TAG, "$title key error") Log.e(TAG, "$title key error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
if (tvModel.needToken) { fetchVideo(tvModel, cookie)
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) {
if (!tvModel.mustToken) {
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else {
fetchVideo(tvModel, cookie)
}
} }
} }
} else { } else {
@@ -164,18 +146,7 @@ class Request {
Log.e(TAG, "$title url error $request $liveInfo") Log.e(TAG, "$title url error $request $liveInfo")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
if (tvModel.needToken) { fetchVideo(tvModel, cookie)
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) {
if (!tvModel.mustToken) {
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else {
fetchVideo(tvModel, cookie)
}
} }
} }
} }
@@ -183,18 +154,7 @@ class Request {
Log.e(TAG, "$title status error") Log.e(TAG, "$title status error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
if (tvModel.needToken) { fetchVideo(tvModel, cookie)
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) {
if (!tvModel.mustToken) {
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else {
fetchVideo(tvModel, cookie)
}
} }
} }
} }
@@ -203,66 +163,39 @@ class Request {
Log.e(TAG, "$title request error") Log.e(TAG, "$title request error")
if (tvModel.retryTimes < tvModel.retryMaxTimes) { if (tvModel.retryTimes < tvModel.retryMaxTimes) {
tvModel.retryTimes++ tvModel.retryTimes++
if (tvModel.needToken) { fetchVideo(tvModel, cookie)
if (tvModel.tokenRetryTimes == tvModel.tokenRetryMaxTimes) {
if (!tvModel.mustToken) {
fetchVideo(tvModel, cookie)
}
} else {
token = ""
fetchVideo(tvModel)
}
} else {
fetchVideo(tvModel, cookie)
}
} }
} }
}) })
} }
fun fetchVideo(tvModel: TVViewModel) { fun fetchVideo(tvModel: TVViewModel) {
if (token == "") { yspTokenService.getInfo()
yspTokenService.getInfo() .enqueue(object : Callback<Info> {
.enqueue(object : Callback<Info> { override fun onResponse(call: Call<Info>, response: Response<Info>) {
override fun onResponse(call: Call<Info>, response: Response<Info>) { if (response.isSuccessful) {
if (response.isSuccessful) { val token = response.body()?.data?.token
token = response.body()?.data?.token!! Log.i(TAG, "info success $token")
Log.i(TAG, "info success $token") val cookie =
val cookie = "vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token"
"vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" fetchVideo(tvModel, cookie)
fetchVideo(tvModel, cookie) } else {
} else { Log.e(TAG, "info status error")
Log.e(TAG, "info status error") if (tvModel.retryTimes < tvModel.retryMaxTimes) {
if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) { tvModel.retryTimes++
tvModel.tokenRetryTimes++
fetchVideo(tvModel)
} else {
if (!tvModel.mustToken) {
val cookie = "vplatform=109"
fetchVideo(tvModel, cookie)
}
}
}
}
override fun onFailure(call: Call<Info>, t: Throwable) {
Log.e(TAG, "info request error $t")
if (tvModel.tokenRetryTimes < tvModel.tokenRetryMaxTimes) {
tvModel.tokenRetryTimes++
fetchVideo(tvModel) fetchVideo(tvModel)
} else {
if (!tvModel.mustToken) {
val cookie = "vplatform=109"
fetchVideo(tvModel, cookie)
}
} }
} }
}) }
} else {
val cookie = override fun onFailure(call: Call<Info>, t: Throwable) {
"vplatform=109; yspopenid=vu0-8lgGV2LW9QjDeuBFsX8yMnzs37Q3_HZF6XyVDpGR_I; vusession=$token" Log.e(TAG, "info request error $t")
fetchVideo(tvModel, cookie) if (tvModel.retryTimes < tvModel.retryMaxTimes) {
} tvModel.retryTimes++
fetchVideo(tvModel)
}
}
})
} }
fun fetchData(tvModel: TVViewModel) { fun fetchData(tvModel: TVViewModel) {
@@ -274,32 +207,7 @@ class Request {
} }
} }
inner class TokenRunnable : Runnable { inner class MyRunnable(private val tvModel: TVViewModel) : Runnable {
override fun run() {
fetchToken()
handler.postDelayed(this, 600000)
}
}
fun fetchToken() {
yspTokenService.getInfo()
.enqueue(object : Callback<Info> {
override fun onResponse(call: Call<Info>, response: Response<Info>) {
if (response.isSuccessful) {
token = response.body()?.data?.token!!
Log.i(TAG, "info success $token")
} else {
Log.e(TAG, "token status error")
}
}
override fun onFailure(call: Call<Info>, t: Throwable) {
Log.e(TAG, "token request error $t")
}
})
}
inner class BtraceRunnable(private val tvModel: TVViewModel) : Runnable {
override fun run() { override fun run() {
fetchBtrace(tvModel) fetchBtrace(tvModel)
handler.postDelayed(this, 60000) handler.postDelayed(this, 60000)
@@ -396,10 +304,8 @@ class Request {
val program = response.body() val program = response.body()
if (program != null) { if (program != null) {
tvViewModel.addProgram(program.dataListList) tvViewModel.addProgram(program.dataListList)
Log.d(TAG, "$title program ${program.dataListList.size}") Log.i(TAG, "$title program ${program.dataListList.size}")
} }
} else {
Log.w(TAG, "$title program error")
} }
} }
@@ -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"),
@@ -277,6 +277,9 @@ CGTN 纪录频道,https://livedoc.cgtn.com/500d/prog_index.m3u8
} }
if (!i.contains(",")) { if (!i.contains(",")) {
channel = i.trim() channel = i.trim()
if (channel == "移动专区") {
break
}
continue continue
} }
val p = i.split(",") val p = i.split(",")
@@ -51,14 +51,14 @@ class YSP(var context: Context) {
private var signature = "" private var signature = ""
private var encryptor: Encryptor? = null private var encryptor: Encryptor? = null
private lateinit var sharedPref: SharedPreferences private var sharedPref: SharedPreferences? = null
init { init {
if (context is MainActivity) { if (context is MainActivity) {
encryptor = Encryptor() encryptor = Encryptor()
encryptor!!.init(context) encryptor!!.init(context)
sharedPref = (context as MainActivity).sharedPref sharedPref = (context as MainActivity).getPreferences(Context.MODE_PRIVATE)
} }
guid = getGuid() guid = getGuid()
@@ -77,10 +77,15 @@ class YSP(var context: Context) {
timeStr = getTimeStr() timeStr = getTimeStr()
// guid = "lq3oqitm_1e15dnzgjnb"
// randStr = "BfcCPQp8Hq"
// timeStr = "1702166501"
cKey = cKey =
encryptor!!.encrypt(cnlid, timeStr, appVer, guid, platform) encryptor!!.encrypt(cnlid, timeStr, appVer, guid, platform)
signature = getSignature() signature = getSignature()
return """{"cnlid":"$cnlid","livepid":"$livepid","stream":"$stream","guid":"$guid","cKey":"$cKey","adjust":$adjust,"sphttps":"$sphttps","platform":"$platform","cmd":"$cmd","encryptVer":"$encryptVer","dtype":"$dtype","devid":"$devid","otype":"$otype","appVer":"$appVer","app_version":"$appVersion","rand_str":"$randStr","channel":"$channel","defn":"$defn","signature":"$signature"}""" return """{"cnlid":"$cnlid","livepid":"$livepid","stream":"$stream","guid":"$guid","cKey":"$cKey","adjust":$adjust,"sphttps":"$sphttps","platform":"$platform","cmd":"$cmd","encryptVer":"$encryptVer","dtype":"$dtype","devid":"$devid","otype":"$otype","appVer":"$appVer","app_version":"$appVersion","rand_str":"$randStr","channel":"$channel","defn":"$defn","signature":"$signature"}"""
// return """{"cnlid":"2000203803","livepid":"600001801","stream":"2","guid":"lq1y36mb_ccfwmja9zan","cKey":"--01A9F5E89BB86A0C61F4025BEDE15309B6913A79FD1AF1EE7F5EC9C7605F377D1D2281488385C32DEB9E7D0DD3559CB700BD7AF44DD5C9DE0AE14D94B8214027B5D664C108AEE23532348DCC61B86F7C8FBB6CF14D588E6093A25E97DF6D66F4882AB28F17016472DD43D45EF076B7F505176A5E8DEDF2662E5F9AB12B69CB20BCE1579BE724091F3AF6826AE34B713906F3FE139C3783F80EECBD08416DC525E1","adjust":1,"sphttps":"1","platform":"5910204","cmd":"2","encryptVer":"8.1","dtype":"1","devid":"devid","otype":"ojson","appVer":"V1.0.0","app_version":"V1.0.0","rand_str":"IOS2soOw44","channel":"ysp_tx","defn":"fhd","signature":"6ca945f651817de8c6e6910457ceafd6"}"""
} }
private fun getTimeStr(): String { private fun getTimeStr(): String {
@@ -94,10 +99,10 @@ class YSP(var context: Context) {
} }
fun getGuid(): String { fun getGuid(): String {
var guid = sharedPref.getString("guid", "") var guid = sharedPref?.getString("guid", "")
if (guid == null || guid.length < 18) { if (guid == null || guid.length < 18) {
guid = generateGuid() guid = generateGuid()
with(sharedPref.edit()) { with(sharedPref!!.edit()) {
putString("guid", guid) putString("guid", guid)
apply() apply()
} }
@@ -107,7 +112,7 @@ class YSP(var context: Context) {
private fun newGuid(): String { private fun newGuid(): String {
guid = generateGuid() guid = generateGuid()
with(sharedPref.edit()) { with(sharedPref!!.edit()) {
putString("guid", guid) putString("guid", guid)
apply() apply()
} }
@@ -8,9 +8,7 @@ class TVListViewModel : ViewModel() {
var maxNum = mutableListOf<Int>() var maxNum = mutableListOf<Int>()
private val _tvListViewModel = MutableLiveData<MutableList<TVViewModel>>() private val tvListViewModel = MutableLiveData<MutableList<TVViewModel>>()
val tvListViewModel: LiveData<MutableList<TVViewModel>>
get() = _tvListViewModel
private val _itemPosition = MutableLiveData<Int>() private val _itemPosition = MutableLiveData<Int>()
val itemPosition: LiveData<Int> val itemPosition: LiveData<Int>
@@ -20,20 +18,22 @@ class TVListViewModel : ViewModel() {
val itemPositionCurrent: LiveData<Int> val itemPositionCurrent: LiveData<Int>
get() = _itemPositionCurrent get() = _itemPositionCurrent
fun getTVListViewModel(): MutableLiveData<MutableList<TVViewModel>> {
return tvListViewModel
}
fun addTVViewModel(tvViewModel: TVViewModel) { fun addTVViewModel(tvViewModel: TVViewModel) {
if (_tvListViewModel.value == null) { val currentTVModelList = tvListViewModel.value ?: mutableListOf()
_tvListViewModel.value = mutableListOf(tvViewModel) currentTVModelList.add(tvViewModel)
} else { tvListViewModel.value = currentTVModelList
_tvListViewModel.value?.add(tvViewModel)
}
} }
fun getTVViewModel(id: Int): TVViewModel? { fun getTVViewModel(id: Int): TVViewModel? {
return _tvListViewModel.value?.get(id) return tvListViewModel.value?.get(id)
} }
fun getTVViewModelCurrent(): TVViewModel? { fun getTVViewModelCurrent(): TVViewModel? {
return _itemPositionCurrent.value?.let { _tvListViewModel.value?.get(it) } return _itemPositionCurrent.value?.let { tvListViewModel.value?.get(it) }
} }
fun setItemPosition(position: Int) { fun setItemPosition(position: Int) {
@@ -46,9 +46,6 @@ class TVListViewModel : ViewModel() {
} }
fun size(): Int { fun size(): Int {
if (_tvListViewModel.value == null) { return tvListViewModel.value!!.size
return 0
}
return _tvListViewModel.value!!.size
} }
} }
@@ -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,66 +77,7 @@ 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 超高清",
"CCTV2 财经",
"CCTV5 体育",
"CCTV5+ 体育赛事",
"CCTV7 国防军事",
"CCTV9 记录",
"CCTV10 科教",
"CCTV11 戏曲",
"CCTV12 社会与法",
"CCTV14 少儿",
"CCTV15 音乐",
"CCTV16 奥林匹克",
"CCTV17 农业农村",
"CCTV3 综艺",
"CCTV6 电影",
"CCTV8 电视剧",
"风云剧场",
"第一剧场",
"怀旧剧场",
"世界地理",
"风云音乐",
"兵器科技",
"风云足球",
"高尔夫网球",
"女性时尚",
"央视文化精品",
"央视台球",
"电视指南",
"卫生健康",
"东方卫视",
"湖南卫视",
"湖北卫视",
"辽宁卫视",
"江苏卫视",
"江西卫视",
"山东卫视",
"广东卫视",
"广西卫视",
"重庆卫视",
"河南卫视",
"河北卫视",
"贵州卫视",
"北京卫视",
"黑龙江卫视",
"浙江卫视",
"安徽卫视",
"深圳卫视",
"四川卫视",
"东南卫视",
"海南卫视",
"天津卫视",
"新疆卫视",
)
private val channelsMustToken = arrayOf(
"CCTV3 综艺", "CCTV3 综艺",
"CCTV6 电影", "CCTV6 电影",
"CCTV8 电视剧", "CCTV8 电视剧",
@@ -211,9 +150,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>
-9
View File
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>