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
|
|
|
|
|
|
import android.view.View
|
|
|
|
|
|
import android.view.Window
|
|
|
|
|
|
import android.view.WindowManager
|
|
|
|
|
|
import android.widget.Button
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
|
|
|
import com.aros.apron.tools.ApronArucoDetect
|
2026-04-03 20:41:05 +08:00
|
|
|
|
import com.aros.apron.tools.ApronArucoDetectPort
|
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
|
|
|
|
|
|
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
|
2026-02-27 10:18:56 +08:00
|
|
|
|
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-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
|
|
|
|
|
|
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-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
|
|
|
|
|
|
|
|
|
|
private var startArucoType = 0 //1执行机库二维码识别 2执行备降点二维码识别
|
|
|
|
|
|
private var dictionary: Dictionary? = null
|
|
|
|
|
|
override fun useEventBus(): Boolean {
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
2026-03-12 14:57:34 +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 ->
|
|
|
|
|
|
runOnUiThread { onCameraSourceUpdated(result.devicePosition, result.lensType) }
|
|
|
|
|
|
})
|
|
|
|
|
|
)
|
|
|
|
|
|
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()
|
|
|
|
|
|
}
|
|
|
|
|
|
LogUtil.log(TAG, "智能刷新:有云台,模拟点击 FPV Widget")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 没有云台:直接刷新当前流
|
|
|
|
|
|
primaryFpvWidget?.post {
|
|
|
|
|
|
refreshVideoStream()
|
|
|
|
|
|
}
|
|
|
|
|
|
LogUtil.log(TAG, "智能刷新:无云台,直接刷新主视频流")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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-02-27 10:18:56 +08:00
|
|
|
|
|
2026-03-12 14:57:34 +08:00
|
|
|
|
}
|
2026-04-25 15:36:59 +08:00
|
|
|
|
btn_test2?.setOnClickListener {
|
|
|
|
|
|
StickManager.getInstance().enableVirtualStick1()
|
2026-02-27 10:18:56 +08:00
|
|
|
|
|
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) {
|
|
|
|
|
|
val flightConnected = isFlightControllerConnect != null && isFlightControllerConnect == true
|
|
|
|
|
|
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
|
|
|
|
|
|
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()
|
|
|
|
|
|
.getValue(KeyTools.createKey(CameraKey.KeyConnection, ComponentIndexType.PORT_1))
|
|
|
|
|
|
}
|
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()
|
|
|
|
|
|
SpeakerManager.getInstance().initMegaphoneInfo()
|
|
|
|
|
|
GimbalManager.getInstance().setmode()
|
|
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
WirelessLinkManager.getInstance().initWirelessLink()
|
|
|
|
|
|
CameraManager.getInstance().initCameraInfo()
|
|
|
|
|
|
NavigationSatelliteSystemManager.getInstance().initNavigationSatelliteSystem()
|
|
|
|
|
|
NavigationSatelliteSystemManager.getInstance().setNavigationSatelliteSystem()
|
|
|
|
|
|
MissionV3Manager.getInstance().initMissionManager()
|
|
|
|
|
|
enableStream()
|
|
|
|
|
|
initMixedStream()
|
|
|
|
|
|
startVtxHeartbeat()
|
|
|
|
|
|
SpeakerManager.getInstance().initMegaphoneInfo()
|
|
|
|
|
|
GeoidManager.getInstance().init(this)
|
|
|
|
|
|
GimbalManager.getInstance().setmode()
|
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()
|
|
|
|
|
|
SpeakerManager.getInstance().initMegaphoneInfo()
|
|
|
|
|
|
GeoidManager.getInstance().init(this)
|
|
|
|
|
|
GimbalManager.getInstance().setmode()
|
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()
|
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, _ ->
|
|
|
|
|
|
Movement.getInstance().isVtx = true
|
|
|
|
|
|
updateVtxHeartbeat()
|
|
|
|
|
|
streamReceive = true
|
|
|
|
|
|
// 使用融合视觉识别器处理云台相机帧
|
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)
|
|
|
|
|
|
if (startArucoType == 1) {
|
|
|
|
|
|
Aprongim.getInstance()?.detectArucoTags(
|
|
|
|
|
|
height,
|
|
|
|
|
|
width,
|
|
|
|
|
|
frameData,
|
|
|
|
|
|
dictionary
|
|
|
|
|
|
)
|
|
|
|
|
|
} else if (startArucoType == 2) {
|
|
|
|
|
|
AlternateArucoDetect.getInstance()?.detectArucoTags(
|
|
|
|
|
|
height,
|
|
|
|
|
|
width,
|
|
|
|
|
|
frameData,
|
|
|
|
|
|
dictionary
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
} 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, _ ->
|
|
|
|
|
|
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)
|
|
|
|
|
|
if (startArucoType == 1) {
|
|
|
|
|
|
Aprondown.getInstance()?.detectArucoTags(
|
|
|
|
|
|
height,
|
|
|
|
|
|
width,
|
|
|
|
|
|
frameData,
|
|
|
|
|
|
dictionary
|
|
|
|
|
|
)
|
|
|
|
|
|
} else if (startArucoType == 2) {
|
|
|
|
|
|
AlternateArucoDetect.getInstance()?.detectArucoTags(
|
|
|
|
|
|
height,
|
|
|
|
|
|
width,
|
|
|
|
|
|
frameData,
|
|
|
|
|
|
dictionary
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
} 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-02-01 14:34:23 +08:00
|
|
|
|
ComponentIndexType.FPV,
|
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)
|
|
|
|
|
|
if (startArucoType == 1) {
|
|
|
|
|
|
ApronArucoDetect.getInstance()?.detectArucoTags(
|
|
|
|
|
|
height,
|
|
|
|
|
|
width,
|
|
|
|
|
|
frameData,
|
|
|
|
|
|
dictionary
|
|
|
|
|
|
)
|
|
|
|
|
|
} else if (startArucoType == 2) {
|
|
|
|
|
|
AlternateArucoDetect.getInstance()?.detectArucoTags(
|
|
|
|
|
|
height,
|
|
|
|
|
|
width,
|
|
|
|
|
|
frameData,
|
|
|
|
|
|
dictionary
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
} 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-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)
|
|
|
|
|
|
if (startArucoType == 1) {
|
|
|
|
|
|
ApronArucoDetectPort.getInstance()?.detectArucoTags(
|
|
|
|
|
|
height,
|
|
|
|
|
|
width,
|
|
|
|
|
|
frameData,
|
|
|
|
|
|
dictionary
|
|
|
|
|
|
)
|
|
|
|
|
|
} else if (startArucoType == 2) {
|
|
|
|
|
|
AlternateArucoDetect.getInstance()?.detectArucoTags(
|
|
|
|
|
|
height,
|
|
|
|
|
|
width,
|
|
|
|
|
|
frameData,
|
|
|
|
|
|
dictionary
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
} 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 -> {
|
2026-04-29 15:44:05 +08:00
|
|
|
|
|
2026-04-25 15:36:59 +08:00
|
|
|
|
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 {
|
|
|
|
|
|
if (!ApronArucoDetect.getInstance().isTriggerSuccess) {
|
|
|
|
|
|
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 {
|
|
|
|
|
|
if (!Aprongim.getInstance().isTriggerSuccess) {
|
|
|
|
|
|
LogUtil.log(TAG, "图传异常:飞往备降点")
|
|
|
|
|
|
//测试图传丢失
|
|
|
|
|
|
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-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-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-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) {
|
|
|
|
|
|
LogUtil.log(TAG, "图传异常:飞往备降点")
|
|
|
|
|
|
//测试图传丢失
|
|
|
|
|
|
AlternateLandingManager.getInstance().startTaskProcess(null)
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 6000)
|
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-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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|