diff --git a/app/src/main/java/com/ckkj/defense_device/home/HomeMenuFragment.kt b/app/src/main/java/com/ckkj/defense_device/home/HomeMenuFragment.kt index 5d969cb..c8faf51 100644 --- a/app/src/main/java/com/ckkj/defense_device/home/HomeMenuFragment.kt +++ b/app/src/main/java/com/ckkj/defense_device/home/HomeMenuFragment.kt @@ -548,6 +548,8 @@ class HomeMenuFragment : Fragment() { val intent = Intent(requireContext(), LiveVideoActivity::class.java) intent.putExtra("device_id", device.deviceId) intent.putExtra("device_name", device.name) + intent.putExtra("deviceNo", device.deviceNo) + intent.putExtra("light", device.light) startActivity(intent) } else { showDefenseDialog( diff --git a/app/src/main/java/com/ckkj/defense_device/liveVideo/LiveVideoActivity.kt b/app/src/main/java/com/ckkj/defense_device/liveVideo/LiveVideoActivity.kt index 1ef93b2..840021d 100644 --- a/app/src/main/java/com/ckkj/defense_device/liveVideo/LiveVideoActivity.kt +++ b/app/src/main/java/com/ckkj/defense_device/liveVideo/LiveVideoActivity.kt @@ -5,6 +5,8 @@ import android.content.res.Configuration import android.graphics.Color import android.os.Bundle import android.util.Log +import android.view.GestureDetector +import android.view.MotionEvent import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -22,6 +24,9 @@ import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack import com.shuyu.gsyvideoplayer.utils.OrientationUtils import kotlinx.coroutines.launch import androidx.lifecycle.lifecycleScope +import android.os.Handler +import android.os.Looper +import com.ckkj.defense_device.view.MyGSYVideoPlayerLive class LiveVideoActivity : BaseActivity() { private lateinit var binding: LiveVideoActivityBinding @@ -30,6 +35,7 @@ class LiveVideoActivity : BaseActivity() { private var isLightOn = false private var isConsoleVisible = false private var deviceId: String? = null + private var deviceNo: String? = null private var alarmList: MutableList = mutableListOf() private var alarmAdapter: AlarmLogHorizontalAdapter? = null private var currentPage = 1 @@ -39,6 +45,8 @@ class LiveVideoActivity : BaseActivity() { private var isLoading = false private var orientationUtils: OrientationUtils? = null + private val ptzHandler = Handler(Looper.getMainLooper()) + private var currentPtzDirection: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -54,6 +62,8 @@ class LiveVideoActivity : BaseActivity() { deviceId = intent.getStringExtra("device_id") val deviceName = intent.getStringExtra("device_name") + deviceNo = intent.getStringExtra("deviceNo") + val light = intent.getStringExtra("light") if (!deviceName.isNullOrEmpty()) { binding.tvTitle.text = deviceName } @@ -112,6 +122,21 @@ class LiveVideoActivity : BaseActivity() { hidePlayerControls() binding.exoPlayContextId.startPlayLogic() binding.exoPlayContextId.fullscreenButton.setOnClickListener { + binding.exoPlayContextId.onPtzControlListener = + object : MyGSYVideoPlayerLive.OnPtzControlListener { + override fun onStartPtz(direction: String) { + val no = deviceNo ?: return + startPtz(deviceNo = no, direction = direction) + } + + override fun onStopPtz() { + stopCurrentPtz() + } + + override fun onShoot() { + deviceNo?.let { shoot(it) } + } + } orientationUtils?.resolveByClick() binding.exoPlayContextId.startWindowFullscreen(this, true, true) } @@ -237,6 +262,114 @@ class LiveVideoActivity : BaseActivity() { binding.ivVoice.setImageResource(R.mipmap.voice) } } + + binding.ivUp.setOnTouchListener { v, event -> + handlePtzTouch( + view = v, + event = event, + direction = "up", + action = { deviceNo } + ) + } + + binding.ivDown.setOnTouchListener { v, event -> + handlePtzTouch( + view = v, + event = event, + direction = "down", + action = { deviceNo } + ) + } + + binding.ivLeft.setOnTouchListener { v, event -> + handlePtzTouch( + view = v, + event = event, + direction = "left", + action = { deviceNo } + ) + } + + binding.ivRight.setOnTouchListener { v, event -> + handlePtzTouch( + view = v, + event = event, + direction = "right", + action = { deviceNo } + ) + } + } + + private fun handlePtzTouch( + view: View, + event: MotionEvent, + direction: String, + action: () -> String? + ): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN -> { + view.isPressed = true + val deviceNo = action() + if (!deviceNo.isNullOrEmpty()) { + startPtz(deviceNo = deviceNo, direction = direction) + } + } + + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + view.isPressed = false + stopCurrentPtz() + } + } + return true + } + + private fun startPtz(deviceNo: String, direction: String) { + stopCurrentPtz() + + currentPtzDirection = direction + lifecycleScope.launch { + try { + val response = when (direction) { + "up" -> RetrofitNetwork.getNetworkService().ptzUp(deviceNo) + "down" -> RetrofitNetwork.getNetworkService().ptzDown(deviceNo) + "left" -> RetrofitNetwork.getNetworkService().ptzLeft(deviceNo) + "right" -> RetrofitNetwork.getNetworkService().ptzRight(deviceNo) + else -> null + } + Log.d("LV", "PTZ $direction start response: ${response?.code}") + } catch (e: Exception) { + Log.e("LV", "PTZ $direction start failed", e) + } + } + } + + private fun shoot(deviceNo: String) { + Log.d("LV", "btnCapture click, deviceNo=$deviceNo, duration=0") + lifecycleScope.launch { + try { + val response = RetrofitNetwork.getNetworkService().ptzShoot( + deviceNo = deviceNo, + duration = "0" + ) + Log.d("LV", "PTZ shoot response: code=${response.code}, msg=${response.msg}") + } catch (e: Exception) { + Log.e("LV", "PTZ shoot failed", e) + } + } + } + + private fun stopCurrentPtz() { + val direction = currentPtzDirection ?: return + currentPtzDirection = null + + lifecycleScope.launch { + try { + val response = RetrofitNetwork.getNetworkService().ptzStop(deviceNo ?: return@launch) + Log.d("LV", "PTZ $direction stop response: ${response.code}") + } catch (e: Exception) { + Log.e("LV", "PTZ $direction stop failed", e) + } + } } private fun fetchAlarmList(page: Int = 1) { @@ -284,6 +417,7 @@ class LiveVideoActivity : BaseActivity() { override fun onDestroy() { super.onDestroy() + ptzHandler.removeCallbacksAndMessages(null) orientationUtils?.releaseListener() binding.exoPlayContextId.release() } diff --git a/app/src/main/java/com/ckkj/defense_device/utils/ApiService.kt b/app/src/main/java/com/ckkj/defense_device/utils/ApiService.kt index 9b0b810..92439e5 100644 --- a/app/src/main/java/com/ckkj/defense_device/utils/ApiService.kt +++ b/app/src/main/java/com/ckkj/defense_device/utils/ApiService.kt @@ -271,5 +271,62 @@ interface ApiService { @POST("/app/joinTheFamily") suspend fun joinTheFamily(@Body request: JoinTheFamilyBean): GetCodeBean + /** + * PTZ向上控制 + */ + @POST("/app/ptz/{deviceNo}/up") + suspend fun ptzUp( + @retrofit2.http.Path("deviceNo") deviceNo: String, + @Query("speed") speed: String = "5", + @Query("duration") duration: String = "0" + ): GetCodeBean + + /** + * PTZ向下控制 + */ + @POST("/app/ptz/{deviceNo}/down") + suspend fun ptzDown( + @retrofit2.http.Path("deviceNo") deviceNo: String, + @Query("speed") speed: String = "5", + @Query("duration") duration: String = "0" + ): GetCodeBean + + /** + * PTZ向左控制 + */ + @POST("/app/ptz/{deviceNo}/left") + suspend fun ptzLeft( + @retrofit2.http.Path("deviceNo") deviceNo: String, + @Query("speed") speed: String = "5", + @Query("duration") duration: String = "0" + ): GetCodeBean + + /** + * PTZ向右控制 + */ + @POST("/app/ptz/{deviceNo}/right") + suspend fun ptzRight( + @retrofit2.http.Path("deviceNo") deviceNo: String, + @Query("speed") speed: String = "5", + @Query("duration") duration: String = "0" + ): GetCodeBean + + /** + * PTZ停止控制 + */ + @POST("/app/ptz/{deviceNo}/stop") + suspend fun ptzStop( + @retrofit2.http.Path("deviceNo") deviceNo: String, + ): GetCodeBean + + /** + * PTZ控制水枪喷水 + */ + @POST("/app/ptz/{deviceNo}/shoot") + suspend fun ptzShoot( + @retrofit2.http.Path("deviceNo") deviceNo: String, + @Query("duration") duration: String, + ): GetCodeBean + } diff --git a/app/src/main/java/com/ckkj/defense_device/view/MyGSYVideoPlayerLive.kt b/app/src/main/java/com/ckkj/defense_device/view/MyGSYVideoPlayerLive.kt index c93972f..8e8d500 100644 --- a/app/src/main/java/com/ckkj/defense_device/view/MyGSYVideoPlayerLive.kt +++ b/app/src/main/java/com/ckkj/defense_device/view/MyGSYVideoPlayerLive.kt @@ -1,8 +1,10 @@ package com.ckkj.defense_device.view +import android.util.Log import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View import com.ckkj.defense_device.R import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer @@ -12,6 +14,18 @@ class MyGSYVideoPlayerLive @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : StandardGSYVideoPlayer(context, attrs) { + companion object { + private const val TAG = "MyGSYVideoPlayerLive" + } + + interface OnPtzControlListener { + fun onStartPtz(direction: String) + fun onStopPtz() + fun onShoot() + } + + var onPtzControlListener: OnPtzControlListener? = null + override fun init(context: Context) { super.init(context) startButton.visibility = View.GONE @@ -76,10 +90,54 @@ class MyGSYVideoPlayerLive @JvmOverloads constructor( overlay.findViewById(R.id.btnCapture) ?.setOnClickListener { - + Log.d("MyGSYVideoPlayerLive", "btnCapture clicked, onShoot invoked") + onPtzControlListener?.onShoot() } + player.initPtzControls(overlay) + return player } + private fun initPtzControls(overlay: View) { + val ptzListener = onPtzControlListener + + overlay.findViewById(R.id.ivUp)?.setOnTouchListener { v, event -> + handlePtzTouch(v, event, "up", ptzListener) + } + + overlay.findViewById(R.id.ivDown)?.setOnTouchListener { v, event -> + handlePtzTouch(v, event, "down", ptzListener) + } + + overlay.findViewById(R.id.ivLeft)?.setOnTouchListener { v, event -> + handlePtzTouch(v, event, "left", ptzListener) + } + + overlay.findViewById(R.id.ivRight)?.setOnTouchListener { v, event -> + handlePtzTouch(v, event, "right", ptzListener) + } + } + + private fun handlePtzTouch( + view: View, + event: MotionEvent, + direction: String, + listener: OnPtzControlListener? + ): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN -> { + Log.d(TAG, "全屏PTZ长按开始 direction=$direction") + view.isPressed = true + listener?.onStartPtz(direction) + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + Log.d(TAG, "全屏PTZ长按结束 direction=$direction") + view.isPressed = false + listener?.onStopPtz() + } + } + return true + } + } \ No newline at end of file