makcar/app/src/main/java/com/aros/apron/activity/MainActivity.kt

1484 lines
63 KiB
Kotlin
Raw Normal View History

2026-01-30 11:47:32 +08:00
package com.aros.apron.activity
2026-03-12 14:57:34 +08:00
2026-01-30 11:47:32 +08:00
import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
2026-06-06 20:09:36 +08:00
import android.util.Log
2026-01-30 11:47:32 +08:00
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.widget.Button
2026-06-06 20:09:36 +08:00
import android.widget.Toast
2026-01-30 11:47:32 +08:00
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import com.aros.apron.R
import com.aros.apron.base.BaseActivity
import com.aros.apron.callback.MqttCallBack
import com.aros.apron.entity.Movement
2026-04-03 20:41:05 +08:00
import com.aros.apron.entity.Synchronizedstatus
2026-01-30 11:47:32 +08:00
import com.aros.apron.manager.AlternateLandingManager
import com.aros.apron.manager.BatteryManager
import com.aros.apron.manager.CameraManager
import com.aros.apron.manager.FlightManager
import com.aros.apron.manager.FlightManager.FLAG_DOWN_LAND
import com.aros.apron.manager.FlightManager.FLAG_START_DETECT_ARUCO_ALTERNATE
import com.aros.apron.manager.FlightManager.FLAG_START_DETECT_ARUCO_APRON
import com.aros.apron.manager.FlightManager.FLAG_STOP_ARUCO
import com.aros.apron.manager.FlightTaskProgressManager
import com.aros.apron.manager.GimbalManager
import com.aros.apron.manager.LEDsSettingsManager
import com.aros.apron.manager.LTEManager
2026-05-25 10:46:57 +08:00
import com.aros.apron.manager.MLTEManager
2026-01-30 11:47:32 +08:00
import com.aros.apron.manager.MediaManager
import com.aros.apron.manager.MissionV3Manager
2026-04-03 20:41:05 +08:00
import com.aros.apron.manager.NavigationSatelliteSystemManager
2026-01-30 11:47:32 +08:00
import com.aros.apron.manager.OSDManager
2026-04-08 13:39:51 +08:00
import com.aros.apron.manager.PayloadWidgetManager
2026-04-03 20:41:05 +08:00
import com.aros.apron.manager.RTKManager
2026-04-08 13:43:50 +08:00
import com.aros.apron.manager.SpeakerManager
2026-01-30 11:47:32 +08:00
import com.aros.apron.manager.StickManager
import com.aros.apron.manager.StreamManager
import com.aros.apron.manager.WirelessLinkManager
2026-04-08 13:43:50 +08:00
import com.aros.apron.mix.Aprondown
import com.aros.apron.mix.Aprongim
2026-01-30 11:47:32 +08:00
import com.aros.apron.tools.AlternateArucoDetect
2026-06-15 17:13:02 +08:00
import com.aros.apron.tools.AprilTagPort
2026-01-30 11:47:32 +08:00
import com.aros.apron.tools.ApronArucoDetect
2026-04-03 20:41:05 +08:00
import com.aros.apron.tools.ApronArucoDetectPort
2026-06-06 20:09:36 +08:00
import com.aros.apron.tools.ApronArucodownmany
2026-01-30 11:47:32 +08:00
import com.aros.apron.tools.DroneHelper
import com.aros.apron.tools.LogUtil
import com.aros.apron.tools.MqttManager
import com.aros.apron.tools.PreferenceUtils
2026-03-12 14:57:34 +08:00
import com.aros.apron.tools.SimplePortScanner
2026-01-30 11:47:32 +08:00
import com.dji.wpmzsdk.manager.WPMZManager
2026-06-15 17:13:02 +08:00
import com.example.nativec.ApriltagDetector
2026-01-30 11:47:32 +08:00
import com.google.gson.Gson
2026-04-03 20:41:05 +08:00
import dji.sdk.keyvalue.key.CameraKey
2026-01-30 11:47:32 +08:00
import dji.sdk.keyvalue.key.DJIKey
import dji.sdk.keyvalue.key.FlightControllerKey
import dji.sdk.keyvalue.key.KeyTools
2026-04-25 15:36:59 +08:00
import dji.sdk.keyvalue.value.camera.CameraMode
2026-01-30 11:47:32 +08:00
import dji.sdk.keyvalue.value.common.CameraLensType
import dji.sdk.keyvalue.value.common.ComponentIndexType
import dji.sdk.keyvalue.value.common.EmptyMsg
2026-02-01 14:34:23 +08:00
import dji.sdk.keyvalue.value.flightassistant.VisionAssistDirection
2026-03-04 11:20:07 +08:00
import dji.sdk.keyvalue.value.flightcontroller.LEDsSettings
2026-01-30 11:47:32 +08:00
import dji.v5.common.callback.CommonCallbacks
import dji.v5.common.callback.CommonCallbacks.CompletionCallback
2026-01-30 11:47:32 +08:00
import dji.v5.common.error.IDJIError
import dji.v5.common.utils.GeoidManager
import dji.v5.manager.KeyManager
import dji.v5.manager.datacenter.MediaDataCenter
import dji.v5.manager.interfaces.ICameraStreamManager
import dji.v5.manager.interfaces.ICameraStreamManager.AvailableCameraUpdatedListener
import dji.v5.network.DJINetworkManager
import dji.v5.network.IDJINetworkStatusListener
import dji.v5.utils.common.JsonUtil
import dji.v5.utils.common.LogPath
import dji.v5.utils.common.LogUtils
import dji.v5.ux.accessory.RTKStartServiceHelper.startRtkService
import dji.v5.ux.cameracore.widget.autoexposurelock.AutoExposureLockWidget
import dji.v5.ux.cameracore.widget.cameracontrols.CameraControlsWidget
import dji.v5.ux.cameracore.widget.cameracontrols.lenscontrol.LensControlWidget
import dji.v5.ux.cameracore.widget.focusexposureswitch.FocusExposureSwitchWidget
import dji.v5.ux.cameracore.widget.focusmode.FocusModeWidget
import dji.v5.ux.cameracore.widget.fpvinteraction.FPVInteractionWidget
import dji.v5.ux.core.base.SchedulerProvider.io
import dji.v5.ux.core.base.SchedulerProvider.ui
import dji.v5.ux.core.communication.BroadcastValues
import dji.v5.ux.core.communication.GlobalPreferenceKeys
import dji.v5.ux.core.communication.ObservableInMemoryKeyedStore
import dji.v5.ux.core.communication.UXKeys
import dji.v5.ux.core.extension.hide
import dji.v5.ux.core.extension.toggleVisibility
import dji.v5.ux.core.panel.systemstatus.SystemStatusListPanelWidget
import dji.v5.ux.core.panel.topbar.TopBarPanelWidget
import dji.v5.ux.core.util.CameraUtil
import dji.v5.ux.core.util.DataProcessor
import dji.v5.ux.core.util.ViewUtil
import dji.v5.ux.core.widget.fpv.FPVStreamSourceListener
import dji.v5.ux.core.widget.fpv.FPVWidget
import dji.v5.ux.core.widget.hsi.HorizontalSituationIndicatorWidget
import dji.v5.ux.core.widget.hsi.PrimaryFlightDisplayWidget
import dji.v5.ux.core.widget.setting.SettingWidget
import dji.v5.ux.gimbal.GimbalFineTuneWidget
import dji.v5.ux.training.simulatorcontrol.SimulatorControlWidget
import dji.v5.ux.training.simulatorcontrol.SimulatorControlWidget.UIState.VisibilityUpdated
import dji.v5.ux.visualcamera.CameraNDVIPanelWidget
import dji.v5.ux.visualcamera.CameraVisiblePanelWidget
import dji.v5.ux.visualcamera.zoom.FocalZoomWidget
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.functions.Consumer
import org.eclipse.paho.client.mqttv3.MqttException
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
2026-02-01 14:34:23 +08:00
import org.opencv.objdetect.Dictionary
import org.opencv.objdetect.Objdetect
2026-01-30 11:47:32 +08:00
import java.util.concurrent.TimeUnit
open class MainActivity : BaseActivity() {
companion object {
// 如果不需要改变 isAppStarted 的值,可以直接这样声明
var isAppStarted: Boolean = false
var streamReceive: Boolean = false
2026-06-06 20:09:36 +08:00
var isscousse: Boolean=false;
2026-04-03 20:41:05 +08:00
private var instance: MainActivity? = null
2026-01-30 11:47:32 +08:00
2026-04-03 20:41:05 +08:00
fun getInstance(): MainActivity? {
return instance
}
private var vtxHeartbeatHandler: Handler? = null
private var lastVtxFrameTime: Long = 0
2026-06-06 20:09:36 +08:00
private var lastPortToastTime: Long = 0
private var lastFpvToastTime: Long = 0
private const val TOAST_INTERVAL_MS = 3000L
2026-04-03 20:41:05 +08:00
private const val VTX_HEARTBEAT_TIMEOUT = 2000L // 3秒没有收到帧就认为图传异常
private var isVtxHeartbeatRunning = false
fun startVtxHeartbeat() {
if (isVtxHeartbeatRunning) return
isVtxHeartbeatRunning = true
lastVtxFrameTime = System.currentTimeMillis()
vtxHeartbeatHandler = Handler(Looper.getMainLooper())
vtxHeartbeatHandler?.post(object : Runnable {
override fun run() {
val currentTime = System.currentTimeMillis()
val timeSinceLastFrame = currentTime - lastVtxFrameTime
if (timeSinceLastFrame > VTX_HEARTBEAT_TIMEOUT) {
//LogUtil.log("MainActivity", "图传心跳超时设置图传状态为false")
Movement.getInstance().isVtx = false
}
if (isVtxHeartbeatRunning) {
vtxHeartbeatHandler?.postDelayed(this, 1000) // 每秒检查一次
}
}
})
}
fun stopVtxHeartbeat() {
isVtxHeartbeatRunning = false
vtxHeartbeatHandler?.removeCallbacksAndMessages(null)
vtxHeartbeatHandler = null
}
fun updateVtxHeartbeat() {
lastVtxFrameTime = System.currentTimeMillis()
}
2026-05-20 10:15:41 +08:00
@JvmStatic
fun setStartArucoType(type: Int) {
instance?.startArucoType = type
}
2026-01-30 11:47:32 +08:00
}
protected var primaryFpvWidget: FPVWidget? = null
protected var fpvInteractionWidget: FPVInteractionWidget? = null
protected var secondaryFPVWidget: FPVWidget? = null
protected var systemStatusListPanelWidget: SystemStatusListPanelWidget? = null
protected var simulatorControlWidget: SimulatorControlWidget? = null
protected var lensControlWidget: LensControlWidget? = null
protected var autoExposureLockWidget: AutoExposureLockWidget? = null
protected var focusModeWidget: FocusModeWidget? = null
protected var focusExposureSwitchWidget: FocusExposureSwitchWidget? = null
protected var cameraControlsWidget: CameraControlsWidget? = null
protected var horizontalSituationIndicatorWidget: HorizontalSituationIndicatorWidget? = null
protected var pfvFlightDisplayWidget: PrimaryFlightDisplayWidget? = null
protected var ndviCameraPanel: CameraNDVIPanelWidget? = null
protected var visualCameraPanel: CameraVisiblePanelWidget? = null
protected var focalZoomWidget: FocalZoomWidget? = null
protected var settingWidget: SettingWidget? = null
protected var topBarPanel: TopBarPanelWidget? = null
protected var fpvParentView: ConstraintLayout? = null
2026-04-03 20:41:05 +08:00
2026-01-30 11:47:32 +08:00
private var mDrawerLayout: DrawerLayout? = null
private var gimbalAdjustDone: TextView? = null
private var btn_test: Button? = null
private var btn_test1: Button? = null
2026-03-12 14:57:34 +08:00
private var btn_test2: Button? = null
private var btn_test3: Button? = null
2026-01-30 11:47:32 +08:00
private var gimbalFineTuneWidget: GimbalFineTuneWidget? = null
private var lastDevicePosition = ComponentIndexType.UNKNOWN
private var lastLensType = CameraLensType.UNKNOWN
private var compositeDisposable: CompositeDisposable? = null
private val cameraSourceProcessor = DataProcessor.create(
CameraSource(
ComponentIndexType.UNKNOWN,
CameraLensType.UNKNOWN
)
)
private val networkStatusListener =
IDJINetworkStatusListener { isNetworkAvailable: Boolean ->
if (isNetworkAvailable) {
LogUtils.d(TAG, "isNetworkAvailable=" + true)
startRtkService(false)
}
}
private val availableCameraUpdatedListener: AvailableCameraUpdatedListener =
object : AvailableCameraUpdatedListener {
override fun onAvailableCameraUpdated(availableCameraList: List<ComponentIndexType>) {
2026-04-03 20:41:05 +08:00
runOnUiThread {
updateFPVWidgetSource(availableCameraList)
}
2026-01-30 11:47:32 +08:00
}
override fun onCameraStreamEnableUpdate(cameraStreamEnableMap: Map<ComponentIndexType, Boolean>) {
//
}
}
2026-03-12 14:57:34 +08:00
private var cameraManager: ICameraStreamManager =
MediaDataCenter.getInstance().cameraStreamManager
2026-01-30 11:47:32 +08:00
2026-06-06 20:09:36 +08:00
private var startArucoType = 0 //1执行机库二维码识别 2执行备降点二维码识别 3扫到任何码触发刹停
2026-01-30 11:47:32 +08:00
private var dictionary: Dictionary? = null
override fun useEventBus(): Boolean {
return true
}
2026-03-12 14:57:34 +08:00
2026-05-20 10:15:41 +08:00
2026-01-30 11:47:32 +08:00
override fun onResume() {
super.onResume()
2026-03-04 11:20:07 +08:00
2026-02-01 14:34:23 +08:00
dictionary = Objdetect.getPredefinedDictionary(Objdetect.DICT_6X6_250)
2026-03-04 11:20:07 +08:00
2026-01-30 11:47:32 +08:00
compositeDisposable = CompositeDisposable()
2026-04-29 15:44:05 +08:00
// 延迟订阅 RxJava 流,避免在初始 layout 阶段触发大量 widget 更新导致 ANR
Handler(Looper.getMainLooper()).postDelayed({
if (compositeDisposable == null) return@postDelayed
compositeDisposable!!.add(
systemStatusListPanelWidget!!.closeButtonPressed()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { pressed: Boolean ->
if (pressed) {
systemStatusListPanelWidget!!.hide()
2026-01-30 11:47:32 +08:00
}
2026-04-29 15:44:05 +08:00
})
compositeDisposable!!.add(
simulatorControlWidget!!.getUIStateUpdates()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { simulatorControlWidgetState: SimulatorControlWidget.UIState? ->
if (simulatorControlWidgetState is VisibilityUpdated) {
if ((simulatorControlWidgetState as VisibilityUpdated).isVisible) {
hideOtherPanels(simulatorControlWidget!!)
}
}
})
compositeDisposable!!.add(
cameraSourceProcessor.toFlowable()
.observeOn(io())
.throttleLast(500, TimeUnit.MILLISECONDS)
.subscribeOn(io())
.subscribe(Consumer { result: CameraSource ->
2026-06-06 20:09:36 +08:00
runOnUiThread {
onCameraSourceUpdated(
result.devicePosition,
result.lensType
)
}
2026-04-29 15:44:05 +08:00
})
)
compositeDisposable!!.add(
ObservableInMemoryKeyedStore.getInstance()
.addObserver(UXKeys.create(GlobalPreferenceKeys.GIMBAL_ADJUST_CLICKED))
.observeOn(ui())
.subscribe { broadcastValues: BroadcastValues? ->
isGimableAdjustClicked(
broadcastValues!!
)
})
}, 500)
2026-01-30 11:47:32 +08:00
ViewUtil.setKeepScreen(this, true)
}
override fun onPause() {
2026-04-29 15:44:05 +08:00
// 在后台线程 dispose RxJava 订阅,避免阻塞主线程导致 ANR
// DJI UX widget 的 unregisterReactions() 会同步清理 RxJava 队列,可能耗时很长
val disposable = compositeDisposable
if (disposable != null && !disposable.isDisposed) {
Thread {
try {
disposable.dispose()
} catch (e: Exception) {
LogUtil.log(TAG, "dispose compositeDisposable error: ${e.message}")
}
}.start()
2026-01-30 11:47:32 +08:00
compositeDisposable = null
}
super.onPause()
ViewUtil.setKeepScreen(this, false)
}
//endregion
2026-03-12 14:57:34 +08:00
private fun hideOtherPanels(widget: View?) {
2026-01-30 11:47:32 +08:00
val panels = arrayOf<View>(
simulatorControlWidget!!
)
for (panel in panels) {
if (widget !== panel) {
panel.visibility = View.GONE
}
}
}
private fun updateFPVWidgetSource(availableCameraList: List<ComponentIndexType>?) {
LogUtils.i(TAG, JsonUtil.toJson(availableCameraList))
if (availableCameraList == null) {
return
}
val cameraList = java.util.ArrayList(availableCameraList)
//没有数据
if (cameraList.isEmpty()) {
secondaryFPVWidget!!.visibility = View.GONE
return
}
//仅一路数据
if (cameraList.size == 1) {
primaryFpvWidget!!.updateVideoSource(availableCameraList[0])
secondaryFPVWidget!!.visibility = View.GONE
return
}
2026-04-03 20:41:05 +08:00
//大于两路数据ort1
val primarySource = getSuitableSource(cameraList, ComponentIndexType.PORT_1)
2026-01-30 11:47:32 +08:00
primaryFpvWidget!!.updateVideoSource(primarySource!!)
cameraList.remove(primarySource)
val secondarySource = getSuitableSource(cameraList, ComponentIndexType.FPV)
secondaryFPVWidget!!.updateVideoSource(secondarySource!!)
secondaryFPVWidget!!.visibility = View.VISIBLE
}
private fun getSuitableSource(
cameraList: List<ComponentIndexType>,
defaultSource: ComponentIndexType
): ComponentIndexType? {
if (cameraList.contains(ComponentIndexType.LEFT_OR_MAIN)) {
return ComponentIndexType.LEFT_OR_MAIN
} else if (cameraList.contains(ComponentIndexType.RIGHT)) {
return ComponentIndexType.RIGHT
} else if (cameraList.contains(ComponentIndexType.UP)) {
return ComponentIndexType.UP
} else if (cameraList.contains(ComponentIndexType.PORT_1)) {
return ComponentIndexType.PORT_1
} else if (cameraList.contains(ComponentIndexType.PORT_2)) {
return ComponentIndexType.PORT_2
} else if (cameraList.contains(ComponentIndexType.PORT_3)) {
return ComponentIndexType.PORT_3
} else if (cameraList.contains(ComponentIndexType.PORT_4)) {
return ComponentIndexType.PORT_4
} else if (cameraList.contains(ComponentIndexType.VISION_ASSIST)) {
return ComponentIndexType.VISION_ASSIST
}
return defaultSource
}
private fun onCameraSourceUpdated(
devicePosition: ComponentIndexType,
lensType: CameraLensType
) {
LogUtils.i(LogPath.SAMPLE, "onCameraSourceUpdated", devicePosition, lensType)
if (devicePosition == lastDevicePosition && lensType == lastLensType) {
return
}
lastDevicePosition = devicePosition
lastLensType = lensType
updateViewVisibility(devicePosition, lensType)
updateInteractionEnabled()
//如果无需使能或者显示的,也就没有必要切换了。
if (fpvInteractionWidget!!.isInteractionEnabled) {
fpvInteractionWidget!!.updateCameraSource(devicePosition, lensType)
}
if (lensControlWidget!!.visibility == View.VISIBLE) {
lensControlWidget!!.updateCameraSource(devicePosition, lensType)
}
if (ndviCameraPanel!!.visibility == View.VISIBLE) {
ndviCameraPanel!!.updateCameraSource(devicePosition, lensType)
}
if (visualCameraPanel!!.visibility == View.VISIBLE) {
visualCameraPanel!!.updateCameraSource(devicePosition, lensType)
}
if (autoExposureLockWidget!!.visibility == View.VISIBLE) {
autoExposureLockWidget!!.updateCameraSource(devicePosition, lensType)
}
if (focusModeWidget!!.visibility == View.VISIBLE) {
focusModeWidget!!.updateCameraSource(devicePosition, lensType)
}
if (focusExposureSwitchWidget!!.visibility == View.VISIBLE) {
focusExposureSwitchWidget!!.updateCameraSource(devicePosition, lensType)
}
if (cameraControlsWidget!!.visibility == View.VISIBLE) {
cameraControlsWidget!!.updateCameraSource(devicePosition, lensType)
}
if (focalZoomWidget!!.visibility == View.VISIBLE) {
focalZoomWidget!!.updateCameraSource(devicePosition, lensType)
}
if (horizontalSituationIndicatorWidget!!.visibility == View.VISIBLE) {
horizontalSituationIndicatorWidget!!.updateCameraSource(devicePosition, lensType)
}
}
2026-03-12 14:57:34 +08:00
private fun updateViewVisibility(
2026-01-30 11:47:32 +08:00
devicePosition: ComponentIndexType,
lensType: CameraLensType
) {
//只在fpv下显示
pfvFlightDisplayWidget!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.VISIBLE else View.INVISIBLE
//fpv下不显示
lensControlWidget!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.INVISIBLE else View.VISIBLE
ndviCameraPanel!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.INVISIBLE else View.VISIBLE
visualCameraPanel!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.INVISIBLE else View.VISIBLE
autoExposureLockWidget!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.INVISIBLE else View.VISIBLE
focusModeWidget!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.INVISIBLE else View.VISIBLE
focusExposureSwitchWidget!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.INVISIBLE else View.VISIBLE
cameraControlsWidget!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.INVISIBLE else View.VISIBLE
focalZoomWidget!!.visibility =
if (CameraUtil.isFPVTypeView(devicePosition)) View.INVISIBLE else View.VISIBLE
horizontalSituationIndicatorWidget!!.setSimpleModeEnable(
CameraUtil.isFPVTypeView(
devicePosition
)
)
//只在部分len下显示
ndviCameraPanel!!.visibility =
if (CameraUtil.isSupportForNDVI(lensType)) View.VISIBLE else View.INVISIBLE
}
/**
* Swap the video sources of the FPV and secondary FPV widgets.
*/
2026-04-03 20:41:05 +08:00
fun swapVideoSource() {
2026-01-30 11:47:32 +08:00
val primarySource = primaryFpvWidget!!.widgetModel.getCameraIndex()
val secondarySource = secondaryFPVWidget!!.widgetModel.getCameraIndex()
//两个source都存在的情况下才进行切换
if (primarySource != ComponentIndexType.UNKNOWN && secondarySource != ComponentIndexType.UNKNOWN) {
primaryFpvWidget!!.updateVideoSource(secondarySource)
secondaryFPVWidget!!.updateVideoSource(primarySource)
}
}
2026-04-29 15:44:05 +08:00
/**
* 强制刷新两个FPVWidget的视频流重新向MediaDataCenter注册
* 用于起飞/航线执行后视频流卡死的场景
*/
fun refreshVideoStream() {
val primarySource = primaryFpvWidget?.widgetModel?.getCameraIndex()
val secondarySource = secondaryFPVWidget?.widgetModel?.getCameraIndex()
if (primarySource != null && primarySource != ComponentIndexType.UNKNOWN) {
primaryFpvWidget?.updateVideoSource(primarySource)
}
if (secondarySource != null && secondarySource != ComponentIndexType.UNKNOWN) {
secondaryFPVWidget?.updateVideoSource(secondarySource)
}
LogUtil.log(TAG, "强制刷新视频流: primary=$primarySource, secondary=$secondarySource")
}
/**
* 智能刷新视频流有云台(secondary)就模拟点击切换没有就直接刷新当前流
* 兼容只有FPV没有云台的设备
*/
fun smartRefreshVideoStream() {
val secondarySource = secondaryFPVWidget?.widgetModel?.getCameraIndex()
if (secondarySource != null && secondarySource != ComponentIndexType.UNKNOWN) {
// 有云台:模拟真实点击,触发 swapVideoSource()
secondaryFPVWidget?.post {
secondaryFPVWidget?.performClick()
}
2026-05-20 10:15:41 +08:00
//LogUtil.log(TAG, "智能刷新:有云台,模拟点击 FPV Widget")
2026-04-29 15:44:05 +08:00
} else {
// 没有云台:直接刷新当前流
primaryFpvWidget?.post {
refreshVideoStream()
}
2026-06-06 20:09:36 +08:00
//LogUtil.log(TAG, "智能刷新:无云台,直接刷新主视频流")
2026-04-29 15:44:05 +08:00
}
}
2026-01-30 11:47:32 +08:00
private fun updateInteractionEnabled() {
fpvInteractionWidget!!.isInteractionEnabled = !CameraUtil.isFPVTypeView(
primaryFpvWidget!!.widgetModel.getCameraIndex()
)
}
private data class CameraSource(
val devicePosition: ComponentIndexType,
val lensType: CameraLensType
)
2026-03-12 14:57:34 +08:00
2026-01-30 11:47:32 +08:00
override fun onBackPressed() {
if (mDrawerLayout!!.isDrawerOpen(GravityCompat.END)) {
mDrawerLayout!!.closeDrawers()
} else {
super.onBackPressed()
}
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
// window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
setContentView(R.layout.activity_main)
isAppStarted = true
2026-04-03 20:41:05 +08:00
instance = this
2026-01-30 11:47:32 +08:00
// mapWidget = findViewById<MapWidget>(R.id.widget_map)
// mapWidget?.initMapLibreMap(applicationContext, OnMapReadyListener { map: DJIMap ->
// val uiSetting = map.uiSettings
// uiSetting?.setZoomControlsEnabled(false)
2026-04-03 20:41:05 +08:00
//
2026-01-30 11:47:32 +08:00
// mapWidget?.onCreate(savedInstanceState)
GeoidManager.getInstance().init(this)
WPMZManager.getInstance().init(this)
MqttManager.getInstance().needConnect()
initView()
2026-03-12 14:57:34 +08:00
2026-04-29 15:44:05 +08:00
// 后台线程初始化 DJI 相关模块,避免阻塞主线程导致 ANR
Thread {
initDJIManager()
}.start()
2026-03-12 14:57:34 +08:00
2026-01-30 11:47:32 +08:00
}
2026-04-08 13:43:50 +08:00
2026-01-30 11:47:32 +08:00
private fun initView() {
2026-03-12 14:57:34 +08:00
fpvParentView = findViewById(R.id.fpv_holder)
mDrawerLayout = findViewById(R.id.root_view)
topBarPanel = findViewById(R.id.panel_top_bar)
2026-01-30 11:47:32 +08:00
settingWidget = topBarPanel?.settingWidget
2026-03-12 14:57:34 +08:00
primaryFpvWidget = findViewById(R.id.widget_primary_fpv)
fpvInteractionWidget = findViewById(R.id.widget_fpv_interaction)
secondaryFPVWidget = findViewById(R.id.widget_secondary_fpv)
systemStatusListPanelWidget = findViewById(R.id.widget_panel_system_status_list)
simulatorControlWidget = findViewById(R.id.widget_simulator_control)
lensControlWidget = findViewById<LensControlWidget>(R.id.widget_lens_control)
ndviCameraPanel = findViewById(R.id.panel_ndvi_camera)
visualCameraPanel = findViewById(R.id.panel_visual_camera)
autoExposureLockWidget = findViewById(R.id.widget_auto_exposure_lock)
focusModeWidget = findViewById(R.id.widget_focus_mode)
focusExposureSwitchWidget = findViewById(R.id.widget_focus_exposure_switch)
pfvFlightDisplayWidget = findViewById(R.id.widget_fpv_flight_display_widget)
focalZoomWidget = findViewById(R.id.widget_focal_zoom)
cameraControlsWidget = findViewById(R.id.widget_camera_controls)
2026-01-30 11:47:32 +08:00
horizontalSituationIndicatorWidget =
findViewById(R.id.widget_horizontal_situation_indicator)
// gimbalAdjustDone = findViewById<TextView>( R.id.fpv_gimbal_ok_btn)
2026-03-12 14:57:34 +08:00
gimbalFineTuneWidget = findViewById(R.id.setting_menu_gimbal_fine_tune)
btn_test = findViewById(R.id.btn_test)
btn_test1 = findViewById(R.id.btn_test1)
btn_test2 = findViewById(R.id.btn_test2)
2026-04-25 15:36:59 +08:00
// btn_test3 = findViewById(R.id.btn_test3)
2026-01-30 11:47:32 +08:00
btn_test?.setOnClickListener {
2026-04-25 15:36:59 +08:00
// if(cameraMode == 3){
// //退出回放模式
// MediaManager.getInstance().disablePlayback();
// }
KeyManager.getInstance().setValue<CameraMode?>(
KeyTools.createKey<CameraMode?>(CameraKey.KeyCameraMode, ComponentIndexType.PORT_1),
CameraMode.find(12),
object : CompletionCallback {
override fun onSuccess() {
OSDManager.getInstance().pushFlightAttitude()
}
override fun onFailure(error: IDJIError) {
}
})
2026-03-12 14:57:34 +08:00
2026-01-30 11:47:32 +08:00
}
btn_test1?.setOnClickListener {
2026-04-25 15:36:59 +08:00
KeyManager.getInstance().performAction<EmptyMsg?>(
KeyTools.createKey<EmptyMsg?, EmptyMsg?>(
CameraKey.KeyStartShootPhoto,
ComponentIndexType.PORT_1
),
object : CommonCallbacks.CompletionCallbackWithParam<EmptyMsg?> {
override fun onSuccess(emptyMsg: EmptyMsg?) {
LogUtil.log(TAG, "拍照成功")
}
2026-03-12 14:57:34 +08:00
2026-04-25 15:36:59 +08:00
override fun onFailure(error: IDJIError) {
LogUtil.log(TAG, "拍照失败:" + Gson().toJson(error))
}
})
2026-03-12 14:57:34 +08:00
}
2026-04-25 15:36:59 +08:00
btn_test2?.setOnClickListener {
StickManager.getInstance().enableVirtualStick1()
2026-03-12 14:57:34 +08:00
}
2026-04-25 15:36:59 +08:00
// btn_test3?.setOnClickListener {
// StreamManager.getInstance().startstream()
//
// }
2026-02-01 14:34:23 +08:00
2026-01-30 11:47:32 +08:00
initClickListener()
MediaDataCenter.getInstance().cameraStreamManager.addAvailableCameraUpdatedListener(
availableCameraUpdatedListener
)
2026-04-03 20:41:05 +08:00
2026-01-30 11:47:32 +08:00
primaryFpvWidget?.setOnFPVStreamSourceListener(object : FPVStreamSourceListener {
override fun onStreamSourceUpdated(
devicePosition: ComponentIndexType,
lensType: CameraLensType
) {
cameraSourceProcessor.onNext(
CameraSource(devicePosition, lensType)
)
}
})
//小surfaceView放置在顶部避免被大的遮挡
secondaryFPVWidget?.setSurfaceViewZOrderOnTop(true)
secondaryFPVWidget?.setSurfaceViewZOrderMediaOverlay(true)
window.setBackgroundDrawable(ColorDrawable(Color.BLACK))
//实现RTK监测网络并自动重连机制
DJINetworkManager.getInstance().addNetworkStatusListener(networkStatusListener)
2026-02-01 14:34:23 +08:00
2026-01-30 11:47:32 +08:00
}
2026-03-12 14:57:34 +08:00
private fun isGimableAdjustClicked(broadcastValues: BroadcastValues) {
2026-01-30 11:47:32 +08:00
if (mDrawerLayout!!.isDrawerOpen(GravityCompat.END)) {
mDrawerLayout!!.closeDrawers()
}
horizontalSituationIndicatorWidget!!.visibility = View.GONE
if (gimbalFineTuneWidget != null) {
gimbalFineTuneWidget!!.visibility = View.VISIBLE
}
}
2026-03-12 14:57:34 +08:00
private fun initClickListener() {
2026-01-30 11:47:32 +08:00
secondaryFPVWidget!!.setOnClickListener { v: View? -> swapVideoSource() }
if (settingWidget != null) {
settingWidget!!.setOnClickListener { v: View? -> toggleRightDrawer() }
}
// Setup top bar state callbacks
val systemStatusWidget = topBarPanel!!.systemStatusWidget
systemStatusWidget?.setOnClickListener { v: View? -> systemStatusListPanelWidget!!.toggleVisibility() }
val simulatorIndicatorWidget = topBarPanel!!.simulatorIndicatorWidget
simulatorIndicatorWidget?.setOnClickListener { v: View? -> simulatorControlWidget!!.toggleVisibility() }
// gimbalAdjustDone!!.setOnClickListener { view: View? ->
// horizontalSituationIndicatorWidget!!.visibility = View.VISIBLE
// if (gimbalFineTuneWidget != null) {
// gimbalFineTuneWidget!!.visibility = View.GONE
// }
// }
}
2026-03-12 14:57:34 +08:00
private fun toggleRightDrawer() {
2026-01-30 11:47:32 +08:00
mDrawerLayout!!.openDrawer(GravityCompat.END)
}
override fun onDestroy() {
2026-03-12 14:57:34 +08:00
2026-01-30 11:47:32 +08:00
super.onDestroy()
isAppStarted = false
2026-04-03 20:41:05 +08:00
stopVtxHeartbeat()
2026-01-30 11:47:32 +08:00
try {
if (MqttManager.getInstance().mqttAndroidClient != null && MqttManager.getInstance().mqttAndroidClient.isConnected) {
MqttManager.getInstance().mqttAndroidClient.unregisterResources()
MqttManager.getInstance().mqttAndroidClient.disconnect() //断开连接
}
} catch (e: MqttException) {
e.printStackTrace()
}
MediaDataCenter.getInstance().cameraStreamManager.removeAvailableCameraUpdatedListener(
availableCameraUpdatedListener
)
DJINetworkManager.getInstance().removeNetworkStatusListener(networkStatusListener)
}
2026-03-12 14:57:34 +08:00
private var initTimes = 0
2026-02-01 14:34:23 +08:00
2026-01-30 11:47:32 +08:00
private fun initDJIManager() {
2026-04-29 15:44:05 +08:00
// 在后台线程中轮询等待飞控/相机连接,避免阻塞主线程
var isFlightControllerConnect =
2026-01-30 11:47:32 +08:00
KeyManager.getInstance().getValue(DJIKey.create(FlightControllerKey.KeyConnection))
2026-04-29 15:44:05 +08:00
var isCameraConnect =
2026-04-03 20:41:05 +08:00
KeyManager.getInstance()
.getValue(KeyTools.createKey(CameraKey.KeyConnection, ComponentIndexType.PORT_1))
2026-04-29 15:44:05 +08:00
val cameraLocationType = PreferenceUtils.getInstance().cameraLocationType
2026-04-03 20:41:05 +08:00
2026-04-29 15:44:05 +08:00
// 根据 cameraLocationType 轮询等待连接
while (true) {
2026-06-06 20:09:36 +08:00
val flightConnected =
isFlightControllerConnect != null && isFlightControllerConnect == true
2026-04-29 15:44:05 +08:00
val cameraConnected = isCameraConnect != null && isCameraConnect == true
2026-04-03 20:41:05 +08:00
2026-04-29 15:44:05 +08:00
val shouldInit = when (cameraLocationType) {
3 -> flightConnected
2026-06-06 20:09:36 +08:00
1, 2, 4, 5 -> flightConnected && cameraConnected
else -> flightConnected && cameraConnected
2026-04-03 20:41:05 +08:00
}
2026-03-04 11:20:07 +08:00
2026-04-29 15:44:05 +08:00
if (shouldInit) break
2026-03-12 14:57:34 +08:00
2026-04-29 15:44:05 +08:00
try {
Thread.sleep(1000)
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
return
}
2026-04-03 20:41:05 +08:00
2026-04-29 15:44:05 +08:00
isFlightControllerConnect =
KeyManager.getInstance().getValue(DJIKey.create(FlightControllerKey.KeyConnection))
isCameraConnect =
KeyManager.getInstance()
2026-06-06 20:09:36 +08:00
.getValue(
KeyTools.createKey(
CameraKey.KeyConnection,
ComponentIndexType.PORT_1
)
)
2026-04-29 15:44:05 +08:00
}
2026-04-08 13:43:50 +08:00
2026-04-29 15:44:05 +08:00
// 飞控/相机连接后,在主线程执行初始化
runOnUiThread {
when (cameraLocationType) {
3 -> initForCameraLocationType3()
4, 5 -> initForCameraLocationType4Or5()
else -> initForCameraLocationTypeDefault()
}
}
}
2026-04-03 20:41:05 +08:00
2026-04-29 15:44:05 +08:00
private fun initForCameraLocationType3() {
initTimes++
LogUtil.log(TAG, "下视觉初始化$initTimes")
cameraManager.setKeepAliveDecoding(true)
FlightManager.getInstance().initFlightInfo()
BatteryManager.getInstance().initBatteryInfo()
StickManager.getInstance().initStickInfo()
GimbalManager.getInstance().initGimbalInfo()
AlternateLandingManager.getInstance().initAlterLandingInfo()
OSDManager.getInstance().initOsd()
FlightTaskProgressManager.getInstance().initFlightTaskProgress()
MediaManager.getInstance().init()
StreamManager.getInstance().initStreamManager()
LEDsSettingsManager.getInstance().initLEDsInfo()
RTKManager.getInstance().initRTKInfo()
NavigationSatelliteSystemManager.getInstance().initNavigationSatelliteSystem()
NavigationSatelliteSystemManager.getInstance().setNavigationSatelliteSystem()
LTEManager.getInstance().initLTEInfo()
WirelessLinkManager.getInstance().initWirelessLink()
CameraManager.getInstance().initCameraInfo()
GeoidManager.getInstance().init(this)
MissionV3Manager.getInstance().initMissionManager()
enableStream()
initFpvStream()
startVtxHeartbeat()
2026-05-20 10:15:41 +08:00
SpeakerManager.getInstance().addMegaphoneInfoListener()
2026-04-29 15:44:05 +08:00
GimbalManager.getInstance().setmode()
2026-05-20 10:15:41 +08:00
PayloadWidgetManager.getInstance().initPayloadInfo()
2026-04-29 15:44:05 +08:00
2026-06-15 17:13:02 +08:00
// ===== AprilTag 初始化 =====
// 相机内参Plumb Bob 标定结果)
ApriltagDetector.getInstance().setCameraParams(
669.256846, // fx
669.564155, // fy
489.4201049, // cx
358.3523993 // cy
)
// 每个 tag 的物理尺寸(米)
val tagSizeMap = mapOf(
248 to 0.167, 247 to 0.167,
246 to 0.071, 244 to 0.071,
242 to 0.063, 241 to 0.061,
245 to 0.084,
250 to 0.187, 249 to 0.187
)
ApriltagDetector.getInstance().setTagSizeMap(tagSizeMap)
ApriltagDetector.getInstance().setTagSize(0.16) // fallback
// 每个 tag 离降落点的偏移(米):[dx右, dy前]
val tagOffsetMap = mapOf(
248 to doubleArrayOf(-0.262, 0.052),
247 to doubleArrayOf( 0.267, 0.052),
246 to doubleArrayOf(-0.052, 0.0),
244 to doubleArrayOf( 0.056, 0.0),
242 to doubleArrayOf(-0.262, -0.194),
241 to doubleArrayOf( 0.267, -0.192),
245 to doubleArrayOf( 0.0, -0.404),
250 to doubleArrayOf(-0.262, -0.483),
249 to doubleArrayOf( 0.267, -0.480)
)
ApriltagDetector.getInstance().setTagOffsets(tagOffsetMap)
// 初始化 C 层检测器
// init(家族名, 纠错bit, 降采样, 高斯模糊sigma, 线程数)
ApriltagDetector.getInstance().init("tag36h11", 2, 2.0, 0.0, 4)
2026-06-06 20:09:36 +08:00
if (PreferenceUtils.getInstance().lteEnable) {
2026-05-25 10:46:57 +08:00
// LogUtil.log("qwq","lteEnable"+PreferenceUtils.getInstance().lteEnable)
MLTEManager.getInstance().initLTEManager()
2026-06-06 20:09:36 +08:00
Handler().postDelayed(Runnable {
MLTEManager.getInstance().setLTEEnhancedTransmissionType()
}, 3000)
2026-05-25 10:46:57 +08:00
}
2026-06-06 20:09:36 +08:00
LogUtil.log("qwq", "lteEnable" + PreferenceUtils.getInstance().lteEnable)
2026-05-25 10:46:57 +08:00
2026-04-29 15:44:05 +08:00
LogUtil.log(TAG, "自定义推流方式:" + PreferenceUtils.getInstance().customStreamType)
Handler(Looper.getMainLooper()).postDelayed({
when (PreferenceUtils.getInstance().customStreamType) {
1 -> StreamManager.getInstance().startLiveWithRTSP()
2 -> StreamManager.getInstance().startLiveWithCustom()
else -> StreamManager.getInstance().startLiveWithCustom()
}
SimplePortScanner.getInstance().startScan()
}, 5000)
2026-03-12 14:57:34 +08:00
2026-04-29 15:44:05 +08:00
LogUtil.log(TAG, "推流类型:" + PreferenceUtils.getInstance().customStreamType)
}
2026-03-12 14:57:34 +08:00
2026-04-29 15:44:05 +08:00
private fun initForCameraLocationType4Or5() {
initTimes++
cameraManager.setKeepAliveDecoding(true)
LogUtil.log(TAG, "云台初始化$initTimes")
FlightManager.getInstance().initFlightInfo()
BatteryManager.getInstance().initBatteryInfo()
StickManager.getInstance().initStickInfo()
GimbalManager.getInstance().initGimbalInfo()
AlternateLandingManager.getInstance().initAlterLandingInfo()
OSDManager.getInstance().initOsd()
FlightTaskProgressManager.getInstance().initFlightTaskProgress()
MediaManager.getInstance().init()
StreamManager.getInstance().initStreamManager()
LEDsSettingsManager.getInstance().initLEDsInfo()
RTKManager.getInstance().initRTKInfo()
LTEManager.getInstance().initLTEInfo()
2026-05-25 10:46:57 +08:00
// LogUtil.log("qwq","lteEnable"+PreferenceUtils.getInstance().lteEnable)
2026-06-06 20:09:36 +08:00
if (PreferenceUtils.getInstance().lteEnable) {
// LogUtil.log("qwq","lteEnable"+PreferenceUtils.getInstance().lteEnable)
2026-05-25 10:46:57 +08:00
MLTEManager.getInstance().initLTEManager()
2026-06-06 20:09:36 +08:00
Handler().postDelayed(Runnable {
MLTEManager.getInstance().setLTEEnhancedTransmissionType()
}, 3000)
2026-05-25 10:46:57 +08:00
}
2026-04-29 15:44:05 +08:00
WirelessLinkManager.getInstance().initWirelessLink()
CameraManager.getInstance().initCameraInfo()
NavigationSatelliteSystemManager.getInstance().initNavigationSatelliteSystem()
NavigationSatelliteSystemManager.getInstance().setNavigationSatelliteSystem()
MissionV3Manager.getInstance().initMissionManager()
enableStream()
initMixedStream()
startVtxHeartbeat()
2026-05-20 10:15:41 +08:00
SpeakerManager.getInstance().addMegaphoneInfoListener()
2026-04-29 15:44:05 +08:00
GeoidManager.getInstance().init(this)
GimbalManager.getInstance().setmode()
2026-05-20 10:15:41 +08:00
PayloadWidgetManager.getInstance().initPayloadInfo()
2026-04-03 20:41:05 +08:00
2026-04-29 15:44:05 +08:00
LogUtil.log(TAG, "自定义推流方式:" + PreferenceUtils.getInstance().customStreamType)
2026-04-03 20:41:05 +08:00
2026-04-29 15:44:05 +08:00
Handler(Looper.getMainLooper()).postDelayed({
when (PreferenceUtils.getInstance().customStreamType) {
1 -> StreamManager.getInstance().startLiveWithRTSP()
2 -> StreamManager.getInstance().startLiveWithCustom()
else -> StreamManager.getInstance().startLiveWithCustom()
}
SimplePortScanner.getInstance().startScan()
}, 5000)
2026-04-03 20:41:05 +08:00
2026-04-29 15:44:05 +08:00
LogUtil.log(TAG, "推流类型:" + PreferenceUtils.getInstance().customStreamType)
}
2026-03-12 14:57:34 +08:00
2026-04-29 15:44:05 +08:00
private fun initForCameraLocationTypeDefault() {
initTimes++
cameraManager.setKeepAliveDecoding(true)
LogUtil.log(TAG, "云台初始化$initTimes")
FlightManager.getInstance().initFlightInfo()
BatteryManager.getInstance().initBatteryInfo()
StickManager.getInstance().initStickInfo()
GimbalManager.getInstance().initGimbalInfo()
AlternateLandingManager.getInstance().initAlterLandingInfo()
OSDManager.getInstance().initOsd()
FlightTaskProgressManager.getInstance().initFlightTaskProgress()
MediaManager.getInstance().init()
StreamManager.getInstance().initStreamManager()
LEDsSettingsManager.getInstance().initLEDsInfo()
RTKManager.getInstance().initRTKInfo()
LTEManager.getInstance().initLTEInfo()
WirelessLinkManager.getInstance().initWirelessLink()
CameraManager.getInstance().initCameraInfo()
NavigationSatelliteSystemManager.getInstance().initNavigationSatelliteSystem()
NavigationSatelliteSystemManager.getInstance().setNavigationSatelliteSystem()
MissionV3Manager.getInstance().initMissionManager()
enableStream()
initCameraStream()
startVtxHeartbeat()
2026-05-20 10:15:41 +08:00
2026-05-25 10:46:57 +08:00
2026-05-20 10:15:41 +08:00
SpeakerManager.getInstance().addMegaphoneInfoListener()
2026-04-29 15:44:05 +08:00
GeoidManager.getInstance().init(this)
GimbalManager.getInstance().setmode()
2026-05-20 10:15:41 +08:00
PayloadWidgetManager.getInstance().initPayloadInfo()
2026-04-03 20:41:05 +08:00
2026-05-25 10:46:57 +08:00
2026-06-06 20:09:36 +08:00
// LogUtil.log("qwq","lteEnable"+PreferenceUtils.getInstance().lteEnable)
2026-05-25 10:46:57 +08:00
2026-06-06 20:09:36 +08:00
if (PreferenceUtils.getInstance().lteEnable) {
2026-05-25 10:46:57 +08:00
// LogUtil.log("qwq","lteEnable"+PreferenceUtils.getInstance().lteEnable)
MLTEManager.getInstance().initLTEManager()
2026-06-06 20:09:36 +08:00
Handler().postDelayed(Runnable {
MLTEManager.getInstance().setLTEEnhancedTransmissionType()
}, 3000)
2026-05-25 10:46:57 +08:00
}
2026-04-29 15:44:05 +08:00
LogUtil.log(TAG, "自定义推流方式:" + PreferenceUtils.getInstance().customStreamType)
2026-04-03 20:41:05 +08:00
2026-04-29 15:44:05 +08:00
Handler(Looper.getMainLooper()).postDelayed({
when (PreferenceUtils.getInstance().customStreamType) {
1 -> StreamManager.getInstance().startLiveWithRTSP()
2 -> StreamManager.getInstance().startLiveWithCustom()
else -> StreamManager.getInstance().startLiveWithCustom()
2026-04-03 20:41:05 +08:00
}
2026-04-29 15:44:05 +08:00
SimplePortScanner.getInstance().startScan()
}, 5000)
LogUtil.log(TAG, "推流类型:" + PreferenceUtils.getInstance().customStreamType)
2026-01-30 11:47:32 +08:00
}
2026-04-03 20:41:05 +08:00
private fun enableStream() {
cameraManager.enableStream(ComponentIndexType.PORT_1, true);
cameraManager.enableStream(ComponentIndexType.FPV, true);
}
2026-01-30 11:47:32 +08:00
@SuppressLint("SuspiciousIndentation")
2026-04-08 13:39:51 +08:00
private fun initMixedStream() {
// 初始化融合视觉降落识别器
2026-04-08 13:43:50 +08:00
//val mixedLanding = MixedVisionLanding.getInstance()
2026-04-08 13:39:51 +08:00
// 为 PORT_1云台相机添加帧监听器
cameraManager.addFrameListener(
ComponentIndexType.PORT_1,
ICameraStreamManager.FrameFormat.YUV420_888
) { frameData, _, _, width, height, _ ->
2026-06-06 20:09:36 +08:00
// LogUtil.log(TAG,"port监听回调了addFrameListener")
// runOnUiThread {
// val now = System.currentTimeMillis()
// if (now - lastPortToastTime >= TOAST_INTERVAL_MS) {
// lastPortToastTime = now
// Toast.makeText(this, "port监听回调了addFrameListener", Toast.LENGTH_SHORT).show()
// }
// }
2026-04-08 13:39:51 +08:00
Movement.getInstance().isVtx = true
updateVtxHeartbeat()
streamReceive = true
2026-06-06 20:09:36 +08:00
2026-04-08 13:39:51 +08:00
// 使用融合视觉识别器处理云台相机帧
2026-04-08 13:43:50 +08:00
//mixedLanding.processGimbalFrame(height, width, frameData, dictionary)
//使用云台
synchronized(Synchronizedstatus.LOCK_OBJ) {
if (!Synchronizedstatus.isIsruningframe() && Synchronizedstatus.isAprongim() && Synchronizedstatus.isSwitchtime()) {
try {
Synchronizedstatus.setIsruningframe(true)
2026-06-06 20:09:36 +08:00
if(!isscousse){
isscousse=true;
LogUtil.log(TAG,"mix视频帧回调了")
}
2026-04-08 13:43:50 +08:00
if (startArucoType == 1) {
Aprongim.getInstance()?.detectArucoTags(
height,
width,
frameData,
dictionary
)
} else if (startArucoType == 2) {
AlternateArucoDetect.getInstance()?.detectArucoTags(
height,
width,
frameData,
dictionary
)
2026-05-20 10:15:41 +08:00
} else if (startArucoType == 3) {
Aprongim.getInstance()?.detectForceTriggerTags(
height,
width,
frameData,
dictionary
)
2026-04-08 13:43:50 +08:00
}
} finally {
Synchronizedstatus.setIsruningframe(false)
}
}
}
2026-04-08 13:39:51 +08:00
}
2026-04-03 20:41:05 +08:00
2026-04-08 13:39:51 +08:00
// 为 FPV下视相机添加帧监听器
cameraManager.addFrameListener(
ComponentIndexType.FPV,
ICameraStreamManager.FrameFormat.YUV420_888
) { frameData, _, _, width, height, _ ->
2026-06-06 20:09:36 +08:00
//LogUtil.log(TAG,"fpv监听回调了addFrameListener")
// runOnUiThread {
// val now = System.currentTimeMillis()
// if (now - lastFpvToastTime >= TOAST_INTERVAL_MS) {
// lastFpvToastTime = now
// Toast.makeText(this, "fpv监听回调了addFrameListener", Toast.LENGTH_SHORT).show()
// }
// }
2026-04-08 13:39:51 +08:00
Movement.getInstance().isVtx = true
updateVtxHeartbeat()
streamReceive = true
// 使用融合视觉识别器处理下视相机帧
2026-04-08 13:43:50 +08:00
//mixedLanding.processDownwardFrame(height, width, frameData, dictionary)
//使用下视觉
synchronized(Synchronizedstatus.LOCK_OBJ) {
if (!Synchronizedstatus.isIsruningframe() && !Synchronizedstatus.isAprongim() && Synchronizedstatus.isSwitchtime()) {
try {
Synchronizedstatus.setIsruningframe(true)
2026-06-06 20:09:36 +08:00
2026-04-08 13:43:50 +08:00
if (startArucoType == 1) {
Aprondown.getInstance()?.detectArucoTags(
height,
width,
frameData,
dictionary
)
} else if (startArucoType == 2) {
AlternateArucoDetect.getInstance()?.detectArucoTags(
height,
width,
frameData,
dictionary
)
2026-05-20 10:15:41 +08:00
} else if (startArucoType == 3) {
Aprondown.getInstance()?.detectForceTriggerTags(
height,
width,
frameData,
dictionary
)
2026-04-08 13:43:50 +08:00
}
} finally {
Synchronizedstatus.setIsruningframe(false)
}
}
}
2026-04-08 13:39:51 +08:00
}
2026-04-03 20:41:05 +08:00
}
@SuppressLint("SuspiciousIndentation")
private fun initFpvStream() {
2026-01-30 11:47:32 +08:00
cameraManager.addFrameListener(
2026-06-15 17:13:02 +08:00
ComponentIndexType.PORT_1,
2026-01-30 11:47:32 +08:00
ICameraStreamManager.FrameFormat.YUV420_888
) { frameData, _, _, width, height, _ ->
2026-03-12 14:57:34 +08:00
Movement.getInstance().isVtx = true
2026-04-03 20:41:05 +08:00
updateVtxHeartbeat()
2026-03-12 14:57:34 +08:00
streamReceive = true
2026-04-03 20:41:05 +08:00
// DualCaptureHelper.getInstance().onFrame(height, width, frameData)
2026-01-30 11:47:32 +08:00
// if (shouldExecute) {
2026-04-03 20:41:05 +08:00
// if (startArucoType == 1&& ApronArucoStatus.getInstance().canProcess(ApronArucoStatus.CameraSource.DOWNWARD)) {
synchronized(Synchronizedstatus.LOCK_OBJ) {
if (!Synchronizedstatus.isIsruningframe()) {
try {
Synchronizedstatus.setIsruningframe(true)
2026-06-06 20:09:36 +08:00
if(!isscousse){
isscousse=true;
LogUtil.log(TAG,"port视频帧回调了")
}
2026-04-03 20:41:05 +08:00
if (startArucoType == 1) {
2026-06-15 17:13:02 +08:00
// ApronArucodownmany.getInstance()?.detectArucoTags(
// height,
// width,
// frameData,
// dictionary
// )
AprilTagPort.getInstance().processFrame(
2026-04-03 20:41:05 +08:00
frameData,
2026-06-15 17:13:02 +08:00
width,
height
2026-04-03 20:41:05 +08:00
)
} else if (startArucoType == 2) {
AlternateArucoDetect.getInstance()?.detectArucoTags(
height,
width,
frameData,
dictionary
)
2026-05-20 10:15:41 +08:00
} else if (startArucoType == 3) {
2026-06-15 17:13:02 +08:00
// ApronArucodownmany.getInstance()?.detectForceTriggerTags(
// height,
// width,
// frameData,
// dictionary
// )
2026-04-03 20:41:05 +08:00
}
} finally {
Synchronizedstatus.setIsruningframe(false)
}
}
2026-03-12 14:57:34 +08:00
}
2026-01-30 11:47:32 +08:00
}
2026-04-03 20:41:05 +08:00
}
2026-03-12 14:57:34 +08:00
2026-05-20 10:15:41 +08:00
2026-04-03 20:41:05 +08:00
@SuppressLint("SuspiciousIndentation")
private fun initCameraStream() {
2026-03-12 14:57:34 +08:00
cameraManager.addFrameListener(
ComponentIndexType.PORT_1,
ICameraStreamManager.FrameFormat.YUV420_888
) { frameData, _, _, width, height, _ ->
Movement.getInstance().isVtx = true
2026-04-03 20:41:05 +08:00
updateVtxHeartbeat()
2026-03-12 14:57:34 +08:00
streamReceive = true
2026-04-03 20:41:05 +08:00
// DualCaptureHelper.getInstance().onFrame(height, width, frameData)
2026-03-12 14:57:34 +08:00
// if (shouldExecute) {
2026-04-03 20:41:05 +08:00
// if (startArucoType == 1&& ApronArucoStatus.getInstance().canProcess(ApronArucoStatus.CameraSource.DOWNWARD)) {
synchronized(Synchronizedstatus.LOCK_OBJ) {
if (!Synchronizedstatus.isIsruningframe()) {
try {
Synchronizedstatus.setIsruningframe(true)
2026-06-06 20:09:36 +08:00
if(!isscousse){
isscousse=true;
LogUtil.log(TAG,"fpv视频帧回调了")
}
2026-04-03 20:41:05 +08:00
if (startArucoType == 1) {
ApronArucoDetectPort.getInstance()?.detectArucoTags(
height,
width,
frameData,
dictionary
)
} else if (startArucoType == 2) {
AlternateArucoDetect.getInstance()?.detectArucoTags(
height,
width,
frameData,
dictionary
)
2026-05-20 10:15:41 +08:00
} else if (startArucoType == 3) {
ApronArucoDetectPort.getInstance()?.detectForceTriggerTags(
height,
width,
frameData,
dictionary
)
2026-04-03 20:41:05 +08:00
}
} finally {
Synchronizedstatus.setIsruningframe(false)
}
}
2026-03-12 14:57:34 +08:00
}
}
2026-04-03 20:41:05 +08:00
}
2026-02-01 14:34:23 +08:00
// private val mLoaderCallback: BaseLoaderCallback = object : BaseLoaderCallback(this) {
// override fun onManagerConnected(status: Int) {
// if (status == SUCCESS) {
// LogUtil.log(TAG,"Version Name="+BuildConfig.VERSION_NAME)
// ApronArucoDetect.getInstance().init()
//
// } else {
// super.onManagerConnected(status)
// }
// }
// }
2026-01-30 11:47:32 +08:00
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(message: String?) {
when (message) {
2026-04-25 15:36:59 +08:00
FLAG_START_DETECT_ARUCO_APRON -> {
MediaDataCenter.getInstance().getCameraStreamManager().setVisionAssistViewDirection(
VisionAssistDirection.DOWN,
object : CompletionCallback {
override fun onSuccess() {
LogUtil.log(TAG, "下视开启成功")
}
override fun onFailure(idjiError: IDJIError) {
LogUtil.log(TAG, "下视开启失败")
}
})
val leDsSettings = LEDsSettings()
leDsSettings.setNavigationLEDsOn(false)
leDsSettings.setStatusIndicatorLEDsOn(true)
leDsSettings.setFrontLEDsOn(true)
KeyManager.getInstance().setValue<LEDsSettings?>(
KeyTools.createKey<LEDsSettings?>(FlightControllerKey.KeyLEDsSettings),
leDsSettings,
object : CompletionCallback {
override fun onSuccess() {
LogUtil.log(TAG, "夜航灯关闭成功")
}
override fun onFailure(idjiError: IDJIError) {
LogUtil.log(TAG, "夜航灯关闭失败")
}
})
2026-01-30 11:47:32 +08:00
KeyManager.getInstance().performAction<EmptyMsg>(
KeyTools.createKey<EmptyMsg, EmptyMsg>(FlightControllerKey.KeyStopAutoLanding),
object : CommonCallbacks.CompletionCallbackWithParam<EmptyMsg?> {
override fun onSuccess(emptyMsg: EmptyMsg?) {
LogUtil.log(TAG, "取消降落,识别机库二维码")
2026-04-03 20:41:05 +08:00
if (PreferenceUtils.getInstance().cameraLocationType == 3) {
Handler().postDelayed(Runnable {
2026-06-15 17:13:02 +08:00
if (!ApronArucodownmany.getInstance().isTriggerSuccess) {
2026-04-03 20:41:05 +08:00
LogUtil.log(TAG, "图传异常:飞往备降点")
//测试图传丢失
AlternateLandingManager.getInstance().startTaskProcess(null)
}
}, 6000)
2026-04-25 15:36:59 +08:00
} else if (PreferenceUtils.getInstance().cameraLocationType == 4 || PreferenceUtils.getInstance().cameraLocationType == 5) {
2026-04-08 13:43:50 +08:00
Handler().postDelayed(Runnable {
2026-06-06 20:09:36 +08:00
if (!Aprongim.getInstance().isTriggerSuccess) {
LogUtil.log(TAG, "图传异常:飞往备降点"+ Movement.getInstance().isVtx)
2026-04-08 13:43:50 +08:00
//测试图传丢失
AlternateLandingManager.getInstance().startTaskProcess(null)
}
}, 6000)
2026-04-03 20:41:05 +08:00
} else {
Handler().postDelayed(Runnable {
if (!ApronArucoDetectPort.getInstance().isTriggerSuccess) {
LogUtil.log(TAG, "图传异常:飞往备降点")
//测试图传丢失
AlternateLandingManager.getInstance().startTaskProcess(null)
}
}, 6000)
}
2026-01-30 11:47:32 +08:00
if (startArucoType == 1) {
return
}
startArucoType = 1
ApronArucoDetect.getInstance().setDetectedBigMarkers()
2026-04-03 20:41:05 +08:00
ApronArucoDetectPort.getInstance().setDetectedBigMarkers()
2026-04-08 13:43:50 +08:00
Aprongim.getInstance().setDetectedBigMarkers()
Aprondown.getInstance().setDetectedBigMarkers()
2026-06-15 17:13:02 +08:00
ApronArucodownmany.getInstance().setDetectedBigMarkers()
AprilTagPort.getInstance().reset()
2026-04-08 13:43:50 +08:00
2026-01-30 11:47:32 +08:00
DroneHelper.getInstance().setGimbalPitchDegree()
//每次触发识别二维码时,为避免获取控制权失败,使多次获取控制权
DroneHelper.getInstance().isVirtualStickEnable = false
DroneHelper.getInstance().setVerticalModeToVelocity()
}
2026-04-25 15:36:59 +08:00
2026-01-30 11:47:32 +08:00
override fun onFailure(error: IDJIError) {
if (startArucoType == 1) {
return
}
startArucoType = 1
LogUtil.log(TAG, "取消降落,识别机库二维码失败:" + Gson().toJson(error))
ApronArucoDetect.getInstance().setDetectedBigMarkers()
2026-04-03 20:41:05 +08:00
ApronArucoDetectPort.getInstance().setDetectedBigMarkers()
2026-04-08 13:43:50 +08:00
Aprongim.getInstance().setDetectedBigMarkers()
Aprondown.getInstance().setDetectedBigMarkers()
2026-06-15 17:13:02 +08:00
ApronArucodownmany.getInstance().setDetectedBigMarkers()
AprilTagPort.getInstance().reset()
2026-04-08 13:43:50 +08:00
2026-01-30 11:47:32 +08:00
DroneHelper.getInstance().setGimbalPitchDegree()
//每次触发识别二维码时,为避免获取控制权失败,使多次获取控制权
2026-03-12 14:57:34 +08:00
DroneHelper.getInstance().isVirtualStickEnable = false
2026-01-30 11:47:32 +08:00
DroneHelper.getInstance().setVerticalModeToVelocity()
}
})
2026-04-25 15:36:59 +08:00
}
2026-06-06 20:09:36 +08:00
2026-01-30 11:47:32 +08:00
FLAG_START_DETECT_ARUCO_ALTERNATE ->
KeyManager.getInstance().performAction<EmptyMsg>(
KeyTools.createKey<EmptyMsg, EmptyMsg>(FlightControllerKey.KeyStopAutoLanding),
object : CommonCallbacks.CompletionCallbackWithParam<EmptyMsg?> {
override fun onSuccess(emptyMsg: EmptyMsg?) {
LogUtil.log(TAG, "取消降落,识别备降点二维码")
2026-04-03 20:41:05 +08:00
if (PreferenceUtils.getInstance().cameraLocationType == 3) {
Handler().postDelayed(Runnable {
2026-04-08 13:43:50 +08:00
if (!ApronArucoDetect.getInstance().isTriggerSuccess) {
LogUtil.log(TAG, "图传异常:飞往备降点")
2026-04-03 20:41:05 +08:00
//测试图传丢失
2026-04-08 13:43:50 +08:00
AlternateLandingManager.getInstance().startTaskProcess(null)
2026-04-03 20:41:05 +08:00
}
2026-04-08 13:43:50 +08:00
}, 6000)
2026-04-25 15:36:59 +08:00
} else if (PreferenceUtils.getInstance().cameraLocationType == 4 || PreferenceUtils.getInstance().cameraLocationType == 5) {
2026-04-08 13:43:50 +08:00
Handler().postDelayed(Runnable {
if (!Aprongim.getInstance().isTriggerSuccess) {
2026-06-06 20:09:36 +08:00
LogUtil.log(TAG, "备降点图传异常:飞往备降点")
2026-04-08 13:43:50 +08:00
AlternateLandingManager.getInstance().startTaskProcess(null)
}
}, 6000)
2026-06-06 20:09:36 +08:00
2026-04-03 20:41:05 +08:00
} else {
Handler().postDelayed(Runnable {
2026-04-08 13:43:50 +08:00
if (!ApronArucoDetectPort.getInstance().isTriggerSuccess) {
LogUtil.log(TAG, "图传异常:飞往备降点")
2026-04-03 20:41:05 +08:00
//测试图传丢失
2026-04-08 13:43:50 +08:00
AlternateLandingManager.getInstance().startTaskProcess(null)
2026-04-03 20:41:05 +08:00
}
2026-04-08 13:43:50 +08:00
}, 6000)
2026-04-03 20:41:05 +08:00
}
2026-01-30 11:47:32 +08:00
if (startArucoType == 2) {
return
}
startArucoType = 2
DroneHelper.getInstance().setGimbalPitchDegree()
//每次触发识别二维码时,为避免获取控制权失败,使多次获取控制权
DroneHelper.getInstance().isVirtualStickEnable = false
DroneHelper.getInstance().setVerticalModeToVelocity()
}
override fun onFailure(error: IDJIError) {
if (startArucoType == 2) {
return
}
startArucoType = 2
LogUtil.log(
TAG,
"取消降落,识别备降点二维码失败:" + Gson().toJson(error)
)
DroneHelper.getInstance().setGimbalPitchDegree()
//每次触发识别二维码时,为避免获取控制权失败,使多次获取控制权
2026-03-12 14:57:34 +08:00
DroneHelper.getInstance().isVirtualStickEnable = false
2026-01-30 11:47:32 +08:00
DroneHelper.getInstance().setVerticalModeToVelocity()
}
})
2026-04-08 13:43:50 +08:00
FLAG_DOWN_LAND -> {
2026-04-08 13:39:51 +08:00
//重置降落变量
ApronArucoDetect.getInstance().setStartAruco(false);
ApronArucoDetectPort.getInstance().setStartAruco(false);
2026-04-08 13:43:50 +08:00
Aprongim.getInstance().setStartAruco(false);
Aprondown.getInstance().setStartAruco(false);
2026-06-15 17:13:02 +08:00
ApronArucodownmany.getInstance().setStartAruco(false);
2026-04-08 13:43:50 +08:00
2026-04-08 13:39:51 +08:00
2026-03-04 11:20:07 +08:00
KeyManager.getInstance().performAction<EmptyMsg>(
KeyTools.createKey<EmptyMsg, EmptyMsg>(FlightControllerKey.KeyStartAutoLanding),
object : CommonCallbacks.CompletionCallbackWithParam<EmptyMsg?> {
override fun onSuccess(emptyMsg: EmptyMsg?) {
startArucoType = 0
LogUtil.log(TAG, "自动降落调用成功")
}
2026-01-30 11:47:32 +08:00
2026-03-04 11:20:07 +08:00
override fun onFailure(error: IDJIError) {
LogUtil.log(TAG, "自动降落调用失败${error.description()}")
}
})
2026-04-08 13:39:51 +08:00
}
2026-01-30 11:47:32 +08:00
FLAG_STOP_ARUCO ->
startArucoType = 0
2026-04-28 11:39:16 +08:00
"REFRESH_VIDEO_SOURCE" -> {
2026-04-29 15:44:05 +08:00
// 智能刷新:有云台模拟点击,没有云台直接刷新
smartRefreshVideoStream()
2026-04-28 11:39:16 +08:00
}
2026-01-30 11:47:32 +08:00
MqttCallBack.FLAG_RESET_CLEAN_MODE ->
setViewVisibilityWithCleanMode()
}
}
2026-03-04 11:20:07 +08:00
2026-01-30 11:47:32 +08:00
private fun setViewVisibilityWithCleanMode() {
if (PreferenceUtils.getInstance().isCleanMode) {
fpvInteractionWidget?.visibility = View.GONE
horizontalSituationIndicatorWidget?.visibility = View.GONE
gimbalFineTuneWidget?.visibility = View.GONE
ndviCameraPanel?.visibility = View.GONE
visualCameraPanel?.visibility = View.GONE
autoExposureLockWidget?.visibility = View.GONE
focusModeWidget?.visibility = View.GONE
focusExposureSwitchWidget?.visibility = View.GONE
cameraControlsWidget?.visibility = View.GONE
focalZoomWidget?.visibility = View.GONE
// returnHomeWidget?.visibility = View.GONE
// takeOffWidget?.visibility = View.GONE
// lensControlWidget?.visibility = View.GONE
simulatorControlWidget?.visibility = View.GONE
pfvFlightDisplayWidget?.visibility = View.GONE
systemStatusListPanelWidget?.visibility = View.GONE
topBarPanel?.visibility = View.GONE
}
}
}