From 88c791a520ceb5a0c60b520f245d9f8bcab74791 Mon Sep 17 00:00:00 2001 From: maxinyu Date: Thu, 4 Jun 2026 17:51:19 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=86=E9=A2=91=E7=BB=84=E4=BB=B6=E6=9B=B4?= =?UTF-8?q?=E6=8D=A2StandardGSYVideoPlayer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 + .../liveVideo/AlarmLogActivity.kt | 498 ++++-------------- .../view/MyGSYVideoPlayer.kt | 62 +++ app/src/main/res/drawable/progress_video.xml | 31 ++ app/src/main/res/drawable/thumb_video.xml | 11 + .../main/res/layout/alarm_log_activity.xml | 134 +---- 6 files changed, 228 insertions(+), 510 deletions(-) create mode 100644 app/src/main/java/com/example/defenseapplication/view/MyGSYVideoPlayer.kt create mode 100644 app/src/main/res/drawable/progress_video.xml create mode 100644 app/src/main/res/drawable/thumb_video.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8ec58f4..38466e4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -103,6 +103,8 @@ dependencies { implementation("com.contrarywind:Android-PickerView:4.1.9") //基于exoPlayer 自定义播放器 JPlayer支持功能 implementation("com.ycjiang:VideoPlayModule-Lite:2.3.61") + // GSYVideoPlayer + implementation("io.github.carguo:gsyvideoplayer:13.0.0") } \ No newline at end of file diff --git a/app/src/main/java/com/example/defenseapplication/liveVideo/AlarmLogActivity.kt b/app/src/main/java/com/example/defenseapplication/liveVideo/AlarmLogActivity.kt index 8bd59b6..1da339f 100644 --- a/app/src/main/java/com/example/defenseapplication/liveVideo/AlarmLogActivity.kt +++ b/app/src/main/java/com/example/defenseapplication/liveVideo/AlarmLogActivity.kt @@ -1,39 +1,32 @@ package com.example.defenseapplication.liveVideo -import android.content.res.Configuration +import android.content.Context import android.graphics.Color +import android.media.AudioManager import android.os.Bundle import android.util.Log import android.view.View -import android.view.ViewGroup -import android.view.WindowManager -import android.widget.LinearLayout -import android.widget.SeekBar -import android.widget.TextView import android.widget.Toast -import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.lifecycle.lifecycleScope -import chuangyuan.ycj.videolibrary.listener.VideoInfoListener -import chuangyuan.ycj.videolibrary.video.ExoUserPlayer -import chuangyuan.ycj.videolibrary.video.VideoPlayerManager -import com.google.android.exoplayer2.ui.PlayerView -import com.google.android.exoplayer2.SimpleExoPlayer import com.example.defenseapplication.R import com.example.defenseapplication.adapter.AlarmLogAdapter import com.example.defenseapplication.bean.DeviceAlarmBean -import com.example.defenseapplication.bean.DeviceAlarmResponse import com.example.defenseapplication.databinding.AlarmLogActivityBinding import com.example.defenseapplication.utils.RetrofitNetwork -import com.gyf.immersionbar.ImmersionBar -import kotlinx.coroutines.launch -import android.os.Handler -import android.os.Looper -import java.text.SimpleDateFormat -import java.util.* +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack +import com.shuyu.gsyvideoplayer.utils.OrientationUtils +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer import com.bigkoo.pickerview.builder.TimePickerBuilder import com.bigkoo.pickerview.listener.OnTimeSelectListener +import com.example.defenseapplication.view.MyGSYVideoPlayer +import com.shuyu.gsyvideoplayer.GSYVideoManager +import kotlinx.coroutines.launch +import java.text.SimpleDateFormat +import java.util.* //告警记录 class AlarmLogActivity : AppCompatActivity() { @@ -42,151 +35,95 @@ class AlarmLogActivity : AppCompatActivity() { private var adapter: AlarmLogAdapter? = null private val alarmList = mutableListOf() private val originalAlarmList = mutableListOf() - private lateinit var exoPlayerManager: ExoUserPlayer - private var isMuted = false - private var isPlaying = false private var deviceId: String? = null private var isLoading = false - private var fromLiveVideo = false - private var isFullScreen = false - private var isControlsVisible = false - private val hideDelay = 5000L // 5秒自动隐藏 - private val hideHandler = Handler(Looper.getMainLooper()) - private val hideRunnable = Runnable { - hideControls() - } - private var passedVideoUrl: String? = null - private var passedSelectedId: String? = null - private var passedCreateTime: String? = null - private var totalDuration: Long = 0 - private val progressHandler = Handler(Looper.getMainLooper()) - private val progressRunnable = object : Runnable { - override fun run() { - updateProgress() - progressHandler.postDelayed(this, 1000) - } - } + private var videoUrl: String? = null + private var orientationUtils: OrientationUtils? = null + + private var isMute = false + private lateinit var audioManager: AudioManager + private var isRightMenuVisible = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = AlarmLogActivityBinding.inflate(layoutInflater) setContentView(binding.root) - - window.setFlags( - WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN - ) + audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager deviceId = intent.getStringExtra("device_id") - fromLiveVideo = intent.getBooleanExtra("from_live_video", false) - passedVideoUrl = intent.getStringExtra("video_url") - passedSelectedId = intent.getStringExtra("selected_id") - passedCreateTime = intent.getStringExtra("create_time") + videoUrl = intent.getStringExtra("video_url") calendar = Calendar.getInstance() updateDateDisplay() initVideoPlayer() initRecyclerView() fetchAlarmList() initListener() - -// onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { -// override fun handleOnBackPressed() { -// if (exoPlayerManager.onBackPressed()) { -// return -// } -// finish() -// } -// }) - } - - private fun dpToPx(dp: Int): Int { - val density = resources.displayMetrics.density - return (dp * density).toInt() } private fun initVideoPlayer() { - val videoPlayerView = binding.exoPlayContextId - val videoUri = if (fromLiveVideo && !passedVideoUrl.isNullOrEmpty()) { - passedVideoUrl!! - } else { - "https://media.w3.org/2010/05/sintel/trailer.mp4" - } + val videoUrl = intent.getStringExtra("video_url") ?: "https://media.w3.org/2010/05/sintel/trailer.mp4" + val progressDrawable = ContextCompat.getDrawable(this, R.drawable.progress_video) + val thumbDrawable = ContextCompat.getDrawable(this, R.drawable.thumb_video) - exoPlayerManager = VideoPlayerManager.Builder(VideoPlayerManager.TYPE_PLAY_GESTURE, videoPlayerView) - .setPlayUri(videoUri) - .setPosition(0) - .addVideoInfoListener(object : VideoInfoListener { - override fun onPlayStart(p0: Long) { - Log.d("AlarmLogActivity", "视频开始播放") - startProgressUpdate() - showControls() - resetHideTimer() + orientationUtils = OrientationUtils(this, binding.exoPlayContextId) + orientationUtils?.isEnable = true + + GSYVideoOptionBuilder() + .setUrl(videoUrl) + .setVideoTitle("") + .setNeedLockFull(true) + .setCacheWithPlay(false) + .setRotateViewAuto(true) + .setLockLand(false) + .setShowFullAnimation(true) + .setNeedShowWifiTip(false) + .setEnlargeImageRes(R.drawable.full_size) + .setBottomShowProgressBarDrawable(progressDrawable, thumbDrawable) + .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@AlarmLogActivity, "播放失败", Toast.LENGTH_SHORT).show() - } - - override fun onPlayEnd() { - Log.d("AlarmLogActivity", "播放结束") - stopProgressUpdate() - hideControls() - } - - override fun isPlaying(p0: Boolean) { - isPlaying = p0 - if (p0) { - startProgressUpdate() - // 视频开始播放时,如果控制栏可见,启动自动隐藏计时器 - if (isControlsVisible) { - resetHideTimer() - } - } else { - stopProgressUpdate() - // 视频暂停时,取消自动隐藏计时器,保持控制栏显示 - hideHandler.removeCallbacks(hideRunnable) - showControls() - } - } - - 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.onControlVisibilityChanged = { show -> + Log.e("GSY_TEST", "Activity收到回调 show=$show") + val visible = if (show) View.VISIBLE else View.GONE - setupControllerVisibilityListener() - updateProgress() - } - - private fun setupControllerVisibilityListener() { - // 移除原来的控制器监听器,改为自定义控制逻辑 - } - - private fun findPlayerView(view: View): PlayerView? { - if (view is PlayerView) { - return view + binding.ivDownload.visibility = visible + binding.ivDelete.visibility = visible + binding.ivMute.visibility = visible } - if (view is ViewGroup) { - for (i in 0 until view.childCount) { - val child = view.getChildAt(i) - val found = findPlayerView(child) - if (found != null) { - return found + + binding.exoPlayContextId.startPlayLogic() + binding.exoPlayContextId.fullscreenButton.setOnClickListener { + orientationUtils?.resolveByClick() + + binding.exoPlayContextId.startWindowFullscreen(this, true, true)?.let { fullPlayer -> + + if (fullPlayer is MyGSYVideoPlayer) { + fullPlayer.onControlVisibilityChanged = { show -> + val visible = if (show) View.VISIBLE else View.GONE + binding.ivDownload.visibility = visible + binding.ivDelete.visibility = visible + binding.ivMute.visibility = visible + } } } } - return null - } + + } private fun initRecyclerView() { adapter = AlarmLogAdapter(alarmList) { item -> if (!item.video.isNullOrEmpty()) { - playVideo(item.video!!) + Toast.makeText(this, "该告警记录有视频", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "该告警记录没有视频", Toast.LENGTH_SHORT).show() } @@ -195,68 +132,6 @@ class AlarmLogActivity : AppCompatActivity() { binding.alarmListRecyclerView.adapter = adapter } - private fun playVideo(videoUrl: String) { - exoPlayerManager.onDestroy() - - exoPlayerManager = VideoPlayerManager.Builder(VideoPlayerManager.TYPE_PLAY_GESTURE, binding.exoPlayContextId) - .setPlayUri(videoUrl) - .setPosition(0) - .addVideoInfoListener(object : VideoInfoListener { - override fun onPlayStart(p0: Long) { - Log.d("AlarmLogActivity", "视频开始播放") - startProgressUpdate() - showControls() - resetHideTimer() - } - - override fun onLoadingChanged() { - } - - override fun onPlayerError(p0: com.google.android.exoplayer2.ExoPlaybackException?) { - Toast.makeText(this@AlarmLogActivity, "播放失败", Toast.LENGTH_SHORT).show() - } - - override fun onPlayEnd() { - Log.d("AlarmLogActivity", "播放结束") - stopProgressUpdate() - hideControls() - } - - override fun isPlaying(p0: Boolean) { - isPlaying = p0 - if (p0) { - startProgressUpdate() - // 视频开始播放时,如果控制栏可见,启动自动隐藏计时器 - if (isControlsVisible) { - resetHideTimer() - } - } else { - stopProgressUpdate() - // 视频暂停时,取消自动隐藏计时器,保持控制栏显示 - hideHandler.removeCallbacks(hideRunnable) - showControls() - } - } - - fun onRepeatModeChanged(p0: Int) { - } - }) - .create() - - exoPlayerManager.startPlayer() - - // 保持当前的静音状态 - if (isMuted) { - try { - val method = exoPlayerManager.javaClass.getMethod("getPlayer") - val player = method.invoke(exoPlayerManager) as? SimpleExoPlayer - player?.volume = 0f - } catch (e: Exception) { - e.printStackTrace() - } - } - } - private fun fetchAlarmList() { val currentDeviceId = deviceId ?: return @@ -328,226 +203,34 @@ class AlarmLogActivity : AppCompatActivity() { binding.tvDate.text = "${month}月${day}日" } - private fun showControls() { - isControlsVisible = true - binding.functionBar.visibility = View.VISIBLE - binding.muteBar.visibility = View.VISIBLE - binding.playBar.visibility = View.VISIBLE - binding.progressBar.visibility = View.VISIBLE - // 只有在视频播放时才启动自动隐藏计时器 - if (isPlaying) { - resetHideTimer() - } - } - - private fun hideControls() { - isControlsVisible = false - binding.functionBar.visibility = View.GONE - binding.muteBar.visibility = View.GONE - binding.playBar.visibility = View.GONE - binding.progressBar.visibility = View.GONE - } - - private fun toggleControls() { - if (isControlsVisible) { - hideControls() - } else { - showControls() - } - } - - private fun resetHideTimer() { - hideHandler.removeCallbacks(hideRunnable) - // 只有在视频播放时才启动自动隐藏计时器 - if (isPlaying) { - hideHandler.postDelayed(hideRunnable, hideDelay) - } - } - private fun initListener() { - // 点击视频区域切换控制栏显示/隐藏 - binding.exoPlayContextId.setOnClickListener { - toggleControls() - } - - // 控制栏上的按钮点击时重置隐藏计时器 - binding.ivBack.setOnClickListener { - resetHideTimer() - finish() - } binding.expandImg.setOnClickListener { showDatePicker() } - binding.ivMute.setOnClickListener { - resetHideTimer() - toggleMute() - } - binding.ivDownload.setOnClickListener { - resetHideTimer() - downloadVideo() + Toast.makeText(this,"下载视频",Toast.LENGTH_SHORT).show() } - binding.ivDelete.setOnClickListener { - resetHideTimer() - deleteVideo() + } - - binding.ivFullScreen.setOnClickListener { - resetHideTimer() - toggleFullScreen() - } - - binding.ivPlay.setOnClickListener { - resetHideTimer() - togglePlay() - } - - binding.seekBar.setOnSeekBarChangeListener(object : android.widget.SeekBar.OnSeekBarChangeListener { - override fun onProgressChanged(seekBar: android.widget.SeekBar?, progress: Int, fromUser: Boolean) { - if (fromUser) { - resetHideTimer() - exoPlayerManager.seekTo(progress.toLong() * 1000) - } - } - - override fun onStartTrackingTouch(seekBar: android.widget.SeekBar?) { - resetHideTimer() - } - - override fun onStopTrackingTouch(seekBar: android.widget.SeekBar?) { - } - }) - } - - private fun toggleMute() { - isMuted = !isMuted - try { - // 通过反射获取底层的 ExoPlayer 实例 - val method = exoPlayerManager.javaClass.getMethod("getPlayer") - val player = method.invoke(exoPlayerManager) as? SimpleExoPlayer - - if (isMuted) { - binding.ivMute.setImageResource(R.drawable.mute) - player?.volume = 0f - Toast.makeText(this, R.string.sound_off, Toast.LENGTH_SHORT).show() + binding.ivMute.setOnClickListener { + isMute = !isMute + if (isMute) { + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0) + binding.ivMute.setImageResource( + R.drawable.mute + ) } else { - binding.ivMute.setImageResource(R.drawable.un_mute) - player?.volume = 1f - Toast.makeText(this, R.string.sound_on, Toast.LENGTH_SHORT).show() + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) / 2, 0) + binding.ivMute.setImageResource( + R.drawable.un_mute + ) } - } catch (e: Exception) { - e.printStackTrace() - Toast.makeText(this, "音量控制失败", Toast.LENGTH_SHORT).show() - } - } - - private fun downloadVideo() { - Toast.makeText(this, R.string.download, Toast.LENGTH_SHORT).show() - } - - private fun deleteVideo() { - Toast.makeText(this, R.string.delete, Toast.LENGTH_SHORT).show() - } - - private fun updateProgress() { - val currentPosition = exoPlayerManager.getCurrentPosition() - totalDuration = exoPlayerManager.getDuration() - - binding.tvCurrentTime.text = formatTime(currentPosition) - binding.tvTotalTime.text = formatTime(totalDuration) - - if (totalDuration > 0) { - val progress = (currentPosition * 100 / totalDuration).toInt() - binding.seekBar.progress = progress - } - } - - private fun formatTime(milliseconds: Long): String { - val seconds = (milliseconds / 1000).toInt() - val hours = seconds / 3600 - val minutes = (seconds % 3600) / 60 - val secs = seconds % 60 - - return if (hours > 0) { - String.format("%02d:%02d:%02d", hours, minutes, secs) - } else { - String.format("%02d:%02d", minutes, secs) - } - } - - private fun startProgressUpdate() { - stopProgressUpdate() - progressHandler.post(progressRunnable) - } - - private fun stopProgressUpdate() { - progressHandler.removeCallbacks(progressRunnable) - } - - private fun toggleFullScreen() { - isFullScreen = !isFullScreen - if (isFullScreen) { - binding.ivFullScreen.setImageResource(R.drawable.full_screen) - requestedOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE - } else { - binding.ivFullScreen.setImageResource(R.drawable.full_size) - requestedOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - } - } - - private fun togglePlay() { - isPlaying = !isPlaying - if (isPlaying) { - binding.ivPlay.setImageResource(R.drawable.zt) - exoPlayerManager.startPlayer() - } else { - binding.ivPlay.setImageResource(R.drawable.start) - exoPlayerManager.onPause() - } - } - - override fun onResume() { - super.onResume() - exoPlayerManager.onResume() - } - - override fun onPause() { - super.onPause() - exoPlayerManager.onPause() - } - - override fun onDestroy() { - super.onDestroy() - exoPlayerManager.onDestroy() - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { - // 进入横屏(全屏)模式 - binding.titleLayout.visibility = View.GONE - binding.alarmListRecyclerView.visibility = View.GONE - - val params = binding.exoPlayContextId.layoutParams - params.width = LinearLayout.LayoutParams.MATCH_PARENT - params.height = LinearLayout.LayoutParams.MATCH_PARENT - binding.exoPlayContextId.layoutParams = params - } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { - // 进入竖屏(非全屏)模式 - binding.titleLayout.visibility = View.VISIBLE - binding.alarmListRecyclerView.visibility = View.VISIBLE - - val params = binding.exoPlayContextId.layoutParams - params.width = LinearLayout.LayoutParams.MATCH_PARENT - params.height = dpToPx(280) - binding.exoPlayContextId.layoutParams = params } - exoPlayerManager.onConfigurationChanged(newConfig) + } private fun showDatePicker() { @@ -568,4 +251,21 @@ class AlarmLogActivity : AppCompatActivity() { .build() timePicker.show() } + + override fun onPause() { + super.onPause() + binding.exoPlayContextId.onVideoPause() + } + + override fun onResume() { + super.onResume() + binding.exoPlayContextId.onVideoResume() + } + + override fun onDestroy() { + super.onDestroy() + + orientationUtils?.releaseListener() + binding.exoPlayContextId.release() + } } diff --git a/app/src/main/java/com/example/defenseapplication/view/MyGSYVideoPlayer.kt b/app/src/main/java/com/example/defenseapplication/view/MyGSYVideoPlayer.kt new file mode 100644 index 0000000..5709f5a --- /dev/null +++ b/app/src/main/java/com/example/defenseapplication/view/MyGSYVideoPlayer.kt @@ -0,0 +1,62 @@ +package com.example.defenseapplication.view + +import android.content.Context +import android.util.AttributeSet +import android.util.Log +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer + +class MyGSYVideoPlayer @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : StandardGSYVideoPlayer(context, attrs) { + + /** 控制栏显示/隐藏监听 */ + var onControlVisibilityChanged: ((Boolean) -> Unit)? = null + + // 播放中显示控制栏 + override fun changeUiToPlayingShow() { + super.changeUiToPlayingShow() + Log.e("GSY_TEST", "通知显示按钮") + onControlVisibilityChanged?.invoke(true) + } + + // 暂停中显示控制栏 + override fun changeUiToPauseShow() { + super.changeUiToPauseShow() + onControlVisibilityChanged?.invoke(true) + } + + // 播放中清除控制栏 + override fun changeUiToPlayingClear() { + super.changeUiToPlayingClear() + Log.e("GSY_TEST", "changeUiToPlayingClear") + onControlVisibilityChanged?.invoke(false) + } + + // 暂停中清除控制栏 + override fun changeUiToPauseClear() { + super.changeUiToPauseClear() + onControlVisibilityChanged?.invoke(false) + } + + // 完成播放 + override fun changeUiToCompleteClear() { + super.changeUiToCompleteClear() + onControlVisibilityChanged?.invoke(false) + } + override fun hideAllWidget() { + Log.e("GSY_TEST", "通知隐藏按钮") + super.hideAllWidget() + onControlVisibilityChanged?.invoke(false) + } + + override fun changeUiToNormal() { + Log.e("GSY_TEST", "changeUiToNormal") + super.changeUiToNormal() + } + + override fun changeUiToPreparingShow() { + Log.e("GSY_TEST", "changeUiToPreparingShow") + super.changeUiToPreparingShow() + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/progress_video.xml b/app/src/main/res/drawable/progress_video.xml new file mode 100644 index 0000000..0e391c9 --- /dev/null +++ b/app/src/main/res/drawable/progress_video.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/thumb_video.xml b/app/src/main/res/drawable/thumb_video.xml new file mode 100644 index 0000000..e10a7a5 --- /dev/null +++ b/app/src/main/res/drawable/thumb_video.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/alarm_log_activity.xml b/app/src/main/res/layout/alarm_log_activity.xml index 7c0aa6e..b3ebecc 100644 --- a/app/src/main/res/layout/alarm_log_activity.xml +++ b/app/src/main/res/layout/alarm_log_activity.xml @@ -2,10 +2,8 @@ @@ -14,137 +12,51 @@ android:layout_height="wrap_content"> - + android:layout_height="280dp"/> - + - - - - + android:layout_marginTop="50dp" + android:id="@+id/rightMenu" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:layout_marginEnd="15dp" + android:orientation="horizontal"> - - + + android:layout_gravity="end|bottom" + android:layout_marginEnd="15dp"> - - - - - - - - - - - - - - - - - - - - - - -