feat: 暂停显示选集、亮度音量手势调节、播放进度续播
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
package com.videoapp.tv
|
||||
|
||||
import android.content.Context
|
||||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
@@ -10,10 +13,12 @@ import android.widget.Button
|
||||
import android.widget.HorizontalScrollView
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.ui.PlayerView
|
||||
import com.videoapp.tv.data.AppDatabase
|
||||
@@ -33,29 +38,49 @@ class PlayerActivity : AppCompatActivity() {
|
||||
private lateinit var episodeList: LinearLayout
|
||||
private lateinit var episodeScroll: HorizontalScrollView
|
||||
private lateinit var loadingIndicator: View
|
||||
private lateinit var errorText: android.widget.TextView
|
||||
private lateinit var errorText: TextView
|
||||
private lateinit var btnClose: ImageButton
|
||||
private lateinit var brightnessVolumeIndicator: TextView
|
||||
|
||||
private var exoPlayer: ExoPlayer? = null
|
||||
private val videoExtractor = VideoExtractor()
|
||||
private val configRepo by lazy { ConfigRepository(this) }
|
||||
private val playHistoryDao by lazy { AppDatabase.getInstance(this).playHistoryDao() }
|
||||
private val audioManager by lazy { getSystemService(Context.AUDIO_SERVICE) as AudioManager }
|
||||
|
||||
private var sources: List<PlaySource> = emptyList()
|
||||
private var currentSourceIndex = 0
|
||||
private var currentEpisode: Episode? = null
|
||||
private var historyEpisode: String? = null
|
||||
private var resumePosition: Long? = null
|
||||
private var currentPlayHistoryId: Long? = null
|
||||
|
||||
private val hideHandler = Handler(Looper.getMainLooper())
|
||||
private val hideRunnable = Runnable { hideControls() }
|
||||
private var controlsVisible = true
|
||||
|
||||
// 用于保存播放历史的信息
|
||||
private val positionSaveHandler = Handler(Looper.getMainLooper())
|
||||
private val positionSaveRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
saveCurrentPosition()
|
||||
positionSaveHandler.postDelayed(this, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
private var videoTitle: String = ""
|
||||
private var videoCategory: String? = null
|
||||
private var coverUrl: String? = null
|
||||
private var detailUrl: String = ""
|
||||
|
||||
private var touchStartY = 0f
|
||||
private var touchStartX = 0f
|
||||
private var isAdjusting = false
|
||||
private var isBrightnessMode = false
|
||||
private var startBrightness = 0f
|
||||
private var startVolume = 0
|
||||
private var maxVolume = 0
|
||||
private val indicatorHideHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_player)
|
||||
@@ -69,12 +94,16 @@ class PlayerActivity : AppCompatActivity() {
|
||||
loadingIndicator = findViewById(R.id.loading_indicator)
|
||||
errorText = findViewById(R.id.error_text)
|
||||
btnClose = findViewById(R.id.btn_close)
|
||||
brightnessVolumeIndicator = findViewById(R.id.brightness_volume_indicator)
|
||||
|
||||
detailUrl = intent.getStringExtra("detail_url") ?: ""
|
||||
videoTitle = intent.getStringExtra("title") ?: ""
|
||||
videoCategory = intent.getStringExtra("category")
|
||||
coverUrl = intent.getStringExtra("cover_url")
|
||||
historyEpisode = intent.getStringExtra("history_episode")
|
||||
resumePosition = if (intent.hasExtra("resume_position")) intent.getLongExtra("resume_position", 0) else null
|
||||
|
||||
maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
|
||||
btnClose.setOnClickListener { finish() }
|
||||
|
||||
@@ -86,6 +115,16 @@ class PlayerActivity : AppCompatActivity() {
|
||||
private fun initExoPlayer() {
|
||||
exoPlayer = ExoPlayer.Builder(this).build().also { player ->
|
||||
playerView.player = player
|
||||
player.addListener(object : Player.Listener {
|
||||
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
|
||||
if (playWhenReady) {
|
||||
hideHandler.postDelayed(hideRunnable, 4000)
|
||||
} else {
|
||||
hideHandler.removeCallbacks(hideRunnable)
|
||||
showControls()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +232,6 @@ class PlayerActivity : AppCompatActivity() {
|
||||
showLoading(true)
|
||||
val config = configRepo.getConfig()
|
||||
|
||||
// 保存播放历史
|
||||
savePlayHistory(ep.title)
|
||||
|
||||
lifecycleScope.launch {
|
||||
@@ -212,13 +250,11 @@ class PlayerActivity : AppCompatActivity() {
|
||||
private fun savePlayHistory(episodeName: String?) {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
// 检查是否已存在相同的播放记录
|
||||
val existing = playHistoryDao.findByDetailUrl(detailUrl)
|
||||
if (existing != null) {
|
||||
// 更新现有记录的播放时间和剧集
|
||||
playHistoryDao.updatePlayTime(existing.id, System.currentTimeMillis(), episodeName)
|
||||
currentPlayHistoryId = existing.id
|
||||
} else {
|
||||
// 插入新记录
|
||||
val playHistory = PlayHistory(
|
||||
title = videoTitle,
|
||||
episodeName = episodeName,
|
||||
@@ -227,19 +263,43 @@ class PlayerActivity : AppCompatActivity() {
|
||||
category = videoCategory,
|
||||
playTime = System.currentTimeMillis()
|
||||
)
|
||||
playHistoryDao.insert(playHistory)
|
||||
val id = playHistoryDao.insert(playHistory)
|
||||
currentPlayHistoryId = id
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// 保存播放历史失败不影响播放功能
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveCurrentPosition() {
|
||||
val player = exoPlayer ?: return
|
||||
val historyId = currentPlayHistoryId ?: return
|
||||
if (playerView.visibility != View.VISIBLE) return
|
||||
val pos = player.currentPosition
|
||||
if (pos > 0) {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
playHistoryDao.updatePosition(historyId, pos)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startPositionSaving() {
|
||||
positionSaveHandler.removeCallbacks(positionSaveRunnable)
|
||||
positionSaveHandler.postDelayed(positionSaveRunnable, 5000)
|
||||
}
|
||||
|
||||
private fun stopPositionSaving() {
|
||||
positionSaveHandler.removeCallbacks(positionSaveRunnable)
|
||||
}
|
||||
|
||||
private fun tryPlayDirectly(detailUrl: String, config: com.videoapp.tv.data.SiteConfig) {
|
||||
// 保存播放历史(直接播放时使用标题作为剧集名)
|
||||
savePlayHistory(videoTitle)
|
||||
|
||||
|
||||
lifecycleScope.launch {
|
||||
val (directUrl, iframeUrl) = videoExtractor.extractFromPlayPage(detailUrl, config)
|
||||
if (directUrl != null) {
|
||||
@@ -265,10 +325,17 @@ class PlayerActivity : AppCompatActivity() {
|
||||
setMediaItem(mediaItem)
|
||||
prepare()
|
||||
playWhenReady = true
|
||||
|
||||
if (resumePosition != null && resumePosition!! > 0) {
|
||||
seekTo(resumePosition!!)
|
||||
resumePosition = null
|
||||
}
|
||||
}
|
||||
startPositionSaving()
|
||||
}
|
||||
|
||||
private fun playWithWebView(url: String) {
|
||||
stopPositionSaving()
|
||||
playerView.visibility = View.GONE
|
||||
playerWebView.visibility = View.VISIBLE
|
||||
showLoading(false)
|
||||
@@ -302,9 +369,60 @@ class PlayerActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun setupTouchListeners() {
|
||||
val listener = View.OnClickListener { toggleControls() }
|
||||
playerView.setOnClickListener(listener)
|
||||
playerWebView.setOnClickListener(listener)
|
||||
val touchListener = View.OnTouchListener { view, event ->
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
touchStartX = event.x
|
||||
touchStartY = event.y
|
||||
isAdjusting = false
|
||||
val halfWidth = view.width / 2
|
||||
isBrightnessMode = touchStartX < halfWidth
|
||||
startBrightness = window.attributes.screenBrightness
|
||||
if (startBrightness < 0) startBrightness = 0.5f
|
||||
startVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaY = touchStartY - event.y
|
||||
if (kotlin.math.abs(event.y - touchStartY) > 10f) {
|
||||
isAdjusting = true
|
||||
}
|
||||
if (isAdjusting) {
|
||||
if (isBrightnessMode) {
|
||||
val newBrightness = (startBrightness + deltaY / view.height).coerceIn(0.01f, 1.0f)
|
||||
window.attributes.screenBrightness = newBrightness
|
||||
window.attributes = window.attributes
|
||||
showIndicator("亮度", (newBrightness * 100).toInt())
|
||||
} else {
|
||||
val range = maxVolume.toFloat()
|
||||
val newVolume = (startVolume + (deltaY / view.height) * range).toInt().coerceIn(0, maxVolume)
|
||||
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newVolume, 0)
|
||||
showIndicator("音量", (newVolume * 100 / maxVolume))
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
val movedFar = kotlin.math.abs(event.x - touchStartX) > 20f || kotlin.math.abs(event.y - touchStartY) > 20f
|
||||
if (!isAdjusting && !movedFar) {
|
||||
toggleControls()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
playerView.setOnTouchListener(touchListener)
|
||||
playerWebView.setOnTouchListener(touchListener)
|
||||
}
|
||||
|
||||
private fun showIndicator(label: String, percent: Int) {
|
||||
brightnessVolumeIndicator.text = "$label: $percent%"
|
||||
brightnessVolumeIndicator.visibility = View.VISIBLE
|
||||
indicatorHideHandler.removeCallbacksAndMessages(null)
|
||||
indicatorHideHandler.postDelayed({
|
||||
brightnessVolumeIndicator.visibility = View.GONE
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
private fun toggleControls() {
|
||||
@@ -338,11 +456,15 @@ class PlayerActivity : AppCompatActivity() {
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
saveCurrentPosition()
|
||||
stopPositionSaving()
|
||||
exoPlayer?.playWhenReady = false
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
saveCurrentPosition()
|
||||
stopPositionSaving()
|
||||
hideHandler.removeCallbacks(hideRunnable)
|
||||
exoPlayer?.release()
|
||||
exoPlayer = null
|
||||
|
||||
Reference in New Issue
Block a user