Compare commits

...
3 Commits
Author SHA1 Message Date
Li ZongYing 0f4a33a3fc change channel order 2024-01-25 11:37:06 +08:00
Li ZongYing c17d62c698 rename to setting 2024-01-19 13:49:43 +08:00
Li ZongYing 33d3da97e5 fix 13 2024-01-18 22:19:04 +08:00
11 changed files with 181 additions and 153 deletions
+5
View File
@@ -14,6 +14,11 @@
## 更新日志 ## 更新日志
### v1.4.7(高版本专用)
* 修复部分用户cctv13播放过程中卡住的问题
* 调整cctv的频道顺序
### v1.4.5(高版本专用) ### v1.4.5(高版本专用)
* 数字选台配置 * 数字选台配置
@@ -22,6 +22,7 @@ class ChannelFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
_binding = ChannelBinding.inflate(inflater, container, false) _binding = ChannelBinding.inflate(inflater, container, false)
_binding!!.root.visibility = View.GONE
(activity as MainActivity).fragmentReady() (activity as MainActivity).fragmentReady()
return binding.root return binding.root
} }
@@ -41,7 +42,6 @@ class ChannelFragment : Fragment() {
handler.postDelayed(removeRunnable, delay) handler.postDelayed(removeRunnable, delay)
} else { } else {
val ch = "${binding.channelContent.text}$channel".toInt() val ch = "${binding.channelContent.text}$channel".toInt()
Log.i(TAG, "channel $ch")
(activity as MainActivity).play(ch) (activity as MainActivity).play(ch)
binding.channelContent.text = "" binding.channelContent.text = ""
view?.visibility = View.GONE view?.visibility = View.GONE
@@ -23,17 +23,11 @@ class InfoFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
_binding = InfoBinding.inflate(inflater, container, false) _binding = InfoBinding.inflate(inflater, container, false)
_binding!!.root.visibility = View.GONE
(activity as MainActivity).fragmentReady() (activity as MainActivity).fragmentReady()
return binding.root return binding.root
} }
override fun onResume() {
super.onResume()
// Use a Handler to delay the fragment transaction
handler.postDelayed(removeRunnable, delay)
}
fun show(tvViewModel: TVViewModel) { fun show(tvViewModel: TVViewModel) {
binding.textView.text = tvViewModel.title.value binding.textView.text = tvViewModel.title.value
if (tvViewModel.title.value == "CCTV8K 超高清") { if (tvViewModel.title.value == "CCTV8K 超高清") {
@@ -56,10 +50,13 @@ class InfoFragment : Fragment() {
handler.postDelayed(removeRunnable, delay) handler.postDelayed(removeRunnable, delay)
} }
override fun onResume() {
super.onResume()
handler.postDelayed(removeRunnable, delay)
}
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
Log.i(TAG, "onPause")
// Cancel the delayed task when the fragment is paused
handler.removeCallbacks(removeRunnable) handler.removeCallbacks(removeRunnable)
} }
@@ -28,6 +28,7 @@ class MainActivity : FragmentActivity() {
private val mainFragment = MainFragment() private val mainFragment = MainFragment()
private val infoFragment = InfoFragment() private val infoFragment = InfoFragment()
private val channelFragment = ChannelFragment() private val channelFragment = ChannelFragment()
private lateinit var settingFragment: SettingFragment
private var doubleBackToExitPressedOnce = false private var doubleBackToExitPressedOnce = false
@@ -42,7 +43,6 @@ class MainActivity : FragmentActivity() {
private var channelNum = true private var channelNum = true
private var versionName = "" private var versionName = ""
private lateinit var dialogFragment: MyDialogFragment
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -58,8 +58,7 @@ class MainActivity : FragmentActivity() {
.add(R.id.main_browse_fragment, infoFragment) .add(R.id.main_browse_fragment, infoFragment)
.add(R.id.main_browse_fragment, channelFragment) .add(R.id.main_browse_fragment, channelFragment)
.add(R.id.main_browse_fragment, mainFragment) .add(R.id.main_browse_fragment, mainFragment)
.hide(infoFragment) .hide(mainFragment)
.hide(channelFragment)
.commit() .commit()
mainFragment.view?.requestFocus() mainFragment.view?.requestFocus()
} }
@@ -70,7 +69,7 @@ class MainActivity : FragmentActivity() {
channelNum = sharedPref.getBoolean(CHANNEL_NUM, channelNum) channelNum = sharedPref.getBoolean(CHANNEL_NUM, channelNum)
versionName = getPackageInfo().versionName versionName = getPackageInfo().versionName
dialogFragment = MyDialogFragment(versionName, channelReversal, channelNum) settingFragment = SettingFragment(versionName, channelReversal, channelNum)
} }
fun showInfoFragment(tvViewModel: TVViewModel) { fun showInfoFragment(tvViewModel: TVViewModel) {
@@ -80,6 +79,20 @@ class MainActivity : FragmentActivity() {
} }
} }
private fun showChannel(channel: String) {
if (!mainFragment.isHidden) {
return
}
if (settingFragment.isVisible) {
return
}
if (channelNum) {
channelFragment.show(channel)
}
}
fun play(tvViewModel: TVViewModel) { fun play(tvViewModel: TVViewModel) {
playerFragment.play(tvViewModel) playerFragment.play(tvViewModel)
mainFragment.view?.requestFocus() mainFragment.view?.requestFocus()
@@ -214,20 +227,20 @@ class MainActivity : FragmentActivity() {
return return
} }
Log.i(TAG, "dialogFragment ${dialogFragment.isVisible}") Log.i(TAG, "settingFragment ${settingFragment.isVisible}")
if (!dialogFragment.isVisible) { if (!settingFragment.isVisible) {
dialogFragment.show(supportFragmentManager, "settings_dialog") settingFragment.show(supportFragmentManager, "setting")
handler.removeCallbacks(hideHelp) handler.removeCallbacks(hideHelp)
handler.postDelayed(hideHelp, delayHideHelp) handler.postDelayed(hideHelp, delayHideHelp)
} else { } else {
handler.removeCallbacks(hideHelp) handler.removeCallbacks(hideHelp)
dialogFragment.dismiss() settingFragment.dismiss()
} }
} }
private val hideHelp = Runnable { private val hideHelp = Runnable {
if (dialogFragment.isVisible) { if (settingFragment.isVisible) {
dialogFragment.dismiss() settingFragment.dismiss()
} }
} }
@@ -283,20 +296,6 @@ class MainActivity : FragmentActivity() {
}, 2000) }, 2000)
} }
private fun showChannel(channel: String) {
if (!mainFragment.isHidden) {
return
}
if (dialogFragment.isVisible) {
return
}
if (channelNum) {
channelFragment.show(channel)
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
when (keyCode) { when (keyCode) {
KeyEvent.KEYCODE_0 -> { KeyEvent.KEYCODE_0 -> {
@@ -25,19 +25,19 @@ 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 : BrowseSupportFragment() {
var itemPosition: Int = 0
private var request: Request = Request() private var itemPosition = 0
private var rowsAdapter: ArrayObjectAdapter? = null private var rowsAdapter: ArrayObjectAdapter? = null
private var request = Request()
var tvListViewModel = TVListViewModel() var tvListViewModel = TVListViewModel()
private var sharedPref: SharedPreferences? = null private var sharedPref: SharedPreferences? = null
private var lastVideoUrl: String = "" private var lastVideoUrl = ""
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private lateinit var mUpdateProgramRunnable: UpdateProgramRunnable private lateinit var mUpdateProgramRunnable: UpdateProgramRunnable
@@ -53,7 +53,6 @@ class MainFragment : BrowseSupportFragment() {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
activity?.let { request.initYSP(it) } activity?.let { request.initYSP(it) }
sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) sharedPref = activity?.getPreferences(Context.MODE_PRIVATE)
loadRows() loadRows()
@@ -100,7 +99,6 @@ class MainFragment : BrowseSupportFragment() {
if (check(tvViewModel)) { if (check(tvViewModel)) {
(activity as? MainActivity)?.play(tvViewModel) (activity as? MainActivity)?.play(tvViewModel)
(activity as? MainActivity)?.showInfoFragment(tvViewModel) (activity as? MainActivity)?.showInfoFragment(tvViewModel)
setSelectedPosition( setSelectedPosition(
tvViewModel.getRowPosition(), true, tvViewModel.getRowPosition(), true,
SelectItemViewHolderTask(tvViewModel.getItemPosition()) SelectItemViewHolderTask(tvViewModel.getItemPosition())
@@ -114,58 +112,6 @@ class MainFragment : BrowseSupportFragment() {
fragmentReady() fragmentReady()
} }
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacks(mUpdateProgramRunnable)
with(sharedPref!!.edit()) {
putInt("position", itemPosition)
apply()
}
}
fun updateProgram(tvViewModel: TVViewModel) {
val timestamp = getDateTimestamp()
if (timestamp - tvViewModel.programUpdateTime > 60) {
if (tvViewModel.program.value!!.isEmpty()) {
tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel)
} else {
if (timestamp - tvViewModel.program.value!!.last().et < 600) {
tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel)
}
}
}
}
inner class UpdateProgramRunnable : Runnable {
override fun run() {
tvListViewModel.getTVListViewModel().value?.filter { it.programId.value != null }
?.forEach { tvViewModel ->
updateProgram(
tvViewModel
)
}
handler.postDelayed(this, 60000)
}
}
fun check(tvViewModel: TVViewModel): Boolean {
val title = tvViewModel.title.value
val videoUrl = tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
if (videoUrl == null || videoUrl == "") {
Log.e(TAG, "$title videoUrl is empty")
return false
}
if (videoUrl == lastVideoUrl) {
Log.e(TAG, "$title videoUrl is duplication")
return false
}
return true
}
fun toLastPosition() { fun toLastPosition() {
setSelectedPosition( setSelectedPosition(
selectedPosition, false, selectedPosition, false,
@@ -206,57 +152,13 @@ class MainFragment : BrowseSupportFragment() {
adapter = rowsAdapter adapter = rowsAdapter
itemPosition = sharedPref?.getInt("position", 0)!! itemPosition = sharedPref?.getInt(POSITION, 0)!!
if (itemPosition >= tvListViewModel.size()) { if (itemPosition >= tvListViewModel.size()) {
itemPosition = 0 itemPosition = 0
} }
tvListViewModel.setItemPosition(itemPosition) tvListViewModel.setItemPosition(itemPosition)
} }
fun fragmentReady() {
ready++
Log.i(TAG, "ready $ready")
if (ready == 4) {
// request.fetchPage()
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
tvViewModel?.changed()
(activity as? MainActivity)?.switchMainFragment()
}
}
fun play(itemPosition: Int) {
view?.post {
if (itemPosition < tvListViewModel.size()) {
this.itemPosition = itemPosition
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
}
}
fun prev() {
view?.post {
itemPosition--
if (itemPosition == -1) {
itemPosition = tvListViewModel.size() - 1
}
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
}
fun next() {
view?.post {
itemPosition++
if (itemPosition == tvListViewModel.size()) {
itemPosition = 0
}
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
}
fun prevSource() { fun prevSource() {
view?.post { view?.post {
val tvViewModel = tvListViewModel.getTVViewModel(itemPosition) val tvViewModel = tvListViewModel.getTVViewModel(itemPosition)
@@ -322,12 +224,113 @@ class MainFragment : BrowseSupportFragment() {
} }
} }
fun check(tvViewModel: TVViewModel): Boolean {
val title = tvViewModel.title.value
val videoUrl = tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
if (videoUrl == null || videoUrl == "") {
Log.e(TAG, "$title videoUrl is empty")
return false
}
if (videoUrl == lastVideoUrl) {
Log.e(TAG, "$title videoUrl is duplication")
return false
}
return true
}
fun fragmentReady() {
ready++
Log.i(TAG, "ready $ready")
if (ready == 4) {
// request.fetchPage()
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
}
fun play(itemPosition: Int) {
view?.post {
if (itemPosition < tvListViewModel.size()) {
this.itemPosition = itemPosition
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
}
}
fun prev() {
view?.post {
itemPosition--
if (itemPosition == -1) {
itemPosition = tvListViewModel.size() - 1
}
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
}
fun next() {
view?.post {
itemPosition++
if (itemPosition == tvListViewModel.size()) {
itemPosition = 0
}
tvListViewModel.setItemPosition(itemPosition)
tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
}
fun updateProgram(tvViewModel: TVViewModel) {
val timestamp = getDateTimestamp()
if (timestamp - tvViewModel.programUpdateTime > 60) {
if (tvViewModel.program.value!!.isEmpty()) {
tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel)
} else {
if (timestamp - tvViewModel.program.value!!.last().et < 600) {
tvViewModel.programUpdateTime = timestamp
request.fetchProgram(tvViewModel)
}
}
}
}
inner class UpdateProgramRunnable : Runnable {
override fun run() {
tvListViewModel.getTVListViewModel().value?.filter { it.programId.value != null }
?.forEach { tvViewModel ->
updateProgram(
tvViewModel
)
}
handler.postDelayed(this, 60000)
}
}
override fun onStop() {
Log.i(TAG, "onStop")
super.onStop()
with(sharedPref!!.edit()) {
putInt(POSITION, itemPosition)
apply()
}
Log.i(TAG, "POSITION saved")
}
override fun onDestroy() {
Log.i(TAG, "onDestroy")
super.onDestroy()
handler.removeCallbacks(mUpdateProgramRunnable)
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
view!!.requestFocus() view?.post { view?.requestFocus() }
} }
companion object { companion object {
private const val TAG = "MainFragment" private const val TAG = "MainFragment"
private const val POSITION = "position"
} }
} }
@@ -74,10 +74,9 @@ 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 = val videoUrlCurrent = tvViewModel.getVideoUrlCurrent()
tvViewModel.videoIndex.value?.let { tvViewModel.videoUrl.value?.get(it) }
playerView?.player?.run { playerView?.player?.run {
videoUrlCurrent?.let { setMediaItem(MediaItem.fromUri(it)) } setMediaItem(MediaItem.fromUri(videoUrlCurrent))
prepare() prepare()
} }
} }
@@ -8,9 +8,9 @@ import androidx.fragment.app.DialogFragment
import com.lizongying.mytv.databinding.DialogBinding import com.lizongying.mytv.databinding.DialogBinding
class MyDialogFragment(private val versionName: String, class SettingFragment(private val versionName: String,
private val channelReversal: Boolean, private val channelReversal: Boolean,
private val channelNum: Boolean, private val channelNum: Boolean,
) : ) :
DialogFragment() { DialogFragment() {
@@ -52,7 +52,7 @@ class MyDialogFragment(private val versionName: String,
} }
companion object { companion object {
const val TAG = "MyDialogFragment" const val TAG = "SettingFragment"
} }
} }
@@ -145,7 +145,7 @@ object TVList {
"CCTV10 科教" to arrayOf("600001805", "2000203503"), "CCTV10 科教" to arrayOf("600001805", "2000203503"),
"CCTV11 戏曲" to arrayOf("600001806", "2000204103"), "CCTV11 戏曲" to arrayOf("600001806", "2000204103"),
"CCTV12 社会与法" to arrayOf("600001807", "2000202603"), "CCTV12 社会与法" to arrayOf("600001807", "2000202603"),
// "CCTV13 新闻" to arrayOf("600001811","2000204603"), "CCTV13 新闻" to arrayOf("600001811","2000204603"),
"CCTV14 少儿" to arrayOf("600001809", "2000204403"), "CCTV14 少儿" to arrayOf("600001809", "2000204403"),
"CCTV15 音乐" to arrayOf("600001815", "2000205003"), "CCTV15 音乐" to arrayOf("600001815", "2000205003"),
"CCTV16 奥林匹克" to arrayOf("600098637", "2012375003"), "CCTV16 奥林匹克" to arrayOf("600098637", "2012375003"),
@@ -196,8 +196,6 @@ object TVList {
private fun setupTV(): Map<String, List<TV>> { private fun setupTV(): Map<String, List<TV>> {
val tvs = """ val tvs = """
央视频道 央视频道
CCTV4K 超高清,
CCTV8K 超高清,
CCTV1 综合,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226231/index.m3u8 CCTV1 综合,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226231/index.m3u8
CCTV2 财经,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226195/index.m3u8 CCTV2 财经,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226195/index.m3u8
CCTV3 综艺,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226397/index.m3u8 CCTV3 综艺,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226397/index.m3u8
@@ -216,6 +214,8 @@ CCTV14 少儿,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226193/inde
CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8 CCTV15 音乐,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221225785/index.m3u8
CCTV16 奥林匹克,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8 CCTV16 奥林匹克,http://39.134.24.162/dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8;http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226921/index.m3u8
CCTV17 农业农村,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226198/index.m3u8 CCTV17 农业农村,http://dbiptv.sn.chinamobile.com/PLTV/88888890/224/3221226198/index.m3u8
CCTV4K 超高清,
CCTV8K 超高清,
风云剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8 风云剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226950/index.m3u8
第一剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226959/index.m3u8 第一剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226959/index.m3u8
怀旧剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226972/index.m3u8 怀旧剧场,http://dbiptv.sn.chinamobile.com/PLTV/88888893/224/3221226972/index.m3u8
@@ -1,5 +1,7 @@
package com.lizongying.mytv package com.lizongying.mytv
import android.content.res.Resources
import android.util.TypedValue
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@@ -12,4 +14,20 @@ object Utils {
fun getDateTimestamp(): Long { fun getDateTimestamp(): Long {
return Date().time / 1000 return Date().time / 1000
} }
fun dpToPx(dp: Float): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
Resources.getSystem().displayMetrics
).toInt()
}
fun dpToPx(dp: Int): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
Resources.getSystem().displayMetrics
).toInt()
}
} }
@@ -20,7 +20,7 @@ class TVViewModel(private var tv: TV) : ViewModel() {
private var itemPosition: Int = 0 private var itemPosition: Int = 0
var retryTimes: Int = 0 var retryTimes: Int = 0
var retryMaxTimes: Int = 5 var retryMaxTimes: Int = 8
var programUpdateTime: Long = 0 var programUpdateTime: Long = 0
private val _errInfo = MutableLiveData<String>() private val _errInfo = MutableLiveData<String>()
@@ -222,18 +222,25 @@ class TVViewModel(private var tv: TV) : ViewModel() {
mMinimumLoadableRetryCount = minimumLoadableRetryCount mMinimumLoadableRetryCount = minimumLoadableRetryCount
} }
/**
* (playerView?.player as ExoPlayer).setMediaSource(tvViewModel.buildSource())
*/
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
fun buildSource(videoUrl: String, mHeaders: Map<String, String>?): HlsMediaSource { fun buildSource(): HlsMediaSource {
val httpDataSource = DefaultHttpDataSource.Factory() val httpDataSource = DefaultHttpDataSource.Factory()
mHeaders?.let { httpDataSource.setDefaultRequestProperties(it) } mHeaders?.let { httpDataSource.setDefaultRequestProperties(it) }
return HlsMediaSource.Factory(httpDataSource).createMediaSource( return HlsMediaSource.Factory(httpDataSource).createMediaSource(
MediaItem.fromUri( MediaItem.fromUri(
Uri.parse(videoUrl) Uri.parse(getVideoUrlCurrent())
) )
) )
} }
fun getVideoUrlCurrent(): String {
return _videoUrl.value!![_videoIndex.value!!]
}
companion object { companion object {
private const val TAG = "TVViewModel" private const val TAG = "TVViewModel"
} }
+2 -2
View File
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '8.2.0' apply false id 'com.android.application' version '8.2.1' apply false
id 'com.android.library' version '8.2.0' apply false id 'com.android.library' version '8.2.1' apply false
id 'org.jetbrains.kotlin.android' version '1.9.21' apply false id 'org.jetbrains.kotlin.android' version '1.9.21' apply false
} }