Merge branch 'dev_robotaxi-d_250603_8.1.0' of gitlab.zhidaoauto.com:SCA/L4HA/AndroidApp/MoGoEagleEye into dev_robotaxi-d_250603_8.1.0

This commit is contained in:
xinfengkun
2025-06-18 14:38:31 +08:00
80 changed files with 1807 additions and 412 deletions

View File

@@ -142,8 +142,28 @@ class MoGoAdasMsgConnectStatusListenerImpl :
CallerAutoPilotControlManager.setRainMode(FunctionBuildConfig.isRainMode)
// 6.6.2 版本默认开启,与海江确认过,默认发盲区模式
CallerAutoPilotControlManager.sendFusionMode(2)
CallerAutoPilotControlManager.sendV2iToPncCmd(FunctionBuildConfig.v2iToPNC)
CallerAutoPilotControlManager.sendV2nToPncCmd(FunctionBuildConfig.v2nTotalSwitch)
//事件数据进PNC应用
if(FunctionBuildConfig.ndeEventDataToPnc){
CallerAutoPilotControlManager.sendNdeDownEventToPnc(1)
}else{
CallerAutoPilotControlManager.sendNdeDownEventToPnc(0)
}
//感知数据进PNC应用
if(FunctionBuildConfig.ndePerceptionDataToPnc){
CallerAutoPilotControlManager.sendNdeDownPerceptionToPnc(1)
}else{
CallerAutoPilotControlManager.sendNdeDownPerceptionToPnc(0)
}
//V2I下行感知进PNC开关
if(FunctionBuildConfig.v2iPerceptionDataToPnc){
CallerAutoPilotControlManager.sendV2iDownPerceptionToPnc(1)
}else{
CallerAutoPilotControlManager.sendV2iDownPerceptionToPnc(0)
}
}
AdasConstants.IpcConnectionStatus.CONNECTING -> {

View File

@@ -586,10 +586,16 @@ class TrafficLightDispatcher : IMoGoAutopilotIdentifyListener, IMoGoTrafficLight
hasCloudControlLight = true
//展示云控基础平台信号灯信息
CallerTrafficLightListenerManager.showCloudTrafficLight(currentState, currentDuration, nextState, nextDuration)
}else{
//隐藏云控基础信号红绿灯
CallerTrafficLightListenerManager.disableTrafficLight()
hasCloudControlLight = false
}
}
}else{
hasCloudControlLight = false
//隐藏云控基础信号红绿灯
CallerTrafficLightListenerManager.disableTrafficLight()
}
}

View File

@@ -56,8 +56,14 @@ object OTAUpgradeManager: IMoGoAutopilotStatusListener, IDataCenterBizListener,
}else{
Log.i(TAG,"20分钟没有收到OTA升级推送置为失败")
CallerHmiManager.showOTADownloadStatusDialog(false, emptyList())
CallerHmiManager.showOTAResultDialog(false)
CallerHmiManager.showOTAResultDialog(isShow = true, result = false)
}
}else if(msg.what == 2){
Log.i(TAG,"司机屏弹窗提示用车人执行车辆下电操作")
//如果OTA升级弹窗没有点击关闭则自动关闭
CallerHmiManager.showOTAResultDialog(isShow = false, result = true)
//司机屏弹窗提示用车人执行车辆下电操作
CallerHmiManager.showOTAPowerOffFinishDialog()
}
}
}
@@ -126,7 +132,7 @@ object OTAUpgradeManager: IMoGoAutopilotStatusListener, IDataCenterBizListener,
val token = productInfo.optString("token")
val productStatus = productInfo.optInt("status")
val failReason = productInfo.optString("fail_reason")
val upgradeReason = productInfo.optString("upgrade_reason")
val upgradeReason = productInfo.optString("upgrade_reason")//升级原因
val taskId = productInfo.optInt("task_id")
val taskItemId = productInfo.optInt("task_item_id")
val otaType = productInfo.optInt("ota_type")
@@ -136,6 +142,7 @@ object OTAUpgradeManager: IMoGoAutopilotStatusListener, IDataCenterBizListener,
val curSize = productInfo.optDouble("cur_size")
val totalSize = productInfo.optDouble("total_size")
val taskNumber = productInfo.optInt("task_number")
val leftTime = productInfo.optInt("left_time") //剩余时间,单位秒
Log.i(TAG, "index=$index")
Log.i(TAG, "token=$token")
@@ -150,7 +157,8 @@ object OTAUpgradeManager: IMoGoAutopilotStatusListener, IDataCenterBizListener,
Log.i(TAG, "is_delay=$isDelay")
Log.i(TAG, "cur_size=$curSize")
Log.i(TAG, "total_size=$totalSize")
Log.i(TAG,"taskNumber=$taskNumber")
Log.i(TAG,"task_number=$taskNumber")
Log.i(TAG,"left_time=$leftTime")
if(index == 0){
//是否需要触发提示升级只判断第一个任务
@@ -251,7 +259,7 @@ object OTAUpgradeManager: IMoGoAutopilotStatusListener, IDataCenterBizListener,
val token = productInfo.optString("token")
val productStatus = productInfo.optInt("status")
val failReason = productInfo.optString("fail_reason")
val upgradeReason = productInfo.optString("upgrade_reason")
val upgradeReason = productInfo.optString("upgrade_reason")//升级原因
val taskId = productInfo.optInt("task_id")
val taskItemId = productInfo.optInt("task_item_id")
val otaType = productInfo.optInt("ota_type")
@@ -261,6 +269,7 @@ object OTAUpgradeManager: IMoGoAutopilotStatusListener, IDataCenterBizListener,
val curSize = productInfo.optDouble("cur_size")
val totalSize = productInfo.optDouble("total_size")
val taskNumber = productInfo.optInt("task_number")
val leftTime = productInfo.optInt("left_time")//剩余时间,单位秒
Log.i(TAG, "index=$index")
Log.i(TAG, "token=$token")
@@ -276,6 +285,7 @@ object OTAUpgradeManager: IMoGoAutopilotStatusListener, IDataCenterBizListener,
Log.i(TAG, "cur_size=$curSize")
Log.i(TAG, "total_size=$totalSize")
Log.i(TAG,"task_number=$taskNumber")
Log.i(TAG,"left_time=$leftTime")
if(index == 0){
//是否需要触发提示升级只判断第一个任务
@@ -334,7 +344,13 @@ object OTAUpgradeManager: IMoGoAutopilotStatusListener, IDataCenterBizListener,
OTAUpgradeConfig.isQuery = false
}else{
CallerHmiManager.showOTADownloadStatusDialog(false,otaUpgradeList)
CallerHmiManager.showOTAResultDialog(upgradeResult)
CallerHmiManager.showOTAResultDialog(true,upgradeResult)
//升级成功,自动执行优雅停服
if(upgradeResult){
CallerAutoPilotControlManager.sendIpcPowerOff()
//当优雅停服完成、需要车辆下电的时候比如当前是停服触发60s后车端告知鹰眼司机屏弹窗提示用车人执行车辆下电操作
handler.sendEmptyMessageDelayed(2,60000)
}
}
CallerOTAManager.invokeOtaDownloadStatus(false)
}else{

View File

@@ -54,6 +54,10 @@ import kotlinx.android.synthetic.main.view_car_info_tab.view.tvPADUpdate
import kotlinx.android.synthetic.main.view_car_info_tab.view.tvPadVersion
import kotlinx.android.synthetic.main.view_car_info_tab.view.tvSlamMapVersion
import kotlinx.android.synthetic.main.view_car_info_tab.view.tvSnInfo
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import system_master.SsmInfo
import system_master.SystemStatusInfo
@@ -106,6 +110,7 @@ class CarInfoTabView @JvmOverloads constructor(
CallerMapShowNameManager.removeListener(TAG)
}
@OptIn(DelicateCoroutinesApi::class)
private fun initView() {
val qrCodeView = CallerOchCustomViewManager.getOchBindQRCodeView(context)
@@ -182,8 +187,10 @@ class CarInfoTabView @JvmOverloads constructor(
}
}
MogoData.mogoMapData.get()?.isCityDataCached {
updateHDDataCacheStatus(it)
GlobalScope.launch(Dispatchers.IO){
MogoData.mogoMapData.get()?.isCityDataCached {
updateHDDataCacheStatus(it)
}
}
if(FunctionBuildConfig.isOffLine){

View File

@@ -61,6 +61,7 @@ import com.mogo.eagle.core.function.hmi.ui.setting.ToolsView.Companion.toolsView
import com.mogo.eagle.core.function.hmi.ui.tools.AdUpgradeDialog
import com.mogo.eagle.core.function.hmi.ui.tools.ModifyBindingCarDialog
import com.mogo.eagle.core.function.hmi.ui.tools.OTADownloadStatusDialog
import com.mogo.eagle.core.function.hmi.ui.tools.OTAPowerOffFinishDialog
import com.mogo.eagle.core.function.hmi.ui.tools.OTAUpgradeDialog
import com.mogo.eagle.core.function.hmi.ui.tools.OTAUpgradeResultDialog
import com.mogo.eagle.core.function.hmi.ui.tools.ToBindingCarDialog
@@ -667,6 +668,7 @@ class MoGoHmiProvider : IMoGoHmiProvider {
private var otaUpgradeDialog: OTAUpgradeDialog ?= null
private var otaDownloadStatusDialog: OTADownloadStatusDialog ?= null
private var otaUpgradeResultDialog: OTAUpgradeResultDialog ?= null
private var otaPowerOffFinishDialog: OTAPowerOffFinishDialog ?= null
/**
* 展示OTA升级弹窗
@@ -720,18 +722,42 @@ class MoGoHmiProvider : IMoGoHmiProvider {
/**
* 展示OTA升级结果弹窗
* @param isShow 是否展示
* @param result true升级成功 false升级失败
*/
override fun showOTAResultDialog(result: Boolean) {
override fun showOTAResultDialog(isShow: Boolean,result: Boolean) {
ThreadUtils.runOnUiThread{
context?.let{
if(otaUpgradeResultDialog == null){
otaUpgradeResultDialog = OTAUpgradeResultDialog(it)
if(isShow){
context?.let{
if(otaUpgradeResultDialog == null){
otaUpgradeResultDialog = OTAUpgradeResultDialog(it)
}
if(otaUpgradeResultDialog?.isShowing == false){
otaUpgradeResultDialog?.show()
}
otaUpgradeResultDialog?.showResult(result)
}
if(otaUpgradeResultDialog?.isShowing == false){
otaUpgradeResultDialog?.show()
}else{
if(otaUpgradeResultDialog?.isShowing == true){
otaUpgradeResultDialog?.dismiss()
}
}
}
}
/**
* OTA升级完成且优雅停服完成、需要车辆下电的时候车端告知鹰眼司机屏弹窗提示用车人执行车辆下电操作
*/
override fun showOTAPowerOffFinishDialog() {
ThreadUtils.runOnUiThread {
context?.let {
if(otaPowerOffFinishDialog == null){
otaPowerOffFinishDialog = OTAPowerOffFinishDialog(it)
}
if(otaPowerOffFinishDialog?.isShowing == false){
otaPowerOffFinishDialog?.show()
}
otaUpgradeResultDialog?.showResult(result)
}
}
}

View File

@@ -480,15 +480,15 @@ class OperatePanelLayout : LinearLayout {
//关闭事件数据进PNC应用
CallerAutoPilotControlManager.sendNdeDownEventToPnc(0)
//查询事件数据进PNC应用
UiThreadHandler.postDelayed({
CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.V2N_TO_PNC)
}, 500)
// UiThreadHandler.postDelayed({
// CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.V2N_TO_PNC)
// }, 500)
//关闭感知数据进PNC应用
CallerAutoPilotControlManager.sendNdeDownPerceptionToPnc(0)
//查询感知数据进PNC应用
UiThreadHandler.postDelayed({
CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.PERCEPTION_TO_PNC)
}, 500)
// UiThreadHandler.postDelayed({
// CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.PERCEPTION_TO_PNC)
// }, 500)
}
return true
}
@@ -502,9 +502,9 @@ class OperatePanelLayout : LinearLayout {
CallerAutoPilotControlManager.sendNdeDownEventToPnc(0)
}
//查询事件数据进PNC应用
UiThreadHandler.postDelayed({
CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.V2N_TO_PNC)
}, 500)
// UiThreadHandler.postDelayed({
// CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.V2N_TO_PNC)
// }, 500)
hmiAction("事件数据进PNC应用, ", isChecked)
clickEventAnalytics("事件数据进PNC应用", isChecked)
return true
@@ -519,9 +519,9 @@ class OperatePanelLayout : LinearLayout {
CallerAutoPilotControlManager.sendNdeDownPerceptionToPnc(0)
}
//查询感知数据进PNC应用
UiThreadHandler.postDelayed({
CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.PERCEPTION_TO_PNC)
}, 500)
// UiThreadHandler.postDelayed({
// CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.PERCEPTION_TO_PNC)
// }, 500)
hmiAction("感知数据进PNC应用, ", isChecked)
clickEventAnalytics("感知数据进PNC应用", isChecked)
return true
@@ -588,9 +588,9 @@ class OperatePanelLayout : LinearLayout {
FunctionBuildConfig.v2iPerceptionDataToPnc = false
CallerAutoPilotControlManager.sendV2iDownPerceptionToPnc(0)
//查询V2I下行感知进PNC开关状态
UiThreadHandler.postDelayed({
CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.V2I_TO_PNC)
}, 500)
// UiThreadHandler.postDelayed({
// CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.V2I_TO_PNC)
// }, 500)
}
hmiAction("V2I下行, ", isChecked)
clickEventAnalytics("V2I下行", isChecked)
@@ -615,9 +615,9 @@ class OperatePanelLayout : LinearLayout {
CallerAutoPilotControlManager.sendV2iDownPerceptionToPnc(0)
}
//查询V2I下行感知进PNC开关状态
UiThreadHandler.postDelayed({
CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.V2I_TO_PNC)
}, 500)
// UiThreadHandler.postDelayed({
// CallerAutoPilotControlManager.sendGetParamReq(AdasConstants.MapSystemParamType.V2I_TO_PNC)
// }, 500)
hmiAction("感知数据进PNC应用, ", isChecked)
clickEventAnalytics("感知数据进PNC应用", isChecked)
return true
@@ -639,54 +639,56 @@ class OperatePanelLayout : LinearLayout {
* @param config 数据
*/
override fun onCloudConfig(config: MessagePad.CloudConfig) {
//云连接地址
config.addrsList.forEach {
when(it.type){
//蘑菇云,暂时用不到
0->{
ThreadUtils.runOnUiThread {
//云连接地址
config.addrsList.forEach {
when(it.type){
//蘑菇云,暂时用不到
0->{
}
//NDE云
1->{
when(it.direction){
//上行和下行
0->{
FunctionBuildConfig.ndeUpwardSwitch = it.enable
FunctionBuildConfig.ndeDownwardSwitch = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_UPWARD_SWITCH)?.also { changeValue(it, FunctionBuildConfig.ndeUpwardSwitch) }
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_DOWNWARD_SWITCH)?.also { changeValue(it, FunctionBuildConfig.ndeDownwardSwitch) }
}
//上行
1->{
FunctionBuildConfig.ndeUpwardSwitch = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_UPWARD_SWITCH)?.also { changeValue(it, FunctionBuildConfig.ndeUpwardSwitch) }
}
//下行
2->{
FunctionBuildConfig.ndeDownwardSwitch = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_DOWNWARD_SWITCH)?.also { changeValue(it, FunctionBuildConfig.ndeDownwardSwitch) }
}
//NDE云
1->{
when(it.direction){
//上行和下行
0->{
FunctionBuildConfig.ndeUpwardSwitch = it.enable
FunctionBuildConfig.ndeDownwardSwitch = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_UPWARD_SWITCH)?.also { changeValue(it, FunctionBuildConfig.ndeUpwardSwitch) }
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_DOWNWARD_SWITCH)?.also { changeValue(it, FunctionBuildConfig.ndeDownwardSwitch) }
}
//上行
1->{
FunctionBuildConfig.ndeUpwardSwitch = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_UPWARD_SWITCH)?.also { changeValue(it, FunctionBuildConfig.ndeUpwardSwitch) }
}
//下行
2->{
FunctionBuildConfig.ndeDownwardSwitch = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_DOWNWARD_SWITCH)?.also { changeValue(it, FunctionBuildConfig.ndeDownwardSwitch) }
}
}
}
}
//基础平台云
2->{
when(it.direction){
//上行和下行
0->{
FunctionBuildConfig.cloudControlUpward = it.enable
FunctionBuildConfig.cloudControlDownward = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(CLOUD_CONTROL_UPWARD)?.also { changeValue(it, FunctionBuildConfig.cloudControlUpward) }
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(CLOUD_CONTROL_DOWNWARD)?.also { changeValue(it, FunctionBuildConfig.cloudControlDownward) }
}
//上行
1->{
FunctionBuildConfig.cloudControlUpward = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(CLOUD_CONTROL_UPWARD)?.also { changeValue(it, FunctionBuildConfig.cloudControlUpward) }
}
//下行
2->{
FunctionBuildConfig.cloudControlDownward = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(CLOUD_CONTROL_DOWNWARD)?.also { changeValue(it, FunctionBuildConfig.cloudControlDownward) }
//基础平台云
2->{
when(it.direction){
//上行和下行
0->{
FunctionBuildConfig.cloudControlUpward = it.enable
FunctionBuildConfig.cloudControlDownward = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(CLOUD_CONTROL_UPWARD)?.also { changeValue(it, FunctionBuildConfig.cloudControlUpward) }
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(CLOUD_CONTROL_DOWNWARD)?.also { changeValue(it, FunctionBuildConfig.cloudControlDownward) }
}
//上行
1->{
FunctionBuildConfig.cloudControlUpward = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(CLOUD_CONTROL_UPWARD)?.also { changeValue(it, FunctionBuildConfig.cloudControlUpward) }
}
//下行
2->{
FunctionBuildConfig.cloudControlDownward = it.enable
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(CLOUD_CONTROL_DOWNWARD)?.also { changeValue(it, FunctionBuildConfig.cloudControlDownward) }
}
}
}
}
@@ -698,7 +700,9 @@ class OperatePanelLayout : LinearLayout {
* 查询摄像头上传NDE云状态返回
*/
override fun onImgUploadCloudStatusResp(resp: MessagePad.ImgUploadCloudStatusResp) {
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(IMAGE_UPLOAD_SWITCH)?.also { changeValue(it, resp.enable) }
ThreadUtils.runOnUiThread {
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(IMAGE_UPLOAD_SWITCH)?.also { changeValue(it, resp.enable) }
}
}
/**
@@ -707,12 +711,14 @@ class OperatePanelLayout : LinearLayout {
* @param adasParam 解析后的配置参数
*/
override fun onGetParamResp(getParamResp: MessagePad.SetParamReq, adasParam: AdasParam) {
//融合v2n开关 NDE下行事件数据进pnc
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_EVENT_DATA_TO_PNC)?.also { changeValue(it,adasParam.v2nToPnc==1) }
//融合v2i开关 V2I下行感知数据进pnc
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(V2I_PERCEPTION_DATA_TO_PNC)?.also { changeValue(it,adasParam.v2iToPnc==1) }
//NDE下行感知数据进pnc
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_PERCEPTION_DATA_TO_PNC)?.also { changeValue(it,adasParam.perceptionToPnc==1) }
// ThreadUtils.runOnUiThread {
// //融合v2n开关 NDE下行事件数据进pnc
// preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_EVENT_DATA_TO_PNC)?.also { changeValue(it,adasParam.v2nToPnc==1) }
// //融合v2i开关 V2I下行感知数据进pnc
// preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(V2I_PERCEPTION_DATA_TO_PNC)?.also { changeValue(it,adasParam.v2iToPnc==1) }
// //NDE下行感知数据进pnc
// preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(NDE_PERCEPTION_DATA_TO_PNC)?.also { changeValue(it,adasParam.perceptionToPnc==1) }
// }
}
/**
@@ -720,7 +726,9 @@ class OperatePanelLayout : LinearLayout {
* @param enable 数据
*/
override fun onObuUploadStatus(enable: MessagePad.SetEnableReq) {
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(OBU_UPWARD_SWITCH)?.also { changeValue(it, enable.enable==1) }
ThreadUtils.runOnUiThread {
preferenceScreen.findPreferenceReal<SwitchPreferenceCompat>(OBU_UPWARD_SWITCH)?.also { changeValue(it, enable.enable==1) }
}
}
}

View File

@@ -0,0 +1,31 @@
package com.mogo.eagle.core.function.hmi.ui.tools
import android.content.Context
import androidx.lifecycle.LifecycleObserver
import com.mogo.commons.voice.AIAssist
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import kotlinx.android.synthetic.main.dialog_ota_power_off_finish.tvPowerOffClose
/**
* OTA升级完成且优雅停服完成、需要车辆下电的时候车端告知鹰眼
* 司机屏弹窗提示用车人执行车辆下电操作
*/
class OTAPowerOffFinishDialog(context: Context) :
BaseFloatDialog(context,TAG), LifecycleObserver {
companion object {
private const val TAG = "OTAPowerOffFinishDialog"
}
init{
setContentView(R.layout.dialog_ota_power_off_finish)
setCanceledOnTouchOutside(false)
tvPowerOffClose.setOnClickListener {
dismiss()
}
//语音提示下电重启
AIAssist.getInstance(context).speakTTSVoice("优雅停服完成,请操作车辆下电重启")
}
}

View File

@@ -11,6 +11,8 @@ import com.mogo.eagle.core.function.call.msgbox.CallerMsgBoxManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.eagle.core.utilcode.util.ResourceUtils
import com.mogo.eagle.core.utilcode.util.TimeUtils
import com.mogo.eagle.core.utilcode.util.TimeUtils.getHourMinSecondFormat
import kotlinx.android.synthetic.main.dialog_ota_upgrade_result.ivUpgradeResult
import kotlinx.android.synthetic.main.dialog_ota_upgrade_result.tvResultClose
import kotlinx.android.synthetic.main.dialog_ota_upgrade_result.tvResultContent
@@ -43,16 +45,18 @@ class OTAUpgradeResultDialog(context: Context) :
//升级成功
ivUpgradeResult.setImageDrawable(ResourceUtils.getDrawable(R.drawable.icon_ota_upgrade_success))
tvResultContent.text = context.resources.getString(R.string.ota_result_success)
tvResultTip.text = context.resources.getString(R.string.ota_result_success_tip)
tvResultTip.text =
String.format(context.resources.getString(R.string.ota_result_success_tip),
TimeUtils.millis2String(System.currentTimeMillis()+60000,getHourMinSecondFormat()))
//消息盒子和语音提示升级成功结果
CallerMsgBoxManager.saveMsgBox(
MsgBoxBean(
MsgBoxType.OTA,
OTAMsg(
"成功", "车辆部署任务执行结果", "车辆部署任务执行成功,请重启车辆")
"成功", "车辆部署任务执行结果", "任务下载完成,一分钟后操作车辆下电重启")
)
)
AIAssist.getInstance(context).speakTTSVoice("车辆部署任务执行成功,请重启车辆")
AIAssist.getInstance(context).speakTTSVoice("任务下载完成,一分钟后操作车辆下电重启")
}else{
//升级失败
ivUpgradeResult.setImageDrawable(ResourceUtils.getDrawable(R.drawable.icon_ota_upgrade_fail))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/dp_900"
android:layout_height="@dimen/dp_620"
android:background="@drawable/bg_bone_dialog"
app:roundLayoutRadius="@dimen/dp_50">
<ImageView
android:id="@+id/ivPowerOffFinish"
android:layout_width="@dimen/dp_140"
android:layout_height="@dimen/dp_140"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="@dimen/dp_87"
android:contentDescription="@string/ota_result_image"
android:src="@drawable/icon_ota_upgrade_success"
/>
<TextView
android:id="@+id/tvPowerOffContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/ivPowerOffFinish"
android:textColor="@color/white"
android:textSize="@dimen/sp_45"
android:layout_marginTop="@dimen/dp_20"
android:textStyle="bold"
android:text="@string/ota_power_off_finish_title"
/>
<TextView
android:id="@+id/tvPowerOffTip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvPowerOffContent"
android:textColor="@color/white"
android:textSize="@dimen/sp_36"
android:layout_marginTop="@dimen/dp_20"
android:text="@string/ota_power_off_finish_tip"
/>
<TextView
android:id="@+id/tvPowerOffClose"
android:layout_width="@dimen/dp_356"
android:layout_height="@dimen/dp_120"
android:gravity="center"
android:text="@string/ota_result_close"
android:background="@drawable/bg_dialog_btn"
android:textColor="@color/color_2EACFF"
android:textSize="@dimen/dp_40"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/dp_62"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -24,6 +24,7 @@
app:layout_constraintBottom_toBottomOf="@id/ivLightStatus"
app:layout_constraintLeft_toLeftOf="@id/ivLightStatus"
app:layout_constraintRight_toRightOf="@id/ivLightStatus"
app:chartRingWidth="@dimen/dp_6"
/>
<com.mogo.eagle.core.function.hmi.ui.widget.TypefaceTextView

View File

@@ -9,23 +9,22 @@
android:id="@+id/ivLightStatus"
android:layout_width="@dimen/dp_90"
android:layout_height="@dimen/dp_90"
android:src="@drawable/icon_green_light_passenger"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginStart="@dimen/dp_20"
android:layout_marginTop="@dimen/dp_10"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_11"
android:contentDescription="@string/common_traffic_light_status"
/>
<com.mogo.eagle.core.function.hmi.ui.widget.ProportionChartTwoView
android:id="@+id/pcvLightProportion"
android:layout_width="@dimen/dp_76"
android:layout_height="@dimen/dp_76"
android:layout_width="@dimen/dp_70"
android:layout_height="@dimen/dp_70"
app:layout_constraintTop_toTopOf="@id/ivLightStatus"
app:layout_constraintBottom_toBottomOf="@id/ivLightStatus"
app:layout_constraintLeft_toLeftOf="@id/ivLightStatus"
app:layout_constraintRight_toRightOf="@id/ivLightStatus"
app:chartRingWidth="@dimen/dp_3"
app:chartRingWidth="@dimen/dp_4"
/>
<com.mogo.eagle.core.function.hmi.ui.widget.TypefaceTextView

View File

@@ -88,7 +88,7 @@
android:visibility="gone"
app:lightUser="driver"
android:focusable="false"
android:layout_marginTop="@dimen/hmi_traffic_light_layout_margin_top"
android:layout_marginTop="@dimen/dp_40"
/>
<!--红绿灯提醒-->

View File

@@ -267,11 +267,13 @@
<string name="ota_upgrade_later">稍后升级</string>
<string name="ota_download_title">资源下载中</string>
<string name="ota_result_image">OTA升级结果图片示例</string>
<string name="ota_result_success">⻋辆部署任务执⾏成功</string>
<string name="ota_result_success">⻋辆部署任务下载完成</string>
<string name="ota_result_fail">⻋辆部署任务执⾏失败</string>
<string name="ota_result_success_tip">请重启⻋辆</string>
<string name="ota_result_success_tip">已自动发起优雅停服,%s后可操作车辆下电重启</string>
<string name="ota_result_fail_tip">请联系管理员</string>
<string name="ota_result_close">关闭</string>
<string name="ota_power_off_finish_title">车辆优雅停服完成</string>
<string name="ota_power_off_finish_tip">请操作车辆下电重启</string>
<string name="fsm_demo_mode_error">FSM美化模式状态下异常标识</string>

View File

@@ -3,21 +3,19 @@ package com.mogo.eagle.core.function
import android.content.Context
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.api.base.IMoGoFunctionServerProvider
import com.mogo.eagle.core.function.api.map.roma.IMogoRoma
import com.mogo.eagle.core.function.business.MapPointCloudSubscriber
import com.mogo.eagle.core.function.business.SpeedLimitDataManager
import com.mogo.eagle.core.function.business.ai.AiCloudIdentifyDataManager.Companion.aiCloudIdentifyDataManager
import com.mogo.eagle.core.function.business.ai.RomaManager
import com.mogo.eagle.core.function.business.ai.RomaManager.Companion.romaManager
import com.mogo.eagle.core.function.business.identify.MapIdentifySubscriber
import com.mogo.eagle.core.function.business.roadcross.RoadCrossCameraManager
import com.mogo.eagle.core.function.business.routeoverlay.DecisionDataManager
import com.mogo.eagle.core.function.business.routeoverlay.MogoRouteOverlayManager
import com.mogo.eagle.core.function.business.routeoverlay.PredictionDataManager
import com.mogo.eagle.core.function.business.trajectoryoverlay.MogoTrajectoryOverlayManager
import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.util.DeviceUtils
import com.mogo.map.MapDataWrapper
@@ -31,6 +29,8 @@ class MapBizProvider :IMoGoFunctionServerProvider, IMogoRoma {
MapDataWrapper.init()
MapIdentifySubscriber.instance
MogoRouteOverlayManager.getInstance().init()
DecisionDataManager.getInstance()
PredictionDataManager.getInstance()
MogoTrajectoryOverlayManager.getInstance().init()
MapPointCloudSubscriber.instance
RoadCrossCameraManager.instance.init(context)

View File

@@ -1,6 +1,5 @@
package com.mogo.eagle.core.function.business.identify
import android.util.Log
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.data.map.MogoLatLng
import com.mogo.eagle.core.data.map.MogoLocation
@@ -8,9 +7,8 @@ import com.mogo.eagle.core.data.traffic.TrafficData
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.api.base.IMoGoSubscriber
import com.mogo.eagle.core.function.api.datacenter.obu.IMoGoObuStatusListener
import com.mogo.eagle.core.function.business.routeoverlay.PredictionDataManager
import com.mogo.eagle.core.function.business.routeoverlay.PredictionOverlayDrawer
import com.mogo.eagle.core.function.business.routeoverlay.PredictionOverlayDrawer2
import com.mogo.eagle.core.function.business.routeoverlay.PredictionOverlayDrawer3
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationWGS84ListenerManager.getLocationHeading
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationWGS84ListenerManager.getWgs84Lat
@@ -19,8 +17,9 @@ import com.mogo.eagle.core.function.call.obu.CallerObuWarningListenerManager
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.map.MogoMap
import com.mogo.map.MogoMap.Companion.mapInstance
import com.mogo.map.identify.MogoIdentifyManager
import com.mogo.map.utils.LocationUtils
import com.zhjt.mogo.adas.data.Adas.TrackedObjectClassID
import com.zhjt.mogo.adas.data.Adas.TrackedObjectType
import mogo.telematics.pad.MessagePad
import mogo.telematics.pad.MessagePad.TrackedObject
import mogo.yycp.api.proto.SocketDownData
@@ -37,6 +36,21 @@ class MapIdentifySubscriber private constructor() : IMoGoSubscriber,
private val TAG = "MapIdentifySubscriber"
private val typeMap: HashMap<TrackedObjectType, TrackedObjectClassID> by lazy {
hashMapOf<TrackedObjectType, TrackedObjectClassID>().apply {
put(TrackedObjectType.TYPE_PEDESTRIAN, TrackedObjectClassID.Person)
put(TrackedObjectType.TYPE_MOTOR, TrackedObjectClassID.MotorCycle)
put(TrackedObjectType.TYPE_BICYCLE, TrackedObjectClassID.Bicycle)
put(TrackedObjectType.TYPE_CAR, TrackedObjectClassID.Car)
put(TrackedObjectType.TYPE_TRUCK, TrackedObjectClassID.Truck)
put(TrackedObjectType.TYPE_BUS, TrackedObjectClassID.Bus)
put(TrackedObjectType.TYPE_WARNINGTRIANGLE, TrackedObjectClassID.WarningTriangle)
put(TrackedObjectType.TYPE_ROADWORK_OCCUPY_0501, TrackedObjectClassID.RoadWork_occupy_0501)
put(TrackedObjectType.TYPE_ROADWORK_BREAK_0502, TrackedObjectClassID.RoadWork_break_0502)
put(TrackedObjectType.TYPE_ROAD_CONGESTION, TrackedObjectClassID.ROAD_CONGESTION)
}
}
init {
onCrate()
}
@@ -95,34 +109,25 @@ class MapIdentifySubscriber private constructor() : IMoGoSubscriber,
if (preObj.predictionTrajectoryList == null || preObj.predictionTrajectoryList.size < 2) return@forEach
carPoiList1 = preObj.predictionTrajectoryList[0].trajectoryPointsList
carPoiList2 = preObj.predictionTrajectoryList[1].trajectoryPointsList
val largeType: Int
location1 = LocationUtils.generateLocation(carPoiList1!![0].x, carPoiList1!![0].y, getLocationHeading())
location2 = LocationUtils.generateLocation(carPoiList2!![0].x, carPoiList2!![0].y, getLocationHeading())
if (location1 == null || location2 == null) return@forEach
probability1 = preObj.predictionTrajectoryList[0].predictionProbability
probability2 = preObj.predictionTrajectoryList[1].predictionProbability
CallerAutopilotIdentifyListenerManager.invokeProbabilityChanged(probability1, probability2)
if (probability1 >= probability2) {
MogoIdentifyManager.getInstance().updateGps(location1!!, MogoMap.SMALL_PRED_MAP)
largeType = 2
} else {
MogoIdentifyManager.getInstance().updateGps(location2!!, MogoMap.SMALL_PRED_MAP)
largeType = 3
}
PredictionOverlayDrawer2.getInstance().drawPredictionList(carPoiList1, getLocationHeading(), false, 2, largeType)
PredictionOverlayDrawer3.getInstance().drawPredictionList(carPoiList2, getLocationHeading(), false, 3, largeType)
MogoIdentifyManager.getInstance().updateGps(location1!!, MogoMap.SMALL_PRED_MAP2)
MogoIdentifyManager.getInstance().updateGps(location2!!, MogoMap.SMALL_PRED_MAP3)
PredictionDataManager.getInstance()?.updateData(carPoiList1!!, 0)
PredictionDataManager.getInstance()?.updateData(carPoiList2!!, 2)
} else {
if (preObj.predictionTrajectoryList.isNullOrEmpty() || mogoMap == null) return@forEach
if (isUnKnownType(preObj.classtype) || preObj.predictionTrajectoryList.isNullOrEmpty() || mogoMap == null) return@forEach
point = preObj.predictionTrajectoryList[0].trajectoryPointsList[0]
arr = mogoMap.switchData(point.x, point.y, false)
arr?.let { lonLatArr ->
val distance = com.mogo.eagle.core.utilcode.util.LocationUtils.getDistance(getWgs84Lat(), getWgs84Lon(), lonLatArr[1], lonLatArr[0])
if (distance > 25) return@forEach
if (distance > 28) return@forEach
}
preObj.predictionTrajectoryList[0].trajectoryPointsList.forEachIndexed { index, point ->
if (index > 9 && index % 2 == 0) {// 步长为2减少点
if (index in 0..44 && index % 2 == 0) {// 步长为2减少点
arr = mogoMap.switchData(point.x, point.y, false)
arr?.let { lonLatArr ->
points.add(MogoLatLng(lonLatArr[1], lonLatArr[0]))
@@ -140,6 +145,20 @@ class MapIdentifySubscriber private constructor() : IMoGoSubscriber,
}
}
private fun isUnKnownType(classType: Int): Boolean {
val type: TrackedObjectType? = TrackedObjectType.forNumber(classType)
return when (type) {
null, TrackedObjectType.TYPE_UNKNOWN, TrackedObjectType.TYPE_UNKNOWN_SMALL,
TrackedObjectType.TYPE_UNKNOWN_BIG, TrackedObjectType.TYPE_UNKNOWN_STATIC,
TrackedObjectType.TYPE_UNKNOWN_DYNAMIC, TrackedObjectType.UNRECOGNIZED -> {
true
}
else -> {
false
}
}
}
fun renderAiCloudResult(cloudData: List<SocketDownData.CloudRoadDataProto>, mapInstance:String = MogoMap.DEFAULT) {
try {
ThreadUtils.getSinglePool().execute {

View File

@@ -0,0 +1,130 @@
package com.mogo.eagle.core.function.business.routeoverlay
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.util.Log
import com.mogo.eagle.core.function.api.autopilot.IMoGoPlanningTrajectoryListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningTrajectoryListenerManager
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.map.MogoMap
import com.mogo.map.MogoMap.Companion.mapInstance
import me.jessyan.autosize.utils.AutoSizeUtils
import mogo.telematics.pad.MessagePad
class DecisionDataManager private constructor() : IMoGoPlanningTrajectoryListener {
companion object {
private const val TAG = "DecisionDataManager"
private const val MSG_DATA_POINTS = 0
private const val MSG_CHECK = 1
@Volatile
private var sInstance: DecisionDataManager? = null
fun getInstance(): DecisionDataManager? {
if (sInstance == null) {
synchronized(DecisionDataManager::class.java) {
if (sInstance == null) {
sInstance = DecisionDataManager()
}
}
}
return sInstance
}
}
private var frequentHandler: FrequentHandler? = null
private var lastUpdateTime = 0L
/**
* 高精地图宽、高
*/
@Volatile
var mapWidth = 808
@Volatile
var mapHeight = 1300
/**
* 决策地图宽、高
*/
@Volatile
var decWidth = 238
@Volatile
var decHeight = 458
init {
val frequentThread = HandlerThread("decision_thread")
frequentThread.start()
frequentHandler = FrequentHandler(frequentThread.looper)
frequentHandler?.sendEmptyMessageDelayed(MSG_CHECK, 1000)
CallerPlanningTrajectoryListenerManager.addListener(TAG, this)
}
private fun updateData(data: FloatArray) {
frequentHandler?.removeMessages(MSG_DATA_POINTS)
val message = Message.obtain()
message.what = MSG_DATA_POINTS
message.obj = data
frequentHandler?.sendMessage(message)
}
fun release() {
}
private inner class FrequentHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
MSG_DATA_POINTS -> {
lastUpdateTime = System.currentTimeMillis()
CallerAutopilotIdentifyListenerManager.invokeScreenPointsChanged(msg.obj as FloatArray)
}
MSG_CHECK -> {
val time = System.currentTimeMillis()
if (lastUpdateTime > 0 && time - lastUpdateTime >= 1000) {
CallerAutopilotIdentifyListenerManager.invokeScreenPointsChanged(floatArrayOf())
}
sendEmptyMessageDelayed(MSG_CHECK, 1000)
}
}
}
}
override fun onAutopilotTrajectory(trajectoryInfos: MutableList<MessagePad.TrajectoryPoint>) {
ThreadUtils.getCpuPool().execute {
val mogoMap = mapInstance.getMogoMap(MogoMap.DEFAULT)
mogoMap?.let { map ->
val lonLatList = ArrayList<android.graphics.Point>()
trajectoryInfos.forEach {
map.toScreenLocation(it.longitude, it.latitude)?.let { point ->
lonLatList.add(point)
Log.d(TAG, "转换后的屏幕坐标为:(${point.x},${point.y})")
}
}
if (lonLatList.size > 0) {
val points = FloatArray(lonLatList.size * 2)
var x = 0f
var y = 0f
var offset = 0f
for (i in 0 until lonLatList.size) {
x = lonLatList[i].x * decWidth / mapWidth.toFloat()
points[i * 2] = x
y = (decHeight + 14 - lonLatList[i].y * decHeight / mapHeight).toFloat()
if (i == 0) {
offset = decHeight * 0.78f - y// carBitmapTop等于height * 0.78offset = carBitmapTop - 1stY
}
points[i * 2 + 1] = y + offset
Log.d(TAG, "转换后的屏幕坐标为:(${x},${y})")
}
updateData(points)
} else {
updateData(floatArrayOf())
}
}
}
}
}

View File

@@ -0,0 +1,130 @@
package com.mogo.eagle.core.function.business.routeoverlay
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.util.Log
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.map.MogoMap
import com.mogo.map.MogoMap.Companion.mapInstance
class PredictionDataManager private constructor() {
companion object {
private const val TAG = "PredictionDataManager"
private const val MSG_CHECK = 0
private const val MSG_DATA_POINTS_0 = 1
private const val MSG_DATA_POINTS_1 = 2
@Volatile
private var sInstance: PredictionDataManager? = null
fun getInstance(): PredictionDataManager? {
if (sInstance == null) {
synchronized(PredictionDataManager::class.java) {
if (sInstance == null) {
sInstance = PredictionDataManager()
}
}
}
return sInstance
}
}
private var frequentHandler: FrequentHandler? = null
private var lastUpdateTime = 0L
/**
* 高精地图宽、高
*/
@Volatile
var mapWidth = 808
@Volatile
var mapHeight = 1300
/**
* 决策地图宽、高
*/
@Volatile
var decWidth = 238
@Volatile
var decHeight = 458
init {
val frequentThread = HandlerThread("prediction_thread")
frequentThread.start()
frequentHandler = FrequentHandler(frequentThread.looper)
frequentHandler?.sendEmptyMessageDelayed(MSG_CHECK, 1000)
}
fun updateData(dataList: List<geometry.Geometry.Point>, index: Int = 0) {
val type = if (index == 0) MSG_DATA_POINTS_0 else MSG_DATA_POINTS_1
frequentHandler?.removeMessages(type)
val message = Message.obtain()
message.what = type
message.obj = dataList
frequentHandler?.sendMessage(message)
}
fun release() {
}
private inner class FrequentHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
MSG_DATA_POINTS_0, MSG_DATA_POINTS_1 -> {
val list = getDataList(msg.obj)
lastUpdateTime = System.currentTimeMillis()
CallerAutopilotIdentifyListenerManager.invokePreScrPointsChanged(list, if (msg.what == MSG_DATA_POINTS_0) 0 else 2)
}
MSG_CHECK -> {
val time = System.currentTimeMillis()
if (lastUpdateTime > 0 && time - lastUpdateTime >= 1000) {
CallerAutopilotIdentifyListenerManager.invokePreScrPointsChanged(floatArrayOf(), 0)
CallerAutopilotIdentifyListenerManager.invokePreScrPointsChanged(floatArrayOf(), 2)
}
sendEmptyMessageDelayed(MSG_CHECK, 1000)
}
}
}
private fun getDataList(obj: Any): FloatArray {
val mogoMap = mapInstance.getMogoMap(MogoMap.DEFAULT) ?: return floatArrayOf()
val list = obj as List<geometry.Geometry.Point>
val lonLatList = ArrayList<android.graphics.Point>()
var arr: DoubleArray?
var point: android.graphics.Point?
list.forEachIndexed { index, poi ->
if (index > 45) return@forEachIndexed
arr = mogoMap.switchData(poi.x, poi.y, false)// UTM转wgs84
if (arr == null || arr!!.size < 2) return@forEachIndexed
point = mogoMap.toScreenLocation(arr!![0], arr!![1])// wgs84转屏幕坐标
if (point == null) return@forEachIndexed
lonLatList.add(point!!)
Log.d(TAG, "预测数据的屏幕坐标为:(${point!!.x},${point!!.y})")
}
if (lonLatList.size > 0) {
val points = FloatArray(lonLatList.size * 2)
var x = 0f
var y = 0f
var offset = 0f
for (i in 0 until lonLatList.size) {
x = lonLatList[i].x * decWidth / mapWidth.toFloat()
points[i * 2] = x
y = (decHeight + 14 - lonLatList[i].y * decHeight / mapHeight).toFloat()
if (i == 0) {
offset = decHeight * 0.78f - y// carBitmapTop等于height * 0.78offset = carBitmapTop - 1stY
}
points[i * 2 + 1] = y + offset
Log.d(TAG, "预测数据转换后的屏幕坐标为:(${x},${y})")
}
return points
} else {
return floatArrayOf()
}
}
}
}

View File

@@ -140,15 +140,15 @@ public class PredictionOverlayDrawer {
if (mPolylineOptions == null) {
builder = new Polyline.Options.Builder("pred_overlay", Level.GUIDE_ROUTE_LINE)
.setUseGps(true)
.setWidth(5)
.setWidth(4)
.setIsGradient(true);
} else {
builder = mPolylineOptions.builder();
}
builder.color(Color.rgb(196, 196, 196));
builder.setIsGradient(false);
builder.color(Color.argb(102,48,163,255));
builder.setIsGradient(true);
builder.setLightOn(false);
builder.setIsDottedLine(false);
builder.setIsDottedLine(true);
builder.isShowArrow(false);
builder.points(pps);
builder.setVisible(true);

View File

@@ -0,0 +1,729 @@
package com.mogo.eagle.core.function.view
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.DashPathEffect
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.View
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.business.routeoverlay.DecisionDataManager
import com.mogo.eagle.core.function.business.routeoverlay.PredictionDataManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.map.R
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import me.jessyan.autosize.utils.AutoSizeUtils
import kotlin.math.min
class CoordinateAnimationView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null
) :
SurfaceView(context, attrs), SurfaceHolder.Callback, IMoGoAutopilotIdentifyListener {
companion object {
private const val TAG = "CoordinateAnimationView"
private const val UPDATE_INTERVAL = 1000 // 数据更新间隔(ms)
private const val MAX_POINTS = 500 // 最大点数
private const val BUFFER_SIZE = MAX_POINTS * 2 // x,y坐标交替存储
private const val FPS = 25
}
private var surfaceHolder: SurfaceHolder? = null
private var animationThread: AnimationThread? = null
private var bufferA: Array<Point?>? = null
private var bufferB: Array<Point?>? = null
private var controlPoints: FloatArray? = null // 控制点缓冲区
private var activePointCount = 0
@Volatile
private var activeBufferIndex = 0
@Volatile
private var drawingBufferIndex = 0
private var lastUpdateTime: Long = 0
private var costTime: Long = 0
private var fpsInterval: Int = 1000 / FPS
@Volatile
private var dataChanged = false // 数据变化标志
@Volatile
private var isRunning = false
@Volatile
private var isVisible = false
private var bezierPath: Path? = null // 贝塞尔曲线路径
private var curvePaint: Paint? = null
private var carPaint: Paint? = null
// private var circlePaint: Paint? = null
private var visibleRect: RectF? = null // 可见区域
private var strokeWidth = 16f
private var carBitmap: Bitmap? = null
// 虚线相关参数
private val DASH_LENGTH: Float = 46f // 虚线线段长度
private val DASH_GAP: Float = 80f // 虚线间隔
private val DASH_SPEED: Float = 8f // 虚线移动速度
private var dashOffsetY = 0f // 虚线Y轴偏移量
private var dashStrokeWidth = 3.6f
private var dashPaint: Paint? = null // 虚线画笔
private val leftPath by lazy {
Path()
}
private val rightPath by lazy {
Path()
}
private var index: Int = 1
init {
init(attrs)
}
private fun init(attrs: AttributeSet?) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CoordinateAnimationView)
index = typedArray.getInt(R.styleable.CoordinateAnimationView_map_index, 1)
typedArray.recycle()
Log.d(TAG, "初始化地图的index为:${index}")
surfaceHolder = holder
surfaceHolder!!.addCallback(this)
curvePaint = Paint()
carPaint = Paint()
carPaint!!.isFilterBitmap = false
carPaint!!.isAntiAlias = true
carBitmap = getOriginBitmap(
R.drawable.decision_car_icon,
AutoSizeUtils.dp2px(context, 128f),
AutoSizeUtils.dp2px(context, 280f)
)
curvePaint!!.setARGB(102, 48, 163, 255)
// curvePaint!!.shader = LinearGradient(
// 50f, 100f, 750f, 100f,
// intArrayOf(0x0FFF0000, 0xFF00FF00.toInt(), 0x0F0000FF),
// floatArrayOf(0f, 0.5f, 1f),
// Shader.TileMode.CLAMP
// )
curvePaint!!.style = Paint.Style.STROKE
curvePaint!!.strokeWidth = strokeWidth
// 一定要设置抗锯齿,否则画贝塞尔曲线时会出现很多白色分割线
curvePaint!!.isAntiAlias = true
// 线段连接处为圆弧
curvePaint!!.strokeJoin = Paint.Join.ROUND
// 设置线冒样式
curvePaint!!.strokeCap = Paint.Cap.SQUARE
// 初始化虚线
dashPaint = Paint()
dashPaint!!.setColor(Color.WHITE)
dashPaint!!.style = Paint.Style.STROKE
dashPaint!!.strokeWidth = dashStrokeWidth
dashPaint!!.isAntiAlias = true
dashPaint!!.pathEffect = DashPathEffect(floatArrayOf(DASH_LENGTH, DASH_GAP), 0f)
bufferA = arrayOfNulls(MAX_POINTS)
bufferB = arrayOfNulls(MAX_POINTS)
for (i in 0 until MAX_POINTS) {
bufferA!![i] = Point()
bufferB!![i] = Point()
}
bezierPath = Path()
dataChanged = false
controlPoints = FloatArray(MAX_POINTS * 4)// 按照三阶贝塞尔曲线来创建
visibleRect = RectF()
// 启用硬件加速
setLayerType(LAYER_TYPE_HARDWARE, null)
}
private fun getOriginBitmap(resId: Int, desWidth: Int, desHeight: Int): Bitmap {
val bitmap = BitmapFactory.decodeResource(resources, resId)
val scaleWidth = (bitmap.width * 2)
val scaleHeight = (bitmap.height * 2)
Log.d(TAG, "$index-Bitmap width:$scaleWidth,height:$scaleHeight")
return Bitmap.createScaledBitmap(
bitmap, scaleWidth,
scaleHeight, true
)
// val options = BitmapFactory.Options()
// options.inJustDecodeBounds = true
// BitmapFactory.decodeResource(resources, resId, options)
// var inSampleSize = 1
// if (options.outHeight > desHeight || options.outWidth > desWidth) {
// while ((options.outHeight / 2 / inSampleSize) >= desHeight && (options.outWidth / inSampleSize) >= desWidth) {
// inSampleSize *= 2// 每次翻倍,保证是 2 的幂次
// }
// }
// options.inJustDecodeBounds = false
// options.inSampleSize = inSampleSize
// return BitmapFactory.decodeResource(resources, resId, options)
}
fun setVisible(isVisible: Boolean) {
this.isVisible = isVisible
}
override fun surfaceCreated(holder: SurfaceHolder) {
animationThread = AnimationThread()
animationThread!!.start()
isRunning = true
CallerAutopilotIdentifyListenerManager.addListener("${TAG}${this.hashCode()}", this)
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
val margin = strokeWidth * 2
// 处理Surface尺寸变化
visibleRect!!.set(margin, margin, width - margin, height - margin)
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
isRunning = false
var retry = true
while (retry) {
try {
animationThread!!.join()
retry = false
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
CallerAutopilotIdentifyListenerManager.removeListener("${TAG}${this.hashCode()}")
carBitmap?.recycle()
carBitmap = null
bufferA = null
bufferB = null
controlPoints = null
}
override fun onVisibilityChanged(changedView: View, visibility: Int) {
super.onVisibilityChanged(changedView, visibility)
val name = changedView.javaClass.simpleName
if (name == TAG || name == "ConstraintLayout") {
isVisible = visibility == View.VISIBLE
}
}
override fun screenPointsChanged(data: FloatArray, index: Int) {
if (index == this.index) {
updatePoints(data)
}
}
override fun preScrPointsChanged(data: FloatArray, index: Int) {
if (index == this.index) {
updatePoints(data)
}
}
private fun updatePoints(newPoints: FloatArray) {
if (!isRunning) {
Log.d(TAG, "$index-渲染线程未启动")
return
}
if (bufferA == null || bufferB == null) {
Log.d(TAG, "$index-updatePoints->缓冲区未初始化完成!")
return
}
// 获取可用缓冲区
val targetBuffer: Array<Point?> = if ((drawingBufferIndex == 0)) bufferB!! else bufferA!!
// 重置目标缓冲区
for (i in 0 until MAX_POINTS) {
targetBuffer[i]?.reset()
}
// 填充新数据(不能超过最大点数)
val count = min(newPoints.size / 2, MAX_POINTS)
var p: Point?
for (i in 0 until count) {
p = targetBuffer[i]
p?.set(newPoints[i * 2], newPoints[i * 2 + 1])
}
activePointCount = count
activeBufferIndex = if (drawingBufferIndex == 0) 1 else 0
dataChanged = true
Log.d(TAG, "$index-数据已更新!")
}
private fun drawMovingDashedPath(canvas: Canvas?) {
if (canvas == null) {
Log.d(TAG, "$index-画布为null!")
return
}
Log.d(TAG, "$index-开始绘制虚线!")
leftPath.rewind()
rightPath.rewind()
// 两条虚线的X坐标左右对称
val leftLineX = width * 0.2f
val rightLineX = width * 0.8f
// 计算当前虚线的偏移量(取模运算确保在一个周期内)
val offset = dashOffsetY % (DASH_LENGTH + DASH_GAP)
// 绘制多条虚线线段,确保覆盖整个屏幕
var y = offset
var endY = 0f
while (y < height + DASH_LENGTH + DASH_GAP) {
endY = min(y + DASH_LENGTH, height.toFloat())
leftPath.moveTo(leftLineX, y)
leftPath.lineTo(leftLineX, endY)
rightPath.moveTo(rightLineX, y)
rightPath.lineTo(rightLineX, endY)
y += DASH_LENGTH + DASH_GAP// 步长
}
canvas.drawPath(leftPath, dashPaint!!)
canvas.drawPath(rightPath, dashPaint!!)
// 更新虚线偏移量
dashOffsetY += DASH_SPEED
if (dashOffsetY > DASH_LENGTH + DASH_GAP) {
dashOffsetY -= (DASH_LENGTH + DASH_GAP)
}
}
private fun drawCar(canvas: Canvas?) {
if (canvas == null) {
Log.d(TAG, "$index-画布为null!")
return
}
canvas.drawBitmap(carBitmap!!, (width - carBitmap!!.width) / 2f, height * 0.78f, carPaint!!)
}
private fun drawPath(canvas: Canvas?) {
if (canvas == null) {
Log.d(TAG, "$index-画布为null!")
return
}
// 获取当前活跃的缓冲区
val currentBuffer = if (activeBufferIndex == 0) bufferA else bufferB
drawingBufferIndex = activeBufferIndex
// 绘制轨迹
if (dataChanged) {
bezierPath?.rewind()
if (activePointCount > 0) {
// 计算贝塞尔曲线的控制点(二阶贝塞尔曲线性能更好)
// calculateControlPoints(currentBuffer, activePointCount, controlPoints!!)
calculateControlPoints2ndKind(currentBuffer, activePointCount, controlPoints!!)
// 使用可见区域裁剪的贝塞尔曲线
// drawClippedBezierPath(currentBuffer, activePointCount, controlPoints!!)
drawClippedBezier2ndKindPath(currentBuffer, activePointCount, controlPoints!!)
}
dataChanged = false
} else {
Log.d(TAG, "$index-数据未更新:${dataChanged}或无有效的点:${activePointCount}")
}
// 绘制贝塞尔曲线
canvas.drawPath(bezierPath!!, curvePaint!!)
Log.d(TAG, "=====$index-渲染贝塞尔曲线完成!=====")
// if (currentBuffer == null) {
// Log.d(TAG, "currentBuffer未初始化完成!")
// return
// }
// canvas.drawCircle(currentBuffer[0]!!.x, currentBuffer[0]!!.y, 30f, circlePaint!!)
}
// 绘制可见区域内的贝塞尔曲线
private fun drawClippedBezierPath(
points: Array<Point?>?,
count: Int,
controlPoints: FloatArray
) {
if (points.isNullOrEmpty()) {
Log.d(TAG, "$index-贝塞尔曲线点的个数为空,返回!")
return
}
var isFirstPoint = true
var wasLastPointVisible = false
var p: Point?
var nextPoint: Point?
var currentPointVisible = false
var nextPointVisible = false
var controlIndex: Int
var x: Float
var y: Float
var nextX: Float
var nextY: Float
var intersection: FloatArray?
for (i in 0 until count - 1) {
p = points[i]
nextPoint = points[i + 1]
if (p == null || nextPoint == null) continue
x = p.x
y = p.y
nextX = nextPoint.x
nextY = nextPoint.y
// 检查当前点和下一个点是否在可见区域内
currentPointVisible = isPointVisible(x, y)
nextPointVisible = isPointVisible(nextX, nextY)
controlIndex = i * 2
// 处理可见性变化
if (currentPointVisible || nextPointVisible) {
// 如果是第一个可见点,移动到该点
if (isFirstPoint) {
bezierPath!!.moveTo(x, y)
isFirstPoint = false
}
// 如果当前点和下一个点都可见,直接连接
if (currentPointVisible && nextPointVisible) {
bezierPath!!.cubicTo(
controlPoints[controlIndex], controlPoints[controlIndex + 1],
controlPoints[controlIndex + 2], controlPoints[controlIndex + 3],
nextX, nextY
)
} else if (currentPointVisible && !nextPointVisible) {
// intersection = findIntersection(x, y, nextX, nextY)
// if (intersection != null) {
// bezierPath!!.lineTo(intersection[0], intersection[1])
// }
bezierPath!!.cubicTo(
controlPoints[controlIndex], controlPoints[controlIndex + 1],
controlPoints[controlIndex + 2], controlPoints[controlIndex + 3],
nextX, nextY
)
} else if (!currentPointVisible && nextPointVisible) {
// intersection = findIntersection(nextX, nextY, x, y)
// if (intersection != null) {
// bezierPath!!.moveTo(intersection[0], intersection[1])
// bezierPath!!.cubicTo(
// controlPoints[controlIndex], controlPoints[controlIndex + 1],
// controlPoints[controlIndex + 2], controlPoints[controlIndex + 3],
// nextX, nextY
// )
// }
bezierPath!!.cubicTo(
controlPoints[controlIndex], controlPoints[controlIndex + 1],
controlPoints[controlIndex + 2], controlPoints[controlIndex + 3],
nextX, nextY
)
}
wasLastPointVisible = true
} else {
// 如果连续不可见点,重置路径状态
if (wasLastPointVisible) {
isFirstPoint = true
}
wasLastPointVisible = false
}
}
}
// 计算贝塞尔曲线的控制点
private fun calculateControlPoints(
points: Array<Point?>?,
count: Int,
controlPoints: FloatArray
) {
if (count < 3 || points.isNullOrEmpty()) {
Log.d(TAG, "$index-点的个数小于3无法绘制贝塞尔曲线!")
return// 至少需要3个点才能计算控制点
}
var prev: Point?
var current: Point?
var next: Point?
var controlIndex: Int
// 使用相邻点的中点作为控制点
for (i in 1 until count - 1) {
prev = points[i - 1]
current = points[i]
next = points[i + 1]
controlIndex = i * 2
if (current == null || prev == null || next == null) continue
// 第一个控制点:当前点和前一个点的中点
controlPoints[controlIndex] = (current.x + prev.x) / 2
controlPoints[controlIndex + 1] = (current.y + prev.y) / 2
// 第二个控制点:当前点和下一个点的中点
controlPoints[controlIndex + 2] = (current.x + next.x) / 2
controlPoints[controlIndex + 3] = (current.y + next.y) / 2
}
if (points[0] == null || points[count - 2] == null) return
// 特殊处理第一个和最后一个控制点
controlPoints[0] = points[0]!!.x
controlPoints[1] = points[0]!!.y
val lastIndex = (count - 2) * 2
controlPoints[lastIndex] = points[count - 2]!!.x
controlPoints[lastIndex + 1] = points[count - 2]!!.y
}
// 计算二阶贝塞尔曲线的控制点
private fun calculateControlPoints2ndKind(
points: Array<Point?>?,
count: Int,
controlPoints: FloatArray
) {
if (count < 2 || points.isNullOrEmpty()) return // 至少需要2个点才能计算控制点
var current: Point?
var next: Point?
var controlIndex: Int
var dx: Float
var dy: Float
// 简单算法:使用相邻点的中点作为控制点
for (i in 0 until count - 1) {
current = points[i]
next = points[i + 1]
if (current == null || next == null) continue
controlIndex = i * 2
// 控制点:当前点和下一个点之间的中点,增加一定偏移使曲线更平滑
dx = next.x - current.x
dy = next.y - current.y
// 控制点位置 = 中点 + 一定比例的方向向量(使曲线更平滑)
controlPoints[controlIndex] = current.x + dx * 0.5f
controlPoints[controlIndex + 1] = current.y + dy * 0.5f
}
}
/**
* 绘制可见区域内的二阶贝塞尔曲线
*/
private fun drawClippedBezier2ndKindPath(
points: Array<Point?>?,
count: Int,
controlPoints: FloatArray
) {
if (points.isNullOrEmpty()) {
Log.d(TAG, "$index-贝塞尔曲线点的个数为空,返回!")
return
}
var isFirstPoint = true
var wasLastPointVisible = false
// var hasPath = false
var p: Point?
var nextPoint: Point?
for (i in 0 until count - 1) {
p = points[i]
nextPoint = points[i + 1]
if (p == null || nextPoint == null) continue
val x = p.x
val y = p.y
val nextX = nextPoint.x
val nextY = nextPoint.y
// 检查当前点和下一个点是否在可见区域内
val currentPointVisible = isPointVisible(x, y)
val nextPointVisible = isPointVisible(nextX, nextY)
// 处理可见性变化
if (currentPointVisible || nextPointVisible) {
// 如果是第一个可见点,移动到该点
if (isFirstPoint) {
// bezierPath!!.moveTo(x-strokeWidth/4, y)
bezierPath!!.moveTo(x, y)
isFirstPoint = false
// hasPath = true
}
// 如果当前点和下一个点都可见,直接连接
if (currentPointVisible && nextPointVisible) {
// 使用二阶贝塞尔曲线quadTo方法(控制点x, 控制点y, 终点x, 终点y)
bezierPath!!.quadTo(
controlPoints[i * 2], controlPoints[i * 2 + 1],
nextX, nextY
)
// hasPath = true
} else if (currentPointVisible && !nextPointVisible) {
// val intersection = findIntersection(x, y, nextX, nextY)
// if (intersection != null) {
// bezierPath!!.lineTo(intersection[0], intersection[1])
//// hasPath = true
// }
bezierPath!!.lineTo(
controlPoints[i * 2], controlPoints[i * 2 + 1],
)
} else if (!currentPointVisible && nextPointVisible) {
// val intersection = findIntersection(nextX, nextY, x, y)
// if (intersection != null) {
// bezierPath!!.moveTo(intersection[0], intersection[1])
// // 绘制二阶贝塞尔曲线
// val controlIndex = i * 2
// bezierPath!!.quadTo(
// controlPoints[controlIndex], controlPoints[controlIndex + 1],
// nextX, nextY
// )
//// hasPath = true
// }
bezierPath!!.lineTo(
controlPoints[i * 2], controlPoints[i * 2 + 1],
)
}
wasLastPointVisible = true
} else {
// 如果连续不可见点,重置路径状态
if (wasLastPointVisible) {
isFirstPoint = true
}
wasLastPointVisible = false
}
}
// if (count > 1 && hasPath) {
// val lastPoint = points[count - 1] ?: return
// val endX = lastPoint.x
// val endY = lastPoint.y
//
// // 添加一个微小的延伸,确保终点形状为方形
// if (isPointVisible(endX, endY)) {
// bezierPath!!.lineTo(endX + strokeWidth / 4, endY)
// }
// }
Log.d(TAG, "=======$index-绘制二阶贝塞尔曲线完成=======!")
}
/**
* 绘制二阶贝塞尔曲线
*/
private fun drawBezierPath2ndKind(count: Int) {
Log.d(TAG, "=======$index-绘制二阶贝塞尔曲线完成=======!")
}
// 检查点是否在可见区域内(考虑线宽)
private fun isPointVisible(x: Float, y: Float): Boolean {
val extraMargin = strokeWidth / 2
return visibleRect!!.contains(x, y) ||
visibleRect!!.let {
it.inset(-extraMargin, -extraMargin)// 扩大(使用负数)边界以考虑线宽
it.contains(x, y)
}
}
// 计算线段与可见区域的交点
private fun findIntersection(x1: Float, y1: Float, x2: Float, y2: Float): FloatArray? {
// // 简化版:只检查线段与可见区域边界的交点
// // 实际应用中可能需要更复杂的贝塞尔曲线与矩形的交点计算
//
// // 检查与左边界的交点
// if ((x1 < visibleRect!!.left && x2 >= visibleRect!!.left) ||
// (x1 >= visibleRect!!.left && x2 < visibleRect!!.left)
// ) {
// val t = (visibleRect!!.left - x1) / (x2 - x1)
// val y = y1 + t * (y2 - y1)
// if (y >= visibleRect!!.top && y <= visibleRect!!.bottom) {
// return floatArrayOf(visibleRect!!.left, y)
// }
// }
//
// // 检查与右边界的交点
// if ((x1 < visibleRect!!.right && x2 >= visibleRect!!.right) ||
// (x1 >= visibleRect!!.right && x2 < visibleRect!!.right)
// ) {
// val t = (visibleRect!!.right - x1) / (x2 - x1)
// val y = y1 + t * (y2 - y1)
// if (y >= visibleRect!!.top && y <= visibleRect!!.bottom) {
// return floatArrayOf(visibleRect!!.right, y)
// }
// }
//
// // 检查与上边界的交点
// if ((y1 < visibleRect!!.top && y2 >= visibleRect!!.top) ||
// (y1 >= visibleRect!!.top && y2 < visibleRect!!.top)
// ) {
// val t = (visibleRect!!.top - y1) / (y2 - y1)
// val x = x1 + t * (x2 - x1)
// if (x >= visibleRect!!.left && x <= visibleRect!!.right) {
// return floatArrayOf(x, visibleRect!!.top)
// }
// }
//
// // 检查与下边界的交点
// if ((y1 < visibleRect!!.bottom && y2 >= visibleRect!!.bottom) ||
// (y1 >= visibleRect!!.bottom && y2 < visibleRect!!.bottom)
// ) {
// val t = (visibleRect!!.bottom - y1) / (y2 - y1)
// val x = x1 + t * (x2 - x1)
// if (x >= visibleRect!!.left && x <= visibleRect!!.right) {
// return floatArrayOf(x, visibleRect!!.bottom)
// }
// }
return null // 没有交点
}
private inner class AnimationThread : Thread() {
override fun run() {
var canvas: Canvas?
lastUpdateTime = System.currentTimeMillis()
while (isRunning) {
if (isVisible) {
canvas = null
try {
canvas = surfaceHolder!!.lockCanvas()
synchronized(surfaceHolder!!) {
// 绘制
Log.d(TAG, "$index-准备绘制!")
// 清屏
canvas?.drawColor(Color.rgb(231, 235, 238))
drawMovingDashedPath(canvas)
drawPath(canvas)
drawCar(canvas)
}
} finally {
if (canvas != null) {
surfaceHolder!!.unlockCanvasAndPost(canvas)
}
}
}
try {
costTime = fpsInterval - System.currentTimeMillis() + lastUpdateTime
if (costTime > 0) {
sleep(costTime)
}
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}
// 轨迹点对象(可复用)
class Point {
var x: Float = 0f
var y: Float = 0f
var isActive: Boolean = false
fun set(x: Float, y: Float) {
this.x = x
this.y = y
this.isActive = true
}
fun reset() {
this.isActive = false
}
}
}

View File

@@ -7,15 +7,16 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.business.routeoverlay.DecisionDataManager
import com.mogo.eagle.core.function.business.routeoverlay.PredictionDataManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.map.R
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import kotlinx.android.synthetic.main.layout_decision_container.view.decMapView
import kotlinx.android.synthetic.main.layout_decision_container.view.preDetailView2
import kotlinx.android.synthetic.main.layout_decision_container.view.preDetailView3
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.layout_decision_container.view.tvPre1
import kotlinx.android.synthetic.main.layout_decision_container.view.tvPre2
import kotlinx.android.synthetic.main.layout_decision_container.view.tvPre3
import me.jessyan.autosize.utils.AutoSizeUtils
class DecisionLayout @JvmOverloads constructor(
context: Context,
@@ -29,31 +30,40 @@ class DecisionLayout @JvmOverloads constructor(
@Volatile
private var lastTime = 0L
private var carType = 0
init {
LayoutInflater.from(context).inflate(R.layout.layout_decision_container, this, true)
initView()
initView(attrs)
}
private fun initView() {
private fun initView(attrs: AttributeSet?) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarMapLayout)
carType = typedArray.getInt(R.styleable.CarMapLayout_car_type, 0)
typedArray.recycle()
when (carType) {
0 -> LayoutInflater.from(context).inflate(R.layout.layout_decision_container, this, true)
else -> LayoutInflater.from(context).inflate(R.layout.layout_b2_decision_container, this, true)
}
storeWidthAndHeight()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerAutopilotIdentifyListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerAutopilotIdentifyListenerManager.removeListener(TAG)
}
fun onCreate(savedInstanceState: Bundle?) {
decMapView.onCreate(savedInstanceState)
preDetailView2.onCreate(savedInstanceState)
preDetailView3.onCreate(savedInstanceState)
}
fun onSaveInstanceState(outState: Bundle) {
decMapView.onSaveInstanceState(outState)
preDetailView2.onSaveInstanceState(outState)
preDetailView3.onSaveInstanceState(outState)
}
fun onResume() {
decMapView.onResume()
preDetailView2.onResume()
preDetailView3.onResume()
}
@SuppressLint("SetTextI18n")
@@ -81,20 +91,33 @@ class DecisionLayout @JvmOverloads constructor(
}
fun onLowMemory() {
decMapView.onLowMemory()
preDetailView2.onLowMemory()
preDetailView3.onLowMemory()
}
fun onPause() {
decMapView.onPause()
preDetailView2.onPause()
preDetailView3.onPause()
}
fun onDestroy() {
decMapView.onDestroy()
preDetailView2.onDestroy()
preDetailView3.onDestroy()
}
private fun storeWidthAndHeight() {
UiThreadHandler.post {
when (carType) {
// 默认值为对应0不需要赋值
1 -> {
val decWid = AutoSizeUtils.dp2px(context, 110f)
val decHet = AutoSizeUtils.dp2px(context, 211f)
val mapWid = AutoSizeUtils.dp2px(context, 996f)
val mapHet = AutoSizeUtils.dp2px(context, 650f)
PredictionDataManager.getInstance()?.decWidth = decWid
PredictionDataManager.getInstance()?.decHeight = decHet
PredictionDataManager.getInstance()?.mapWidth = mapWid
PredictionDataManager.getInstance()?.mapHeight = mapHet
DecisionDataManager.getInstance()?.decWidth = decWid
DecisionDataManager.getInstance()?.decHeight = decHet
DecisionDataManager.getInstance()?.mapWidth = mapWid
DecisionDataManager.getInstance()?.mapHeight = mapHet
}
}
}
}
}

View File

@@ -3,15 +3,20 @@ package com.mogo.eagle.core.function.view
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
import androidx.lifecycle.LifecycleObserver
import chassis.Chassis
import com.mogo.eagle.core.data.config.FunctionBuildConfig.accThreshold
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationWGS84Listener
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisStatesListener
import com.mogo.eagle.core.function.business.routeoverlay.DecisionDataManager
import com.mogo.eagle.core.function.business.routeoverlay.PredictionDataManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationWGS84ListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisStatesListenerManager
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager
import com.mogo.eagle.core.function.view.CoordinateAnimationView.Companion
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
import com.mogo.map.MogoMap
import com.mogo.map.MogoMapView

View File

@@ -6,42 +6,56 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.map.R
import kotlinx.android.synthetic.main.layout_prediction_container.view.preDetailView
class PredictionLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : ConstraintLayout(context, attrs) {
private var carType = 0
private var preDetailView: PredictionMapView? = null
init {
LayoutInflater.from(context).inflate(R.layout.layout_prediction_container, this, true)
initView()
initView(attrs)
}
private fun initView() {
private fun initView(attrs: AttributeSet?) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarMapLayout)
carType = typedArray.getInt(R.styleable.CarMapLayout_car_type, 0)
typedArray.recycle()
when (carType) {
0 -> {
LayoutInflater.from(context).inflate(R.layout.layout_prediction_container, this, true)
preDetailView = findViewById(R.id.preDetailView)
}
else -> {
LayoutInflater.from(context).inflate(R.layout.layout_b2_prediction_container, this, true)
preDetailView = findViewById(R.id.preDetailView)
}
}
}
fun onCreate(savedInstanceState: Bundle?) {
preDetailView.onCreate(savedInstanceState)
preDetailView?.onCreate(savedInstanceState)
}
fun onSaveInstanceState(outState: Bundle) {
preDetailView.onSaveInstanceState(outState)
preDetailView?.onSaveInstanceState(outState)
}
fun onResume() {
preDetailView.onResume()
preDetailView?.onResume()
}
fun onLowMemory() {
preDetailView.onLowMemory()
preDetailView?.onLowMemory()
}
fun onPause() {
preDetailView.onPause()
preDetailView?.onPause()
}
fun onDestroy() {
preDetailView.onDestroy()
preDetailView?.onDestroy()
}
}

View File

@@ -116,6 +116,6 @@ class PredictionMap2View(context: Context, attrs: AttributeSet) : MogoMapView(co
override fun onChassisLocationWGS84(gnssInfo: MogoLocation) {
// 跟新地图控件
// setExtraGPSData(gnssInfo)
setExtraGPSData(gnssInfo)
}
}

View File

@@ -116,7 +116,7 @@ class PredictionMap3View(context: Context, attrs: AttributeSet) : MogoMapView(co
override fun onChassisLocationWGS84(gnssInfo: MogoLocation) {
// 跟新地图控件
// setExtraGPSData(gnssInfo)
setExtraGPSData(gnssInfo)
}
}

View File

@@ -129,7 +129,7 @@ class PredictionMapView(context: Context, attrs: AttributeSet) : MogoMapView(con
override fun onChassisLocationWGS84(gnssInfo: MogoLocation) {
// 跟新地图控件
// setExtraGPSData(gnssInfo)
setExtraGPSData(gnssInfo)
if (System.currentTimeMillis() - lastTime >= 5000) {
// 预测给的UTM坐标数据没有带上度带信息根据定位计算度带
mapInstance.getMogoMap(MogoMap.DEFAULT)?.switchData(gnssInfo.longitude, gnssInfo.latitude, true)

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<com.mogo.eagle.core.function.view.CoordinateAnimationView
android:id="@+id/preDetailView2"
android:layout_width="110dp"
android:layout_height="211dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="@dimen/dp_12"
android:layout_marginBottom="11dp"
app:map_index="0"
/>
<com.mogo.eagle.core.function.view.CoordinateAnimationView
android:id="@+id/decMapView"
android:layout_width="110dp"
android:layout_height="211dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="11dp"
app:map_index="1"
/>
<com.mogo.eagle.core.function.view.CoordinateAnimationView
android:id="@+id/preDetailView3"
android:layout_width="110dp"
android:layout_height="211dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="@dimen/dp_12"
android:layout_marginBottom="11dp"
app:map_index="2"
/>
<TextView
android:id="@+id/tvDecTitle"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_36"
android:text="AIP 规划与决策"
android:textColor="#394047"
android:textSize="24dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="@dimen/dp_7"
/>
<TextView
android:id="@+id/tvPre1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvDecTitle"
android:layout_marginStart="@dimen/dp_49"
android:layout_marginTop="@dimen/dp_43"
android:text="11%"
android:textColor="#63707D"
android:textSize="20dp"
/>
<TextView
android:id="@+id/tvPre2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvDecTitle"
android:layout_marginTop="@dimen/dp_43"
android:text="86%"
android:textColor="#63707D"
android:textSize="20dp"
/>
<TextView
android:id="@+id/tvPre3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvDecTitle"
android:layout_marginEnd="@dimen/dp_51"
android:layout_marginTop="@dimen/dp_43"
android:text="3%"
android:textColor="#63707D"
android:textSize="20dp"
/>
</merge>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_36"
android:text="Prediction 预测"
android:textColor="#394047"
android:textSize="@dimen/dp_24"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="@dimen/dp_8"
/>
<com.mogo.eagle.core.function.view.PredictionMapView
android:id="@+id/preDetailView"
android:layout_width="348dp"
android:layout_height="230dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="12dp"
app:carPosition="6"
app:isAutoLocation="false"
app:isDisplayAnim="false"
app:isWeatherEnable="false"
app:styleMode="MAP_STYLE_DAY_VR_AIP"
app:vrAngleMode="MAP_STYLE_VR_ANGLE_TOP" />
</merge>

View File

@@ -6,7 +6,7 @@
android:layout_height="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<com.mogo.eagle.core.function.view.PredictionMap2View
<com.mogo.eagle.core.function.view.CoordinateAnimationView
android:id="@+id/preDetailView2"
android:layout_width="238dp"
android:layout_height="458dp"
@@ -14,14 +14,10 @@
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="@dimen/dp_27"
android:layout_marginBottom="24dp"
app:carPosition="6"
app:isAutoLocation="false"
app:isDisplayAnim="false"
app:isWeatherEnable="false"
app:styleMode="MAP_STYLE_DAY_VR_AIP"
app:vrAngleMode="MAP_STYLE_VR_ANGLE_TOP" />
app:map_index="0"
/>
<com.mogo.eagle.core.function.view.DecisionMapView
<com.mogo.eagle.core.function.view.CoordinateAnimationView
android:id="@+id/decMapView"
android:layout_width="238dp"
android:layout_height="458dp"
@@ -29,16 +25,10 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="24dp"
android:focusable="false"
android:focusableInTouchMode="false"
app:carPosition="6"
app:isAutoLocation="false"
app:isDisplayAnim="false"
app:isWeatherEnable="false"
app:styleMode="MAP_STYLE_DAY_VR_AIP"
app:vrAngleMode="MAP_STYLE_VR_ANGLE_TOP" />
app:map_index="1"
/>
<com.mogo.eagle.core.function.view.PredictionMap3View
<com.mogo.eagle.core.function.view.CoordinateAnimationView
android:id="@+id/preDetailView3"
android:layout_width="238dp"
android:layout_height="458dp"
@@ -46,68 +36,8 @@
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="@dimen/dp_27"
android:layout_marginBottom="20dp"
app:carPosition="6"
app:isAutoLocation="false"
app:isDisplayAnim="false"
app:isWeatherEnable="false"
app:styleMode="MAP_STYLE_DAY_VR_AIP"
app:vrAngleMode="MAP_STYLE_VR_ANGLE_TOP" />
<!-- <androidx.cardview.widget.CardView-->
<!-- android:id="@+id/decContainer"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- app:cardBackgroundColor="#D7F1FF"-->
<!-- app:cardCornerRadius="20dp"-->
<!-- app:cardElevation="0dp"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- >-->
<!-- <com.mogo.eagle.core.function.view.PredictionMap2View-->
<!-- android:id="@+id/preDetailView2"-->
<!-- android:layout_width="238dp"-->
<!-- android:layout_height="458dp"-->
<!-- android:layout_gravity="start|bottom"-->
<!-- android:layout_marginStart="@dimen/dp_27"-->
<!-- android:layout_marginBottom="24dp"-->
<!-- app:carPosition="6"-->
<!-- app:isAutoLocation="false"-->
<!-- app:isDisplayAnim="false"-->
<!-- app:isWeatherEnable="false"-->
<!-- app:styleMode="MAP_STYLE_DAY_VR_AIP"-->
<!-- app:vrAngleMode="MAP_STYLE_VR_ANGLE_TOP" />-->
<!-- <com.mogo.eagle.core.function.view.DecisionMapView-->
<!-- android:id="@+id/decMapView"-->
<!-- android:layout_width="238dp"-->
<!-- android:layout_height="458dp"-->
<!-- android:layout_gravity="center_horizontal|bottom"-->
<!-- android:layout_marginBottom="24dp"-->
<!-- android:focusable="false"-->
<!-- android:focusableInTouchMode="false"-->
<!-- app:carPosition="6"-->
<!-- app:isAutoLocation="false"-->
<!-- app:isDisplayAnim="false"-->
<!-- app:isWeatherEnable="false"-->
<!-- app:styleMode="MAP_STYLE_DAY_VR_AIP"-->
<!-- app:vrAngleMode="MAP_STYLE_VR_ANGLE_TOP" />-->
<!-- <com.mogo.eagle.core.function.view.PredictionMap3View-->
<!-- android:id="@+id/preDetailView3"-->
<!-- android:layout_width="238dp"-->
<!-- android:layout_height="458dp"-->
<!-- android:layout_gravity="end|bottom"-->
<!-- android:layout_marginEnd="@dimen/dp_27"-->
<!-- android:layout_marginBottom="20dp"-->
<!-- app:carPosition="6"-->
<!-- app:isAutoLocation="false"-->
<!-- app:isDisplayAnim="false"-->
<!-- app:isWeatherEnable="false"-->
<!-- app:styleMode="MAP_STYLE_DAY_VR_AIP"-->
<!-- app:vrAngleMode="MAP_STYLE_VR_ANGLE_TOP" />-->
<!-- </androidx.cardview.widget.CardView>-->
app:map_index="2"
/>
<TextView
android:id="@+id/tvDecTitle"

View File

@@ -6,30 +6,6 @@
android:layout_height="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<!-- <androidx.cardview.widget.CardView-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- app:cardBackgroundColor="#D7F1FF"-->
<!-- app:cardCornerRadius="20dp"-->
<!-- app:cardElevation="0dp"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toTopOf="parent">-->
<!-- <com.mogo.eagle.core.function.view.PredictionMapView-->
<!-- android:id="@+id/preDetailView"-->
<!-- android:layout_width="756dp"-->
<!-- android:layout_height="500dp"-->
<!-- android:layout_gravity="center_horizontal|bottom"-->
<!-- android:layout_marginBottom="26dp"-->
<!-- app:carPosition="6"-->
<!-- app:isAutoLocation="false"-->
<!-- app:isDisplayAnim="false"-->
<!-- app:isWeatherEnable="false"-->
<!-- app:styleMode="MAP_STYLE_DAY_VR_AIP"-->
<!-- app:vrAngleMode="MAP_STYLE_VR_ANGLE_TOP" />-->
<!-- </androidx.cardview.widget.CardView>-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@@ -76,4 +76,15 @@
<!-- 是否是订单结束页显示 -->
<attr name="isOrderEnd" format="boolean" />
</declare-styleable>
<declare-styleable name="CoordinateAnimationView">
<attr name="map_index" format="integer" />
</declare-styleable>
<declare-styleable name="CarMapLayout">
<attr name="car_type" format="enum" >
<enum name="Taxi" value="0" />
<enum name="B2" value="1" /> />
</attr>
</declare-styleable>
</resources>