package com.casic.endoscope.view import android.annotation.SuppressLint import android.graphics.PixelFormat import android.os.Bundle import android.os.Handler import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.MotionEvent import android.view.SurfaceHolder import android.view.View import androidx.lifecycle.lifecycleScope import com.casic.endoscope.adapter.CameraPointAdapter import com.casic.endoscope.bean.CameraPointBean import com.casic.endoscope.databinding.ActivityMainBinding import com.casic.endoscope.extensions.createHorizontalCommand import com.casic.endoscope.extensions.createImageFileDir import com.casic.endoscope.extensions.createVerticalCommand import com.casic.endoscope.extensions.createVideoFileDir import com.casic.endoscope.extensions.getChannel import com.casic.endoscope.extensions.toTime import com.casic.endoscope.utils.DataBaseManager import com.casic.endoscope.utils.ProjectConstant import com.casic.endoscope.utils.hk.MessageCodeHub import com.casic.endoscope.utils.hk.SDKGuider import com.casic.endoscope.widgets.AddCameraPointDialog import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.HCNetSDK import com.hikvision.netsdk.NET_DVR_JPEGPARA import com.hikvision.netsdk.NET_DVR_PREVIEWINFO import com.hikvision.netsdk.NET_DVR_SERIALSTART_V40 import com.hikvision.netsdk.PTZCommand import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.getStatusBarHeight import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.WeakReferenceHandler import com.pengxh.kt.lite.widget.SteeringWheelView import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.text.SimpleDateFormat import java.util.Date import java.util.Locale import java.util.Timer import java.util.TimerTask @SuppressLint("all") class MainActivity : KotlinBaseActivity<ActivityMainBinding>(), SurfaceHolder.Callback, Handler.Callback { private val kTag = "MainActivity" private val context = this@MainActivity private val hkSDK by lazy { HCNetSDK.getInstance() } private val timeFormat by lazy { SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA) } private val selectedSpeed = 7 private val messageCode = 2024021901 private var clickTime = 0L private var previewHandle = -1 private var selectChannel = -1 private var returnUserId = -1 private var aChannelNum = 0 private var startAChannel = 0 private var dChannelNum = 0 private var startDChannel = 0 private var isPreviewSuccess = false //手指是否已经从方向控制盘抬起 private var isActionUp = true //焦距按钮是否已松开 private var isScaleButtonUp = true //是否拍照成功 private var isCaptureSuccess = true private var timer: Timer? = null private var timerTask: TimerTask? = null private var seconds = 0L private var dataBeans: MutableList<CameraPointBean> = ArrayList() private var selectedItems: MutableList<CameraPointBean> = ArrayList() private lateinit var weakReferenceHandler: WeakReferenceHandler private lateinit var dataAdapter: CameraPointAdapter override fun initViewBinding(): ActivityMainBinding { return ActivityMainBinding.inflate(layoutInflater) } override fun initOnCreate(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) dataBeans = DataBaseManager.get.loadAllCameraPoint() //显示数据 weakReferenceHandler.sendEmptyMessage(messageCode) } override fun handleMessage(msg: Message): Boolean { if (msg.what == messageCode) { //绑定数据 dataAdapter = CameraPointAdapter(this, dataBeans) binding.recyclerView.adapter = dataAdapter dataAdapter.setOnItemCheckedListener(object : CameraPointAdapter.OnItemCheckedListener { override fun onItemChecked(position: Int, items: ArrayList<CameraPointBean>) { selectedItems = items } }) } return true } override fun initEvent() { binding.openAlbumButton.setOnClickListener { if (isPreviewSuccess) { "设备预览画面中,无法切换".show(this) return@setOnClickListener } navigatePageTo<AlbumActivity>() } //单张拍照 binding.imageButton.setOnClickListener { if (isPreviewSuccess) { binding.imageButton.isEnabled = false val strJpeg = NET_DVR_JPEGPARA() strJpeg.wPicQuality = 1 strJpeg.wPicSize = 2 val imagePath = "/${createImageFileDir()}/${timeFormat.format(Date())}.png" hkSDK.NET_DVR_CaptureJPEGPicture( returnUserId, selectChannel, strJpeg, imagePath ) if (MessageCodeHub.getErrorCode() == 0) { "画面抓取成功".show(this) binding.imageButton.isEnabled = true } } else { "摄像头预览未打开,无法拍照".show(this) } } //视频录制 binding.videoButton.setOnClickListener { if (isPreviewSuccess) { val videoPath = "/${createVideoFileDir()}/${timeFormat.format(Date())}.mp4" hkSDK.NET_DVR_SaveRealData(returnUserId, videoPath) binding.videoStateView.visibility = View.VISIBLE } else { "摄像头预览未打开,无法录制视频".show(this) } } binding.videoButton.setOnLongClickListener { //停止视频抓取 hkSDK.NET_DVR_StopSaveRealData(returnUserId) binding.videoStateView.visibility = View.INVISIBLE "视频录制成功".show(this) true } //连续拍照 binding.multipleImageButton.setOnClickListener { if (dataBeans.isEmpty()) { "请先设置连续拍照步骤".show(this) return@setOnClickListener } if (isPreviewSuccess) { lifecycleScope.launch(Dispatchers.IO) { dataBeans.forEach { //再拍照 if (isCaptureSuccess) { isCaptureSuccess = false //先执行角度 executeAngle(it.hAngle, it.vAngle) withContext(Dispatchers.Main) { "抓取步骤${it.step}画面".show(context) } val strJpeg = NET_DVR_JPEGPARA() strJpeg.wPicQuality = 1 strJpeg.wPicSize = 2 val imagePath = "/${createImageFileDir()}/${timeFormat.format(Date())}.png" hkSDK.NET_DVR_CaptureJPEGPicture( returnUserId, selectChannel, strJpeg, imagePath ) isCaptureSuccess = MessageCodeHub.getErrorCode() == 0 } } withContext(Dispatchers.Main) { "连续抓取画面完成".show(context) } } } else { "摄像头预览未打开,无法拍照".show(this) } } binding.addButton.setOnClickListener { val step = if (dataBeans.isEmpty()) { 0 } else { dataBeans.last().step } AddCameraPointDialog.Builder() .setContext(this) .setLastStep(step) .setNegativeButton("取消") .setPositiveButton("添加") .setOnDialogButtonClickListener(object : AddCameraPointDialog.OnDialogButtonClickListener { override fun onConfirmClick(step: Int, hAngle: Int, vAngle: Int) { DataBaseManager.get.cacheCameraPoint(step, hAngle, vAngle) dataBeans = DataBaseManager.get.loadAllCameraPoint() dataAdapter.setRefreshData(dataBeans) } override fun onCancelClick() { } }).build().show() } binding.deleteButton.setOnClickListener { if (selectedItems.isEmpty()) { "请先选择要删除的步骤".show(this) return@setOnClickListener } DataBaseManager.get.deleteCameraPoints(selectedItems) dataBeans = DataBaseManager.get.loadAllCameraPoint() dataAdapter.setRefreshData(dataBeans) selectedItems.clear() } binding.steeringWheelView.setOnWheelTouchListener(object : SteeringWheelView.OnWheelTouchListener { override fun onCenterClicked() { if (!isPreviewSuccess) { val deviceItem = SDKGuider.sdkGuider.devManageGuider.DeviceItem() deviceItem.szDevName = "" deviceItem.devNetInfo = SDKGuider.sdkGuider.devManageGuider.DevNetInfo( ProjectConstant.HK_NET_IP, ProjectConstant.HK_NET_PORT, ProjectConstant.HK_NET_USERNAME, ProjectConstant.HK_NET_PASSWORD ) if (deviceItem.szDevName.isEmpty()) { deviceItem.szDevName = deviceItem.devNetInfo.szIp } val loginV40Jna = SDKGuider.sdkGuider.devManageGuider.login_v40_jna( deviceItem.szDevName, deviceItem.devNetInfo ) if (loginV40Jna) { //配置设备通道 try { val deviceInfo = SDKGuider.sdkGuider.devManageGuider.devList[0] returnUserId = deviceInfo.szUserId aChannelNum = deviceInfo.deviceInfoV40_jna.struDeviceV30.byChanNum.toInt() startAChannel = deviceInfo.deviceInfoV40_jna.struDeviceV30.byStartChan.toInt() dChannelNum = deviceInfo.deviceInfoV40_jna.struDeviceV30.byIPChanNum + deviceInfo.deviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256 startDChannel = deviceInfo.deviceInfoV40_jna.struDeviceV30.byStartChan.toInt() var iAnalogStartChan = startAChannel var iDigitalStartChan = startDChannel val channelList = ArrayList<String>() for (i in 0 until aChannelNum) { channelList.add("ACamera_$iAnalogStartChan") iAnalogStartChan++ } for (i in 0 until dChannelNum) { channelList.add("DCamera_$iDigitalStartChan") iDigitalStartChan++ } selectChannel = Integer.valueOf(channelList[0].getChannel()) val streamList = ArrayList<String>() streamList.add("main_stream") streamList.add("sub_stream") streamList.add("third_stream") //开始预览 if (previewHandle != -1) { SDKGuider.sdkGuider.devPreviewGuider.RealPlay_Stop_jni(previewHandle) } val strutPlayInfo = NET_DVR_PREVIEWINFO() strutPlayInfo.lChannel = selectChannel strutPlayInfo.dwStreamType = 1 strutPlayInfo.bBlocked = 1 strutPlayInfo.hHwnd = binding.surfaceView.holder previewHandle = SDKGuider.sdkGuider.devPreviewGuider.RealPlay_V40_jni( returnUserId, strutPlayInfo, null ) if (previewHandle < 0) { Log.d(kTag, "initEvent: Err:${MessageCodeHub.getErrorCode()}") return } isPreviewSuccess = true "预览开启成功".show(context) //开始计时 timer = Timer() timerTask = object : TimerTask() { override fun run() { seconds++ lifecycleScope.launch(Dispatchers.Main) { binding.runningTimeView.text = seconds.toTime() } } } timer?.schedule(timerTask, 0, 1000) } catch (e: IndexOutOfBoundsException) { e.printStackTrace() "设备未正常连接,无法开启预览".show(context) } } } else { if (!SDKGuider.sdkGuider.devPreviewGuider.RealPlay_Stop_jni(previewHandle)) { return } previewHandle = -1 isPreviewSuccess = false "预览关闭成功".show(context) //停止计时 timerTask?.cancel() timer?.cancel() } } override fun onLeftTurn() { if (isPreviewSuccess && isActionUp) { hkSDK.NET_DVR_PTZControlWithSpeed( previewHandle, PTZCommand.PAN_LEFT, 0, selectedSpeed ) isActionUp = false } } override fun onTopTurn() { if (isPreviewSuccess && isActionUp) { hkSDK.NET_DVR_PTZControlWithSpeed( previewHandle, PTZCommand.TILT_UP, 0, selectedSpeed ) isActionUp = false } } override fun onRightTurn() { if (isPreviewSuccess && isActionUp) { hkSDK.NET_DVR_PTZControlWithSpeed( previewHandle, PTZCommand.PAN_RIGHT, 0, selectedSpeed ) isActionUp = false } } override fun onBottomTurn() { if (isPreviewSuccess && isActionUp) { hkSDK.NET_DVR_PTZControlWithSpeed( previewHandle, PTZCommand.TILT_DOWN, 0, selectedSpeed ) isActionUp = false } } override fun onActionTurnUp(dir: SteeringWheelView.Direction) { when (dir) { SteeringWheelView.Direction.LEFT -> { hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_LEFT, 1) isActionUp = true } SteeringWheelView.Direction.TOP -> { hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.TILT_UP, 1) isActionUp = true } SteeringWheelView.Direction.RIGHT -> { hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_RIGHT, 1) isActionUp = true } SteeringWheelView.Direction.BOTTOM -> { hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.TILT_DOWN, 1) isActionUp = true } } } }) binding.upScaleButton.setOnTouchListener { _, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { if (isPreviewSuccess && isScaleButtonUp) { hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.ZOOM_IN, 0) isScaleButtonUp = false } } MotionEvent.ACTION_UP -> { isScaleButtonUp = true hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.ZOOM_IN, 1) } } false } binding.downScaleButton.setOnTouchListener { _, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { if (isPreviewSuccess && isScaleButtonUp) { hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.ZOOM_OUT, 0) isScaleButtonUp = false } } MotionEvent.ACTION_UP -> { isScaleButtonUp = true hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.ZOOM_OUT, 1) } } false } } /** * 角度执行 * */ private fun executeAngle(horizontal: Int, vertical: Int) { //建立透明通道 val serialStart = NET_DVR_SERIALSTART_V40() //串口类型:1- 232 串口,2- 485 串口 serialStart.dwSerialPort = 2 //串口编号 serialStart.wPort = 0 val serialHandle = hkSDK.NET_DVR_SerialStart_V40( returnUserId, serialStart ) { _, _, _, _ -> } //向透明通道发送数据,水平和垂直需要分开发送 hkSDK.NET_DVR_SerialSend( serialHandle, 1, horizontal.createHorizontalCommand(), horizontal.createHorizontalCommand().size ) try { Thread.sleep(50) } catch (e: InterruptedException) { e.printStackTrace() } hkSDK.NET_DVR_SerialSend( serialHandle, 1, vertical.createVerticalCommand(), vertical.createVerticalCommand().size ) try { Thread.sleep(50) } catch (e: InterruptedException) { e.printStackTrace() } //关闭通道 hkSDK.NET_DVR_SerialStop(serialHandle) } override fun observeRequestState() { } override fun surfaceCreated(holder: SurfaceHolder) { binding.surfaceView.holder.setFormat(PixelFormat.TRANSLUCENT) if (-1 == previewHandle) { return } val surface = holder.surface if (surface.isValid) { if (-1 == SDKGuider.sdkGuider.devPreviewGuider.RealPlaySurfaceChanged_jni( previewHandle, 0, holder ) ) { Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") } } } override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { } override fun surfaceDestroyed(holder: SurfaceHolder) { if (-1 == previewHandle) { return } if (holder.surface.isValid) { if (-1 == SDKGuider.sdkGuider.devPreviewGuider.RealPlaySurfaceChanged_jni( previewHandle, 0, null ) ) { Log.d(kTag, "surfaceDestroyed: ${MessageCodeHub.getErrorCode()}") } } } override fun setupTopBarLayout() { ImmersionBar.with(this).statusBarDarkFont(false).init() //根据不同设备状态栏高度设置statusBarView高度 val statusBarHeight = getStatusBarHeight() binding.rootView.setPadding(0, statusBarHeight, 0, 0) binding.rootView.requestLayout() } override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { if (keyCode == KeyEvent.KEYCODE_BACK) { if (System.currentTimeMillis() - clickTime > 2000) { "再按一次退出应用".show(this) clickTime = System.currentTimeMillis() return true } else { super.onKeyDown(keyCode, event) } } return super.onKeyDown(keyCode, event) } }