diff --git a/README.md b/README.md index ca1289c..795eef8 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,12 @@ ## 更新日志 +### v1.0.7 + +* 支持rtsp直播 +* 支持循环播放 +* 支持txt/m3u视频源 + ### v1.0.6 * 修复视频可能无声音的问题 @@ -88,11 +94,8 @@ adb install my-tv-0.apk * 音量不同 * 收藏夹 -* 自定义源 * 节目增加预告 * 频道列表优化 -* 更新的视频源不会覆盖已有的节目源 -* 更新后自动播放 * 节目列表焦点消失的问题 ## 赞赏 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 68115f7..d400d2f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -74,6 +74,8 @@ dependencies { // For HLS playback support with ExoPlayer implementation("androidx.media3:media3-exoplayer-hls:$media3Version") + implementation("androidx.media3:media3-exoplayer-rtsp:$media3Version") + implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.leanback:leanback:1.0.0") implementation("com.github.bumptech.glide:glide:4.11.0") @@ -94,4 +96,6 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC") implementation(files("libs/lib-decoder-ffmpeg-release.aar")) + + implementation("io.github.lizongying:gua64:1.4.3") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 880e77c..2d29dcc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,6 +48,7 @@ + diff --git a/app/src/main/java/com/lizongying/mytv0/MainActivity.kt b/app/src/main/java/com/lizongying/mytv0/MainActivity.kt index 8884559..ec67d72 100644 --- a/app/src/main/java/com/lizongying/mytv0/MainActivity.kt +++ b/app/src/main/java/com/lizongying/mytv0/MainActivity.kt @@ -17,7 +17,6 @@ import android.view.WindowManager import android.widget.Toast import androidx.fragment.app.FragmentActivity import com.lizongying.mytv0.models.TVList -import kotlin.math.abs class MainActivity : FragmentActivity() { @@ -147,25 +146,23 @@ class MainActivity : FragmentActivity() { return super.onFling(e1, e2, velocityX, velocityY) } - override fun onScroll( - e1: MotionEvent?, - e2: MotionEvent, - distanceX: Float, - distanceY: Float - ): Boolean { - // 计算手势滑动的方向和距离 - val deltaY = e1?.y?.let { e2.y.minus(it) } ?: 0f - val deltaX = e1?.x?.let { e2.x.minus(it) } ?: 0f - - // 如果是垂直滑动 - if (abs(deltaY) > abs(deltaX)) { - if ((e1?.x ?: 0f) > windowManager.defaultDisplay.width * 2 / 3) { - adjustVolume(deltaY) // 调整音量 - } - } - - return super.onScroll(e1, e2, distanceX, distanceY) - } +// override fun onScroll( +// e1: MotionEvent?, +// e2: MotionEvent, +// distanceX: Float, +// distanceY: Float +// ): Boolean { +// val deltaY = e1?.y?.let { e2.y.minus(it) } ?: 0f +// val deltaX = e1?.x?.let { e2.x.minus(it) } ?: 0f +// +// if (abs(deltaY) > abs(deltaX)) { +// if ((e1?.x ?: 0f) > windowManager.defaultDisplay.width * 2 / 3) { +// adjustVolume(deltaY) +// } +// } +// +// return super.onScroll(e1, e2, distanceX, distanceY) +// } private fun adjustVolume(deltaY: Float) { val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) @@ -200,7 +197,16 @@ class MainActivity : FragmentActivity() { // // 可以添加一个toast来显示当前亮度 // Toast.makeText(context, "Brightness: $brightness", Toast.LENGTH_SHORT).show() // } + } + fun onPlayEnd() { + val tvModel = TVList.getTVModelCurrent() + if (SP.repeatInfo) { + infoFragment.show(tvModel) + if (SP.channelNum) { + channelFragment.show(tvModel) + } + } } fun play(position: Int) { diff --git a/app/src/main/java/com/lizongying/mytv0/PlayerFragment.kt b/app/src/main/java/com/lizongying/mytv0/PlayerFragment.kt index 50eeebe..b90f028 100644 --- a/app/src/main/java/com/lizongying/mytv0/PlayerFragment.kt +++ b/app/src/main/java/com/lizongying/mytv0/PlayerFragment.kt @@ -14,6 +14,7 @@ import androidx.media3.common.MediaItem import androidx.media3.common.MimeTypes import androidx.media3.common.PlaybackException import androidx.media3.common.Player +import androidx.media3.common.Player.REPEAT_MODE_ALL import androidx.media3.common.VideoSize import androidx.media3.common.util.UnstableApi import androidx.media3.datasource.DataSource @@ -24,6 +25,7 @@ import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.mediacodec.MediaCodecSelector import androidx.media3.exoplayer.mediacodec.MediaCodecUtil +import com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION import com.google.android.exoplayer2.SimpleExoPlayer import com.lizongying.mytv0.databinding.PlayerBinding import com.lizongying.mytv0.models.TVModel @@ -41,6 +43,13 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback { private lateinit var surfaceView: SurfaceView + private lateinit var mainActivity: MainActivity + + override fun onActivityCreated(savedInstanceState: Bundle?) { + mainActivity = activity as MainActivity + super.onActivityCreated(savedInstanceState) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -71,6 +80,7 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback { .build() } playerView.player = player + player?.repeatMode = REPEAT_MODE_ALL player?.playWhenReady = true player?.addListener(object : Player.Listener { override fun onVideoSizeChanged(videoSize: VideoSize) { @@ -87,6 +97,28 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback { } } + override fun onIsPlayingChanged(isPlaying: Boolean) { + Log.i(TAG, "isPlaying $isPlaying") + super.onIsPlayingChanged(isPlaying) + } + + override fun onPlaybackStateChanged(playbackState: Int) { + Log.i(TAG, "playbackState $playbackState") + super.onPlaybackStateChanged(playbackState) + } + + + override fun onPositionDiscontinuity( + oldPosition: Player.PositionInfo, + newPosition: Player.PositionInfo, + reason: Int + ) { + if (reason == DISCONTINUITY_REASON_PERIOD_TRANSITION) { + mainActivity.onPlayEnd() + } + super.onPositionDiscontinuity(oldPosition, newPosition, reason) + } + override fun onPlayerError(error: PlaybackException) { super.onPlayerError(error) tvModel?.setReady() diff --git a/app/src/main/java/com/lizongying/mytv0/SP.kt b/app/src/main/java/com/lizongying/mytv0/SP.kt index 694cd5d..e42d10c 100644 --- a/app/src/main/java/com/lizongying/mytv0/SP.kt +++ b/app/src/main/java/com/lizongying/mytv0/SP.kt @@ -21,6 +21,8 @@ object SP { private const val KEY_POSITION_SUB = "position_sub" + private const val KEY_REPEAT_INFO = "repeat_info" + private lateinit var sp: SharedPreferences /** @@ -56,4 +58,8 @@ object SP { var positionSub: Int get() = sp.getInt(KEY_POSITION_SUB, 0) set(value) = sp.edit().putInt(KEY_POSITION_SUB, value).apply() + + var repeatInfo: Boolean + get() = sp.getBoolean(KEY_REPEAT_INFO, true) + set(value) = sp.edit().putBoolean(KEY_REPEAT_INFO, value).apply() } \ No newline at end of file diff --git a/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt b/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt index 6222950..527ab9d 100644 --- a/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt +++ b/app/src/main/java/com/lizongying/mytv0/SettingFragment.kt @@ -49,6 +49,13 @@ class SettingFragment : Fragment() { (activity as MainActivity).settingActive() } + val switchRepeatInfo = _binding?.switchRepeatInfo + switchRepeatInfo?.isChecked = SP.repeatInfo + switchRepeatInfo?.setOnCheckedChangeListener { _, isChecked -> + SP.repeatInfo = isChecked + (activity as MainActivity).settingActive() + } + val updateManager = UpdateManager(context, this, context.appVersionCode) binding.checkVersion.setOnClickListener { OnClickListenerCheckVersion(updateManager) diff --git a/app/src/main/java/com/lizongying/mytv0/models/TVList.kt b/app/src/main/java/com/lizongying/mytv0/models/TVList.kt index 1887337..f7be32e 100644 --- a/app/src/main/java/com/lizongying/mytv0/models/TVList.kt +++ b/app/src/main/java/com/lizongying/mytv0/models/TVList.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.MutableLiveData import com.google.gson.JsonSyntaxException import com.lizongying.mytv0.R import com.lizongying.mytv0.showToast +import io.github.lizongying.Gua import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -98,8 +99,98 @@ object TVList { } private fun str2List(str: String) { - val type = object : com.google.gson.reflect.TypeToken>() {}.type - list = com.google.gson.Gson().fromJson(str, type) + var string = str + val g = Gua() + if (g.verify(str)) { + string = g.decode(str) + } + if (string.isBlank()) { + return + } + Log.i("string", string) + Log.i("string first", "${string[0]}") + when (string[0]) { + '[' -> { + Log.i("", "1111111") + val type = object : com.google.gson.reflect.TypeToken>() {}.type + list = com.google.gson.Gson().fromJson(string, type) + } + + '#' -> { + Log.i("", "2222222") + val lines = string.lines() + + Log.i("lines", "$lines") + val nameRegex = Regex("""tvg-name="([^"]+)"""") + val logRegex = Regex("""tvg-logo="([^"]+)"""") + val groupRegex = Regex("""group-title="([^"]+)"""") + + val l = mutableListOf() + for ((index, line) in lines.withIndex()) { + val trimmedLine = line.trim() + if (trimmedLine.startsWith("#EXTINF")) { + val info = trimmedLine.split(",") + Log.i("info", "$info") + + val title = info.last() + val name = nameRegex.find(info.first())?.groupValues?.get(1) + val group = groupRegex.find(info.first())?.groupValues?.get(1) + val logo = logRegex.find(info.first())?.groupValues?.get(1) + val uris = + if (index + 1 < lines.size) listOf(lines[index + 1]) else emptyList() + Log.i("info", "$title $name $group $logo $uris") + val tv = TV( + 0, + name!!, + title, + "", + logo!!, + "", + uris, + mapOf(), + group!!, + listOf(), + ) + + l.add(tv) + } + } + list = l + } + + else -> { + Log.i("", "333333") + val lines = string.lines() + var group = "" + val l = mutableListOf() + for (line in lines) { + val trimmedLine = line.trim() + if (trimmedLine.isNotEmpty()) { + if (trimmedLine.contains("#genre#")) { + group = trimmedLine.split(',', limit = 2)[0].trim() + } else { + val arr = trimmedLine.split(',').map { it.trim() } + val tv = TV( + 0, + "", + arr.first(), + "", + "", + "", + arr.drop(1), + mapOf(), + group, + listOf(), + ) + + l.add(tv) + } + } + } + list = l + } + } + Log.i("TVList", "$list") listModel = list.map { tv -> @@ -130,6 +221,10 @@ object TVList { } } + fun getTVModelCurrent(): TVModel { + return getTVModel(position.value!!) + } + fun getTVModel(idx: Int): TVModel { return listModel[idx] } diff --git a/app/src/main/res/layout/setting.xml b/app/src/main/res/layout/setting.xml index 5eddd40..48bddc6 100644 --- a/app/src/main/res/layout/setting.xml +++ b/app/src/main/res/layout/setting.xml @@ -98,5 +98,11 @@ android:layout_marginTop="5dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 97f558d..122ddbf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,4 +15,5 @@ 赞赏 配置地址 确认 + 循环播放时显示信息 \ No newline at end of file