视频组件

This commit is contained in:
2026-06-05 09:33:30 +08:00
parent 88c791a520
commit d60322cbc2
4 changed files with 74 additions and 256 deletions

View File

@@ -78,6 +78,7 @@ class AlarmLogActivity : AppCompatActivity() {
.setShowFullAnimation(true)
.setNeedShowWifiTip(false)
.setEnlargeImageRes(R.drawable.full_size)
.setShrinkImageRes(R.drawable.full_screen)
.setBottomShowProgressBarDrawable(progressDrawable, thumbDrawable)
.setVideoAllCallBack(object : GSYSampleCallBack() {
override fun onPrepared(url: String?, vararg objects: Any?) {
@@ -91,6 +92,15 @@ class AlarmLogActivity : AppCompatActivity() {
}
})
.build(binding.exoPlayContextId)
binding.exoPlayContextId.backButton.setOnClickListener {
if (orientationUtils?.isLand == 1) {
// 当前全屏,先退出全屏
binding.exoPlayContextId.fullscreenButton.performClick()
} else {
finish()
}
}
binding.exoPlayContextId.onControlVisibilityChanged = { show ->
Log.e("GSY_TEST", "Activity收到回调 show=$show")

View File

@@ -1,47 +1,37 @@
package com.example.defenseapplication.liveVideo
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.media.MediaRecorder
import android.media.AudioManager
import android.os.Bundle
import android.util.Log
import android.view.animation.ScaleAnimation
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chuangyuan.ycj.videolibrary.video.ExoUserPlayer
import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.AlarmLogHorizontalAdapter
import com.example.defenseapplication.base.BaseActivity
import com.example.defenseapplication.bean.DeviceAlarmBean
import com.example.defenseapplication.bean.DeviceAlarmResponse
import com.example.defenseapplication.databinding.LiveVideoActivityBinding
import com.example.defenseapplication.utils.ActivityManager
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.MyGSYVideoPlayer
import com.gyf.immersionbar.ImmersionBar
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
import kotlinx.coroutines.launch
import java.io.File
import java.io.IOException
import android.content.res.Configuration
import androidx.activity.OnBackPressedCallback
import chuangyuan.ycj.videolibrary.listener.VideoInfoListener
import chuangyuan.ycj.videolibrary.video.VideoPlayerManager
import android.view.View
import com.bumptech.glide.Glide
import androidx.lifecycle.lifecycleScope
import com.alibaba.sdk.android.emas.f
class LiveVideoActivity : BaseActivity() {
private lateinit var binding: LiveVideoActivityBinding
private lateinit var exoPlayerManager: ExoUserPlayer
private var isVoiceEnabled = false
private var isLightOn = false
private var isRecording = false
private var mediaRecorder: MediaRecorder? = null
private var micPulseAnimation: ScaleAnimation? = null
private var deviceId: String? = null
private var alarmList: MutableList<DeviceAlarmBean> = mutableListOf()
private var alarmAdapter: AlarmLogHorizontalAdapter? = null
@@ -50,18 +40,8 @@ class LiveVideoActivity : BaseActivity() {
private val pageSize = 10
private var hasMore = true
private var isLoading = false
private var isFirstLoad = true
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
startRecordingMode()
} else {
Toast.makeText(this, R.string.recording_permission_needed, Toast.LENGTH_SHORT).show()
isRecording = false
}
}
private var orientationUtils: OrientationUtils? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -83,47 +63,52 @@ class LiveVideoActivity : BaseActivity() {
initAlarmRecyclerView()
initListener()
fetchAlarmList()
// onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
// override fun handleOnBackPressed() {
// if (exoPlayerManager.onBackPressed()) {
// return
// }
// finish()
// }
// })
}
private fun initVideoPlayer() {
val videoPlayerView = binding.exoPlayContextId
val videoUrl = "https://www.w3schools.com/html/movie.mp4"
exoPlayerManager = VideoPlayerManager.Builder(VideoPlayerManager.TYPE_PLAY_GESTURE, videoPlayerView)
.setPlayUri("https://media.w3.org/2010/05/sintel/trailer.mp4")
.setPosition(0)
.addVideoInfoListener(object : VideoInfoListener {
override fun onPlayStart(p0: Long) {
Log.d("LiveVideoActivity", "视频开始播放")
orientationUtils = OrientationUtils(this, binding.exoPlayContextId)
orientationUtils?.isEnable = true
GSYVideoOptionBuilder()
.setUrl(videoUrl)
.setCacheWithPlay(false)
.setRotateViewAuto(true)
.setLockLand(false)
.setShowFullAnimation(true)
.setNeedShowWifiTip(false)
.setEnlargeImageRes(R.drawable.full_size)
.setShrinkImageRes(R.drawable.full_screen)
.setVideoAllCallBack(object : GSYSampleCallBack() {
override fun onPrepared(url: String?, vararg objects: Any?) {
super.onPrepared(url, *objects)
orientationUtils?.isEnable = true
}
override fun onLoadingChanged() {
}
override fun onPlayerError(p0: com.google.android.exoplayer2.ExoPlaybackException?) {
Toast.makeText(this@LiveVideoActivity, "播放失败", Toast.LENGTH_SHORT).show()
}
override fun onPlayEnd() {
Log.d("LiveVideoActivity", "播放结束")
}
override fun isPlaying(p0: Boolean) {
}
fun onRepeatModeChanged(p0: Int) {
override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
super.onQuitFullscreen(url, *objects)
orientationUtils?.backToProtVideo()
}
})
.create()
.build(binding.exoPlayContextId)
exoPlayerManager.startPlayer()
binding.exoPlayContextId.startPlayLogic()
binding.exoPlayContextId.fullscreenButton.setOnClickListener {
orientationUtils?.resolveByClick()
binding.exoPlayContextId.startWindowFullscreen(this, true, true)?.let { fullPlayer ->
if (fullPlayer is MyGSYVideoPlayer) {
fullPlayer.onControlVisibilityChanged = { show ->
Log.e("GSY_TEST", "全屏回调 show=$show")
}
}
}
}
}
private fun initAlarmRecyclerView() {
@@ -177,20 +162,15 @@ class LiveVideoActivity : BaseActivity() {
startActivity(intent)
}
binding.llVoice.setOnClickListener {
toggleVoice()
}
binding.llLight.setOnClickListener {
toggleLight()
}
isLightOn = !isLightOn
binding.llRecord.setOnClickListener {
toggleRecording()
}
binding.llAlarmLog.setOnClickListener {
Toast.makeText(this, R.string.view_alarm_log, Toast.LENGTH_SHORT).show()
if (isLightOn) {
binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on_open)
} else {
binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on)
}
}
binding.xq.setOnClickListener {
@@ -199,53 +179,6 @@ class LiveVideoActivity : BaseActivity() {
intent.putExtra("from_live_video", true)
startActivity(intent)
}
// 控制台方向按钮点击事件
binding.ivUp.setOnClickListener {
handleDirectionClick("up")
}
binding.ivDown.setOnClickListener {
handleDirectionClick("down")
}
binding.ivLeft.setOnClickListener {
handleDirectionClick("left")
}
binding.ivRight.setOnClickListener {
handleDirectionClick("right")
}
// 触摸反馈
setTouchFeedback(binding.ivUp)
setTouchFeedback(binding.ivDown)
setTouchFeedback(binding.ivLeft)
setTouchFeedback(binding.ivRight)
}
private fun setTouchFeedback(view: View) {
view.setOnTouchListener { v, event ->
when (event.action) {
android.view.MotionEvent.ACTION_DOWN -> {
v.scaleX = 0.9f
v.scaleY = 0.9f
}
android.view.MotionEvent.ACTION_UP, android.view.MotionEvent.ACTION_CANCEL -> {
v.scaleX = 1.0f
v.scaleY = 1.0f
}
}
false
}
}
private fun handleDirectionClick(direction: String) {
val directionText = when (direction) {
"up" -> getString(R.string.direction_up)
"down" -> getString(R.string.direction_down)
"left" -> getString(R.string.direction_left)
"right" -> getString(R.string.direction_right)
else -> ""
}
}
private fun fetchAlarmList(page: Int = 1) {
@@ -260,20 +193,13 @@ class LiveVideoActivity : BaseActivity() {
)
Log.d("LiveVideoActivity", "总条数: ${response.total}, 当前页数据条数: ${response.rows.size}")
val newData = response.rows
if (newData.isEmpty()) {
} else {
newData.take(3).forEachIndexed { index, item ->
Log.d("LiveVideoActivity", "${index + 1}条 - id: ${item.id}, categoryName: ${item.categoryName}, createTime: ${item.createTime}")
}
}
totalPage = if (response.total % pageSize == 0) {
response.total / pageSize
} else {
response.total / pageSize + 1
}
isFirstLoad = false
if (page == 1) {
alarmList.clear()
hasMore = totalPage > 1
@@ -288,135 +214,19 @@ class LiveVideoActivity : BaseActivity() {
}
}
private fun toggleVoice() {
isVoiceEnabled = !isVoiceEnabled
if (isVoiceEnabled) {
Glide.with(this).asGif().load(com.example.defenseapplication.R.drawable.voice_on_open).into(binding.ivVoice)
} else {
binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice)
}
}
private fun toggleLight() {
isLightOn = !isLightOn
if (isLightOn) {
binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on_open)
} else {
binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on)
}
}
private fun toggleRecording() {
if (!isRecording) {
if (checkRecordAudioPermission()) {
startRecordingMode()
} else {
requestRecordAudioPermission()
}
} else {
stopRecordingMode()
}
}
private fun checkRecordAudioPermission(): Boolean {
return ContextCompat.checkSelfPermission(
this,
Manifest.permission.RECORD_AUDIO
) == PackageManager.PERMISSION_GRANTED
}
private fun requestRecordAudioPermission() {
requestPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
}
private fun startRecordingMode() {
isRecording = true
binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy_open)
binding.llAlarmLog.visibility = View.GONE
binding.consoleBar.visibility = View.VISIBLE
Glide.with(this).asGif().load(com.example.defenseapplication.R.drawable.voice_on_open).into(binding.ivVoice)
isVoiceEnabled = true
startVoiceRecording()
}
private fun stopRecordingMode() {
isRecording = false
binding.ivRecord.setImageResource(com.example.defenseapplication.R.drawable.copy)
binding.llAlarmLog.visibility = View.VISIBLE
binding.consoleBar.visibility = View.GONE
binding.ivVoice.setImageResource(com.example.defenseapplication.R.drawable.voice)
isVoiceEnabled = false
stopVoiceRecording()
stopMicPulseAnimation()
}
private fun stopMicPulseAnimation() {
micPulseAnimation?.let {
binding.ivVoice.clearAnimation()
micPulseAnimation = null
}
}
private fun startVoiceRecording() {
mediaRecorder = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
val outputDir = getExternalFilesDir(null) ?: filesDir
val audioDir = File(outputDir, "audio")
if (!audioDir.exists()) {
audioDir.mkdirs()
}
val outputFile = File(audioDir, "voice_record_${System.currentTimeMillis()}.3gp")
setOutputFile(outputFile.absolutePath)
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
try {
prepare()
start()
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(this@LiveVideoActivity, R.string.recording_start_failed, Toast.LENGTH_SHORT).show()
}
}
}
private fun stopVoiceRecording() {
mediaRecorder?.let {
try {
it.stop()
it.release()
} catch (e: Exception) {
e.printStackTrace()
}
mediaRecorder = null
}
}
override fun onResume() {
super.onResume()
exoPlayerManager.onResume()
binding.exoPlayContextId.onVideoResume()
}
override fun onPause() {
super.onPause()
exoPlayerManager.onPause()
binding.exoPlayContextId.onVideoPause()
}
override fun onDestroy() {
super.onDestroy()
exoPlayerManager.onDestroy()
orientationUtils?.releaseListener()
binding.exoPlayContextId.release()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
exoPlayerManager.onConfigurationChanged(newConfig)
}
}
}