视频组件

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

View File

@@ -1,47 +1,37 @@
package com.example.defenseapplication.liveVideo package com.example.defenseapplication.liveVideo
import android.Manifest import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.media.AudioManager
import android.media.MediaRecorder
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.animation.ScaleAnimation import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import chuangyuan.ycj.videolibrary.video.ExoUserPlayer
import com.example.defenseapplication.R import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.AlarmLogHorizontalAdapter import com.example.defenseapplication.adapter.AlarmLogHorizontalAdapter
import com.example.defenseapplication.base.BaseActivity import com.example.defenseapplication.base.BaseActivity
import com.example.defenseapplication.bean.DeviceAlarmBean import com.example.defenseapplication.bean.DeviceAlarmBean
import com.example.defenseapplication.bean.DeviceAlarmResponse
import com.example.defenseapplication.databinding.LiveVideoActivityBinding import com.example.defenseapplication.databinding.LiveVideoActivityBinding
import com.example.defenseapplication.utils.ActivityManager import com.example.defenseapplication.utils.ActivityManager
import com.example.defenseapplication.utils.RetrofitNetwork import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.MyGSYVideoPlayer
import com.gyf.immersionbar.ImmersionBar 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 kotlinx.coroutines.launch
import java.io.File import androidx.lifecycle.lifecycleScope
import java.io.IOException import com.alibaba.sdk.android.emas.f
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
class LiveVideoActivity : BaseActivity() { class LiveVideoActivity : BaseActivity() {
private lateinit var binding: LiveVideoActivityBinding private lateinit var binding: LiveVideoActivityBinding
private lateinit var exoPlayerManager: ExoUserPlayer
private var isVoiceEnabled = false private var isVoiceEnabled = false
private var isLightOn = 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 deviceId: String? = null
private var alarmList: MutableList<DeviceAlarmBean> = mutableListOf() private var alarmList: MutableList<DeviceAlarmBean> = mutableListOf()
private var alarmAdapter: AlarmLogHorizontalAdapter? = null private var alarmAdapter: AlarmLogHorizontalAdapter? = null
@@ -50,18 +40,8 @@ class LiveVideoActivity : BaseActivity() {
private val pageSize = 10 private val pageSize = 10
private var hasMore = true private var hasMore = true
private var isLoading = false private var isLoading = false
private var isFirstLoad = true
private val requestPermissionLauncher = registerForActivityResult( private var orientationUtils: OrientationUtils? = null
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
startRecordingMode()
} else {
Toast.makeText(this, R.string.recording_permission_needed, Toast.LENGTH_SHORT).show()
isRecording = false
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -83,47 +63,52 @@ class LiveVideoActivity : BaseActivity() {
initAlarmRecyclerView() initAlarmRecyclerView()
initListener() initListener()
fetchAlarmList() fetchAlarmList()
// onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
// override fun handleOnBackPressed() {
// if (exoPlayerManager.onBackPressed()) {
// return
// }
// finish()
// }
// })
} }
private fun initVideoPlayer() { private fun initVideoPlayer() {
val videoPlayerView = binding.exoPlayContextId val videoUrl = "https://www.w3schools.com/html/movie.mp4"
exoPlayerManager = VideoPlayerManager.Builder(VideoPlayerManager.TYPE_PLAY_GESTURE, videoPlayerView) orientationUtils = OrientationUtils(this, binding.exoPlayContextId)
.setPlayUri("https://media.w3.org/2010/05/sintel/trailer.mp4") orientationUtils?.isEnable = true
.setPosition(0)
.addVideoInfoListener(object : VideoInfoListener { GSYVideoOptionBuilder()
override fun onPlayStart(p0: Long) { .setUrl(videoUrl)
Log.d("LiveVideoActivity", "视频开始播放") .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 onQuitFullscreen(url: String?, vararg objects: Any?) {
} super.onQuitFullscreen(url, *objects)
orientationUtils?.backToProtVideo()
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) {
} }
}) })
.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() { private fun initAlarmRecyclerView() {
@@ -177,20 +162,15 @@ class LiveVideoActivity : BaseActivity() {
startActivity(intent) startActivity(intent)
} }
binding.llVoice.setOnClickListener {
toggleVoice()
}
binding.llLight.setOnClickListener { binding.llLight.setOnClickListener {
toggleLight() isLightOn = !isLightOn
}
binding.llRecord.setOnClickListener { if (isLightOn) {
toggleRecording() binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on_open)
} } else {
binding.ivLight.setImageResource(com.example.defenseapplication.R.drawable.light_on)
binding.llAlarmLog.setOnClickListener { }
Toast.makeText(this, R.string.view_alarm_log, Toast.LENGTH_SHORT).show()
} }
binding.xq.setOnClickListener { binding.xq.setOnClickListener {
@@ -199,53 +179,6 @@ class LiveVideoActivity : BaseActivity() {
intent.putExtra("from_live_video", true) intent.putExtra("from_live_video", true)
startActivity(intent) 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) { private fun fetchAlarmList(page: Int = 1) {
@@ -260,19 +193,12 @@ class LiveVideoActivity : BaseActivity() {
) )
Log.d("LiveVideoActivity", "总条数: ${response.total}, 当前页数据条数: ${response.rows.size}") Log.d("LiveVideoActivity", "总条数: ${response.total}, 当前页数据条数: ${response.rows.size}")
val newData = response.rows 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) { totalPage = if (response.total % pageSize == 0) {
response.total / pageSize response.total / pageSize
} else { } else {
response.total / pageSize + 1 response.total / pageSize + 1
} }
isFirstLoad = false
if (page == 1) { if (page == 1) {
alarmList.clear() alarmList.clear()
@@ -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() { override fun onResume() {
super.onResume() super.onResume()
exoPlayerManager.onResume() binding.exoPlayContextId.onVideoResume()
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
exoPlayerManager.onPause() binding.exoPlayContextId.onVideoPause()
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
exoPlayerManager.onDestroy() orientationUtils?.releaseListener()
} binding.exoPlayContextId.release()
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
exoPlayerManager.onConfigurationChanged(newConfig)
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -44,12 +44,10 @@
</LinearLayout> </LinearLayout>
<!-- 视频播放区域 --> <!-- 视频播放区域 -->
<chuangyuan.ycj.videolibrary.widget.VideoPlayerView <com.example.defenseapplication.view.MyGSYVideoPlayer
android:id="@+id/exoPlayContextId" android:id="@+id/exoPlayContextId"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="250dp" android:layout_height="280dp"/>
android:background="@android:color/transparent"
app:use_controller="false"/>
<!-- 功能按钮区域 --> <!-- 功能按钮区域 -->
<LinearLayout <LinearLayout