Merge branch 'dev_robotaxi-d-app-module_251_220125_2.5.1'

# Conflicts:
#	modules.txt
This commit is contained in:
donghongyu
2022-02-24 18:43:10 +08:00
2464 changed files with 56050 additions and 26801 deletions

View File

@@ -52,15 +52,24 @@ dependencies {
kapt rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.adasHigh
implementation rootProject.ext.dependencies.mogoami
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.modulecommon
implementation rootProject.ext.dependencies.moduleservice
implementation rootProject.ext.dependencies.mogo_core_data
implementation rootProject.ext.dependencies.mogo_core_utils
implementation rootProject.ext.dependencies.mogo_core_network
implementation rootProject.ext.dependencies.mogo_core_function_api
implementation rootProject.ext.dependencies.mogo_core_function_call
} else {
implementation project(':modules:mogo-module-common')
implementation project(':modules:mogo-module-service')
implementation project(':core:mogo-core-data')
implementation project(':core:mogo-core-utils')
implementation project(':core:mogo-core-network')
implementation project(':core:mogo-core-function-api')
implementation project(':core:mogo-core-function-call')
}

View File

@@ -6,13 +6,22 @@ import androidx.annotation.RequiresPermission
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.eagle.core.data.autopilot.AutopilotControlCmdParameter
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.data.constants.MoGoConfig
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotProvider
import com.mogo.eagle.core.function.api.map.collect.IMoGoMapDataCollectProvider
import com.mogo.eagle.core.function.autopilot.adapter.MoGoAdasListenerImpl
import com.mogo.eagle.core.function.autopilot.adapter.MoGoAdasMsgConnectStatusListenerImpl
import com.mogo.eagle.core.function.autopilot.adapter.MoGoHandAdasMsgManager
import com.mogo.eagle.core.function.autopilot.server.AsyncDataToAutopilotServer
import com.mogo.eagle.core.function.call.map.CallerMapDataCollectorManager
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.eagle.core.utilcode.util.LogUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.zhidao.support.adas.high.AdasManager
import com.zhidao.support.adas.high.bean.IPCUpgradeInfo
import com.zhidao.support.adas.high.common.CupidLogUtils
import java.util.concurrent.TimeUnit
@@ -23,16 +32,46 @@ import java.util.concurrent.TimeUnit
*/
@Route(path = MogoServicePaths.PATH_AUTO_PILOT)
class MoGoAutopilotProvider :
IMoGoAutopilotProvider {
IMoGoAutopilotProvider, IMoGoMapDataCollectProvider.OnMapCollectCmdListener {
private val TAG = "MoGoAutoPilotProvider"
private var mContext: Context? = null
override val functionName: String
get() = TAG
override fun init(context: Context) {
MoGoHandAdasMsgManager.getInstance();
Logger.d(TAG, "初始化工控机连接……")
mContext = context
// 初始化ADAS 域控制器
//AdasManager.getInstance().create(context)
CupidLogUtils.setEnableLog(false)
CupidLogUtils.setIsWriteLog(false)
// TODO 临时方案根据不同的身份标识连接不同的工控机IP
when (FunctionBuildConfig.appIdentityMode) {
0x00 -> // 司机
{
CallerMapDataCollectorManager.registerOnMapCollectTaskListener(this)
AdasManager.getInstance().create(context, FunctionBuildConfig.adasConnectIP)
}
0x01 -> // 乘客
{
// 乘客端默认接收绘制全局路径+引导线
//FunctionBuildConfig.isDemoMode = true
//FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = true
AdasManager.getInstance().create(context, FunctionBuildConfig.adasConnectIP)
}
else -> // 默认采用UDP寻址方式
AdasManager.getInstance().create(context)
}
//////////////////////////////////注意先后顺序AdasManager.getInstance().create后才可以设置监听/////////////////////////////////////////////
// 监听 adas 连接状态
AdasManager.getInstance().setOnAdasConnectStatusListener(MoGoAdasMsgConnectStatusListenerImpl())
// 监听ADAS-SDK获取到的工控机数据
AdasManager.getInstance().setOnAdasListener(MoGoAdasListenerImpl())
// 同步数据给工控机的服务
AsyncDataToAutopilotServer.INSTANCE.initServer()
// 同步是否开启美化模式
setDemoMode(FunctionBuildConfig.isDemoMode)
}
/**
@@ -47,6 +86,8 @@ class MoGoAutopilotProvider :
ThreadUtils.executeBySingleWithDelay(object : ThreadUtils.SimpleTask<String>() {
@RequiresPermission(permission.INTERNET)
override fun doInBackground(): String {
// 保存本地 AutoPilot IP地址
mContext?.let { SharedPrefsMgr.getInstance(it).putString(MoGoConfig.AUTOPILOT_IP, autoPilotIp) }
// 设置IP地址
AdasManager.getInstance().setIPCIp(autoPilotIp)
// 打开通讯连接
@@ -60,12 +101,22 @@ class MoGoAutopilotProvider :
}, 1000, TimeUnit.MILLISECONDS)
}
override fun onMapCollectStart(cmdId: Int, cmdTime: Long) {
val result = recordPackage(2, cmdId)
Logger.d(TAG, "开始记录包: [$cmdId, $result]")
}
override fun onMapCollectEnd(cmdId: Int, cmdTime: Long) {
val result = stopRecord(2, cmdId)
Logger.d(TAG, "结束记录包: [$cmdId, $result]")
}
override fun startAutoPilot(result: AutopilotControlParameters) {
if (AdasManager.getInstance().isSocketConnect) {
val parameter = AutopilotControlCmdParameter("aiCloudToStartAutopilot", result)
AdasManager.getInstance().aiCloudToAdasData(GsonUtils.toJson(parameter))
} else {
LogUtils.eTag(TAG, "车机与工控机链接失败,无法开启自动驾驶")
Logger.e(TAG, "车机与工控机链接失败,无法开启自动驾驶")
}
}
@@ -77,12 +128,25 @@ class MoGoAutopilotProvider :
if (AdasManager.getInstance().isSocketConnect) {
AdasManager.getInstance().controlAutopilotCarHead()
} else {
LogUtils.eTag(TAG, "车机与工控机链接失败,无法断开自动驾驶")
Logger.e(TAG, "车机与工控机链接失败,无法断开自动驾驶")
}
}
override fun recordPackage(): Boolean {
return AdasManager.getInstance().recordPackage(1, (System.currentTimeMillis() / 1000).toInt())
return AdasManager.getInstance()
.recordPackage(1, (System.currentTimeMillis() / 1000).toInt())
}
override fun recordPackage(type: Int, id: Int): Boolean {
return AdasManager.getInstance().recordPackage(type, id)
}
override fun recordPackage(type: Int, id: Int, duration: Int): Boolean {
return AdasManager.getInstance().recordPackage(type, id, duration)
}
override fun stopRecord(type: Int, id: Int): Boolean {
return AdasManager.getInstance().stopRecord(type, id)
}
override fun setEnableLog(isEnableLog: Boolean) {
@@ -94,10 +158,62 @@ class MoGoAutopilotProvider :
}
override fun onDestroy() {
CallerMapDataCollectorManager.unRegisterOnMapCollectTaskListener(this)
}
override fun setAutoPilotSpeed(speed: Int): Boolean {
return AdasManager.getInstance().setSpeed(speed)
}
override fun setIPCShutDown() {
AdasManager.getInstance().shutdownIPC()
}
override fun setIPCReboot() {
AdasManager.getInstance().rebootIPC()
}
override fun recordCause(key: String?, name: String?, id: String?, reason: String?) {
AdasManager.getInstance().recordCause(key, name, id, reason)
}
/**
* 工控机升级确认
*/
override fun setIPCUpgradeAffirm() {
AdasManager.getInstance().sendBaseInfo(IPCUpgradeInfo.affirm());
}
/**
* 工控机升级取消
*/
override fun setIPCUpgradeCancel() {
AdasManager.getInstance().sendBaseInfo(IPCUpgradeInfo.cancel());
}
/**
* 演示模式(美化模式)
* isEnable = true 开启
* isEnable = false 关闭
*/
override fun setDemoMode(isEnable: Boolean) {
if (isEnable) {
AdasManager.getInstance().enableDemoMode()
} else {
AdasManager.getInstance().disableDemoMode()
}
}
/**
* 办公室调试使用,强制开启自动驾驶,将 statuspilotmodecontrol_pilotmode强追设置为 1
* isEnable = true 开启
* isEnable = false 关闭
*/
override fun setControlAutopilotCarAuto(isEnable: Boolean) {
if (isEnable) {
AdasManager.getInstance().controlAutopilotCarAuto()
} else {
AdasManager.getInstance().controlAutopilotCarHead()
}
}
}

View File

@@ -0,0 +1,325 @@
package com.mogo.eagle.core.function.autopilot.adapter;
import android.util.Log;
import androidx.annotation.Nullable;
import com.mogo.eagle.core.data.autopilot.ADASTrajectoryInfo;
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult;
import com.mogo.eagle.core.data.autopilot.AutopilotCarStateInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotRouteInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotWarnMessage;
import com.mogo.eagle.core.data.config.HdMapBuildConfig;
import com.mogo.eagle.core.data.traffic.TrafficData;
import com.mogo.eagle.core.function.autopilot.utils.AdasObjectConvertUtils;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotCarStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotPlanningListenerManager;
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.module.common.datacenter.SnapshotLocationDataCenter;
import com.mogo.module.service.MarkerServiceHandler;
import com.mogo.module.service.timedelay.TimeDelayUploadManager;
import com.zhidao.support.adas.high.AdasManager;
import com.zhidao.support.adas.high.OnAdasListener;
import com.zhidao.support.adas.high.bean.AutopilotRoute;
import com.zhidao.support.adas.high.bean.AutopilotStatus;
import com.zhidao.support.adas.high.bean.AutopilotWayArrive;
import com.zhidao.support.adas.high.bean.CarLaneInfo;
import com.zhidao.support.adas.high.bean.CarStateInfo;
import com.zhidao.support.adas.high.bean.IPCUpgradeStateInfo;
import com.zhidao.support.adas.high.bean.LightStatueInfo;
import com.zhidao.support.adas.high.bean.ObstaclesInfo;
import com.zhidao.support.adas.high.bean.RectInfo;
import com.zhidao.support.adas.high.bean.SSHResult;
import com.zhidao.support.adas.high.bean.TrajectoryInfo;
import com.zhidao.support.adas.high.bean.WarnMessageInfo;
import com.zhidao.support.adas.high.bean.guardian.AutopilotGuardianInfo;
import com.zhidao.support.adas.high.bean.record.AutopilotRecordResult;
import com.zhidao.support.obu.ami.AmiClientManager;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* @author congtaowang
* @since 2020/10/22
* <p>
* 适配ADAS 回调监听分发
*/
public class MoGoAdasListenerImpl implements OnAdasListener {
private final String TAG = "OnAdasListenerAdapter";
@Override
public void onRectData(RectInfo rectInfo) {
if (HdMapBuildConfig.isMapLoaded) {
ArrayList<TrafficData> recognizedListResults = AdasObjectConvertUtils.INSTANCE.regroupTrafficDataData(rectInfo.getModels());
CallerAutopilotIdentifyListenerManager.INSTANCE.invokeAutopilotIdentifyDataUpdate(recognizedListResults);
}
}
@Override
public void onCarStateData(CarStateInfo carStateInfo) {
if (HdMapBuildConfig.isMapLoaded) {
// Logger.d(TAG, "--------carStateInfo.toString() = " + carStateInfo.toString());
//can数据转发
CarStateInfo.ValuesBean bean = carStateInfo.getValues();
// Log.w("DHY-location", bean.getLon() + "," + bean.getLat() + " OnAdasListenerAdapter-onCarStateData");
if (bean != null) {
int turnLight = bean.getTurnLightOften(); //转向灯状态 0是正常 1是左转 2是右转
AmiClientManager.getInstance().setTurnLightState(turnLight);
int brakeLight = bean.getBrake_light(); //TODO
// Logger.d(TAG, "onCarStateData ---- turnLight = " + turnLight + "---brakeLight = " + brakeLight);
//设置转向灯
CallerHmiManager.INSTANCE.showTurnLight(turnLight);
//设置刹车信息
CallerHmiManager.INSTANCE.showBrakeLight(brakeLight);
} else {
Logger.e(TAG, "bean == null ");
}
AutopilotCarStateInfo autopilotCarStateInfo = AdasObjectConvertUtils.INSTANCE.fromAdasCarStateInfoObject(carStateInfo);
// 将数据通过数据中心分发出去
CallerAutopilotCarStatusListenerManager.INSTANCE.invokeAutopilotCarStateData(autopilotCarStateInfo);
// 同步给MAP地图
adasCarDataCallback(autopilotCarStateInfo);
}
}
public void adasCarDataCallback(@Nullable AutopilotCarStateInfo stateInfo) {
//模拟数据时,不更新由工控机传输的自车位置
if (TimeDelayUploadManager.getInstance().isMock()) {
return;
}
if (stateInfo != null && stateInfo.getValues() != null) {
//Logger.d(TAG,"同步自车数据给地图……");
MogoApisHandler.getInstance().getApis().getAdasControllerApi().setLastLat(stateInfo.getValues().getLat());
MogoApisHandler.getInstance().getApis().getAdasControllerApi().setLastLon(stateInfo.getValues().getLon());
MogoApisHandler.getInstance().getApis().getAdasControllerApi().setSatelliteTime(stateInfo.getValues().getSatelliteTime());
JSONObject data = new JSONObject();
try {
data.putOpt("lon", stateInfo.getValues().getLon());
data.putOpt("lat", stateInfo.getValues().getLat());
data.putOpt("alt", stateInfo.getValues().getAlt());
data.putOpt("speed", stateInfo.getValues().getGnss_speed());
data.putOpt("heading", stateInfo.getValues().getHeading());
data.putOpt("acceleration", stateInfo.getValues().getAcceleration());
data.putOpt("yawRate", stateInfo.getValues().getYaw_rate());
data.putOpt("gpsProvider", 1);
try {
data.putOpt("systemTime", Long.parseLong(stateInfo.getValues().getSystemTime()));
} catch (Exception e) {
// e.printStackTrace();
}
try {
data.putOpt("satelliteTime", Long.parseLong(stateInfo.getValues().getSatelliteTime()));
} catch (Exception e) {
e.printStackTrace();
}
try {
data.putOpt("receiverDataTime", Long.parseLong(stateInfo.getValues().getReceiverDataTime()));
} catch (Exception e) {
// e.printStackTrace();
}
try {
data.putOpt("adasSatelliteTime", Long.parseLong(stateInfo.getValues().getAdasSatelliteTime()));
} catch (Exception e) {
// e.printStackTrace();
}
MarkerServiceHandler.getApis().getMapServiceApi().getMapUIController().syncLocation2Map(data);
SnapshotLocationDataCenter.getInstance().syncAdasLocationInfo(data);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void autopilotStatus(AutopilotStatus autopilotStatus) {
if (HdMapBuildConfig.isMapLoaded) {
AutopilotStatus.ValuesBean autopilotStatusValues = autopilotStatus.getValues();
if (autopilotStatusValues != null) {
// 初始化自动驾驶状态信息
AutopilotStatusInfo autopilotStatusInfo = CallerAutoPilotStatusListenerManager.INSTANCE.getAutoPilotStatusInfo();
autopilotStatusInfo.setState(autopilotStatusValues.getState());
autopilotStatusInfo.setPilotmode(autopilotStatusValues.getPilotmode());
autopilotStatusInfo.setControl_pilotmode(autopilotStatusValues.getControl_pilotmode());
autopilotStatusInfo.setReason(autopilotStatusValues.getReason());
autopilotStatusInfo.setCamera(autopilotStatusValues.getCamera());
autopilotStatusInfo.setRtk(autopilotStatusValues.getRtk());
autopilotStatusInfo.setRadar(autopilotStatusValues.getRadar());
autopilotStatusInfo.setSpeed(autopilotStatusValues.getSpeed());
// 初始化自动驾驶状态信息
if (autopilotStatusInfo.getVersion() == null) {
autopilotStatusInfo.setVersion(AdasManager.getInstance().getAdasConfig().getVersion());
}
if (autopilotStatusInfo.getConnectIP() == null) {
autopilotStatusInfo.setConnectIP(AdasManager.getInstance().getAdasConfig().getAddress());
}
if (autopilotStatusInfo.getDockVersion() == null) {
autopilotStatusInfo.setDockVersion(AdasManager.getInstance().getAdasConfig().getDockVersion());
}
CallerAutoPilotStatusListenerManager.INSTANCE.invokeAutoPilotStatus();
}
}
}
@Override
public void autopilotArrive(AutopilotWayArrive autopilotWayArrive) {
if (HdMapBuildConfig.isMapLoaded) {
Logger.d(TAG, "autopilotArrive : " + autopilotWayArrive);
if (autopilotWayArrive != null) {
AutopilotWayArrive.ResultBean result = autopilotWayArrive.getResult();
if (result != null) {
AutopilotWayArrive.ResultBean.EndLatLonBean endLatLon = result.getEndLatLon();
if (endLatLon != null) {
AutopilotStationInfo stationInfo = new AutopilotStationInfo(result.getCarType(), endLatLon.getLon(), endLatLon.getLat());
CallerAutoPilotStatusListenerManager.INSTANCE.invokeArriveAtStation(stationInfo);
}
}
}
}
}
@Override
public void onAutopilotRoute(AutopilotRoute route) {
if (HdMapBuildConfig.isMapLoaded) {
Logger.d(TAG, "onAutopilotRoute : " + route.toString());
AutopilotRouteInfo autopilotRoute = AdasObjectConvertUtils.INSTANCE.fromAdasAutopilotRoute(route);
CallerAutopilotPlanningListenerManager.INSTANCE.invokeAutopilotRotting(autopilotRoute);
}
}
@Override
public void onAutopilotTrajectory(List<TrajectoryInfo> trajectoryList) {
if (HdMapBuildConfig.isMapLoaded) {
Logger.d(TAG, "onAutopilotTrajectory : " + trajectoryList);
ArrayList<ADASTrajectoryInfo> trajectoryInfoArrayList = new ArrayList<>();
if (trajectoryList != null && trajectoryList.size() > 0) {
for (TrajectoryInfo trajectory : trajectoryList) {
ADASTrajectoryInfo adasTrajectoryInfo = new ADASTrajectoryInfo();
adasTrajectoryInfo.setLat(trajectory.getLat());
adasTrajectoryInfo.setLon(trajectory.getLon());
adasTrajectoryInfo.setAcceleration(trajectory.getAcceleration());
adasTrajectoryInfo.setAccumulatedDis(trajectory.getAccumulatedDis());
adasTrajectoryInfo.setTime(trajectory.getTime());
adasTrajectoryInfo.setVelocity(trajectory.getVelocity());
adasTrajectoryInfo.setAlt(trajectory.getAlt());
adasTrajectoryInfo.setKappa(trajectory.getKappa());
adasTrajectoryInfo.setTheta(trajectory.getTheta());
trajectoryInfoArrayList.add(adasTrajectoryInfo);
}
Log.e(TAG, "time:" + System.currentTimeMillis() + "trajectoryInfoArrayList:" + trajectoryInfoArrayList);
}
CallerAutopilotPlanningListenerManager.INSTANCE.invokeAutopilotTrajectory(trajectoryInfoArrayList);
}
}
@Override
public void onAutopilotSNRequest() {
CallerAutoPilotStatusListenerManager.INSTANCE.invokeAutopilotSNRequest();
}
@Override
public void onAutopilotGuardian(AutopilotGuardianInfo guardianInfo) {
if (HdMapBuildConfig.isMapLoaded) {
AutopilotGuardianStatusInfo autopilotRoute = AdasObjectConvertUtils.INSTANCE.fromAutopilotGuardianInfo(guardianInfo);
CallerAutoPilotStatusListenerManager.INSTANCE.invokeAutopilotGuardian(autopilotRoute);
}
}
@Override
public void onAutopilotRecord(AutopilotRecordResult result) {
if (result != null) {
AutoPilotRecordResult real = new AutoPilotRecordResult();
real.setDiskFree(result.getDiskFree());
real.setId(result.getId());
real.setDuration(result.getDuration());
real.setNote(result.getNote());
real.setTotal(result.getTotalSize());
real.setType(result.getType());
real.setFileName(result.getFilename());
real.setKey(result.getKey());
real.setStat(result.getStat());
real.setTimestamp(result.getTimestamp());
CallerAutopilotIdentifyListenerManager.INSTANCE.invokeAutopilotRecordResult(real);
}
}
@Override
public void onLightStateData(LightStatueInfo lightStatueInfo) {
}
@Override
public void onObstaclesInfo(ObstaclesInfo obstaclesInfo) {
}
@Override
public void onCarLaneInfo(CarLaneInfo carLaneInfo) {
}
@Override
public void onWarnMessage(WarnMessageInfo warnMessageInfo) {
final AutopilotWarnMessage warnMessage = AdasObjectConvertUtils.INSTANCE.fromAdasObject(warnMessageInfo);
CallerAutopilotIdentifyListenerManager.INSTANCE.invokeAutopilotWarnMessage(warnMessage);
}
@Override
public void onVideoSize(int width, int height) {
}
/**
* 工控机升级状态
*
* @param info 工控机升级状态
*/
@Override
public void onUpgradeStateInfo(IPCUpgradeStateInfo info) {
if (info != null) {
Logger.d(TAG, "onUpgradeStateInfo " +
" upgrade mode=" + info.getUpgradeMode() +
" download status=" + info.getDownloadStatus() +
" download progress current=" + info.getProgress().getCurrent() +
" download progress total=" + info.getProgress().getTotal() +
" download version=" + info.getImages() +
" upgrade status=" + info.getUpgradeStatus());
CallerHmiManager.INSTANCE.showAdUpgradeStatus(info.getUpgradeMode(), info.getDownloadStatus(), info.getProgress().getCurrent(),
info.getProgress().getTotal(), info.getImages(), info.getUpgradeStatus());
} else {
Logger.d(TAG, "onUpgradeStateInfo : upgrade state info is null");
}
}
/**
* 命令返回结果
* @param info 结果返回实体
*/
@Override
public void onSSHResult(SSHResult info) {
if(info!=null && "docker restart autocar_default_1".equals(info.cmd)){
CallerHmiManager.INSTANCE.showDockerRebootResult(info.code, info.msg);
}else{
Logger.d(TAG,"onSSHResult : result info is null");
}
}
}

View File

@@ -0,0 +1,106 @@
package com.mogo.eagle.core.function.autopilot.adapter
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.data.app.AppConfigInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.data.constants.MoGoConfig
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.autopilot.network.AdasServiceModel
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.zhidao.support.adas.high.AdasManager
import com.zhidao.support.adas.high.OnAdasMsgConnectStatusListener
import com.zhidao.support.adas.high.bean.BasicInfo
import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit
/**
* ADAS-SDK与工控机连接状态回调
*
* @author donghongyu
*/
class MoGoAdasMsgConnectStatusListenerImpl : OnAdasMsgConnectStatusListener, IMoGoAutopilotStatusListener {
private val TAG = "MoGoAdasMsgConnectStatusListenerImpl"
//自动驾驶状态
private var mCurrentAutopilotStatus = -1
//自动驾驶车速度
private var mCurrentAutopilotSpeed = 0f
override fun onWebSocketConnectSuccess() {
Logger.d(TAG, "webSocket 连接成功")
// 初始化自动驾驶状态信息
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectIP = AdasManager.getInstance().adasConfig.address
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectStatus = true
CallerAutoPilotStatusListenerManager.invokeAutoPilotStatus()
// 同步SN给工控机
syncBasicInfoToAutopilot()
// 开启轮训上传自动驾驶状态
updateDriveStatusTask()
}
override fun onWebSocketConnectFailed(reason: String) {
Logger.d(TAG, "webSocket 连接失败 reason:$reason")
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectIP = AdasManager.getInstance().adasConfig.address
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectStatus = false
CallerAutoPilotStatusListenerManager.invokeAutoPilotStatus()
}
/**
* 工控机获取SN
*/
override fun onAutopilotSNRequest() {
syncBasicInfoToAutopilot()
}
override fun onAutopilotArriveAtStation(autopilotWayArrive: AutopilotStationInfo?) {
if (autopilotWayArrive != null) {
val lon = autopilotWayArrive.lon
val lat = autopilotWayArrive.lat
AdasServiceModel.getInstance().reportSite(lon, lat)
}
}
override fun onAutopilotStatusResponse(autoPilotStatusInfo: AutopilotStatusInfo) {
val state = autoPilotStatusInfo.state
val speed = autoPilotStatusInfo.speed
mCurrentAutopilotStatus = state
mCurrentAutopilotSpeed = speed
}
/**
* 同步SN信息给工控机
*/
private fun syncBasicInfoToAutopilot() {
Logger.d(TAG, "同步PAD的SN给工控机……")
val info = BasicInfo()
// 设置PAD-SN给工控
info.setSn(MoGoAiCloudClientConfig.getInstance().sn)
// 设置网络环境
info.setNetEnvironment(DebugConfig.getNetMode())
AdasManager.getInstance().setBasicInfo(info)
}
/**
* 上传自动驾驶状态任务
*/
private fun updateDriveStatusTask() {
Logger.d(TAG, "updateDriveStatusTask")
Flowable.interval(0, 5, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { aLong: Long? ->
AdasServiceModel.getInstance().updateDriveStatus(mCurrentAutopilotStatus, mCurrentAutopilotSpeed)
}
}
}

View File

@@ -1,38 +0,0 @@
package com.mogo.eagle.core.function.autopilot.adapter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
/**
* @author xiaoyuzhou
* @date 2021/10/20 1:02 下午
* 自动驾驶状态回调用
*/
public class MoGoAutopilotStatusListenerImpl implements IMoGoAutopilotStatusListener {
@Override
public void onAutopilotStatusResponse(@NonNull AutopilotStatusInfo autoPilotStatusInfo) {
}
@Override
public void onAutopilotArriveAtStation(@Nullable AutopilotStationInfo autopilotWayArrive) {
}
@Override
public void onAutopilotSNRequest() {
}
@Override
public void onAutopilotGuardian(@Nullable AutopilotGuardianStatusInfo guardianInfo) {
}
}

View File

@@ -0,0 +1,85 @@
package com.mogo.eagle.core.function.autopilot.adapter;
import androidx.annotation.Nullable;
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult;
import com.mogo.eagle.core.data.autopilot.AutopilotCarStateInfo;
import com.mogo.eagle.core.data.autopilot.AutopilotWarnMessage;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.traffic.TrafficData;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotCarStateListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotCarStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
import com.mogo.module.common.drawer.IdentifyDataDrawer;
import com.zhidao.support.obu.ami.AmiClientManager;
import java.util.ArrayList;
public class MoGoHandAdasMsgManager implements
IMoGoAutopilotIdentifyListener,
IMoGoAutopilotCarStateListener {
private final String TAG = "AdasEventManager";
private static volatile MoGoHandAdasMsgManager moGoHandAdasMsgManager;
private MoGoHandAdasMsgManager() {
CallerAutopilotIdentifyListenerManager.INSTANCE.addListener(TAG, this);
CallerAutopilotCarStatusListenerManager.INSTANCE.addListener(TAG, this);
}
public static MoGoHandAdasMsgManager getInstance() {
if (moGoHandAdasMsgManager == null) {
synchronized (MoGoHandAdasMsgManager.class) {
if (moGoHandAdasMsgManager == null) {
moGoHandAdasMsgManager = new MoGoHandAdasMsgManager();
}
}
}
return moGoHandAdasMsgManager;
}
@Override
public void onAutopilotCarStateData(@Nullable AutopilotCarStateInfo autoPilotCarStateInfo) {
// 获取的自动驾驶车辆信息不是空的时候
if (autoPilotCarStateInfo != null) {
//can数据转发
AutopilotCarStateInfo.ValuesBean bean = autoPilotCarStateInfo.getValues();
if (bean != null) {
//转向灯状态 0是正常 1是左转 2是右转
int turnLight = bean.getTurn_light();
AmiClientManager.getInstance().setTurnLightState(turnLight);
}
}
}
@Override
public void onAutopilotIdentifyDataUpdate(@Nullable ArrayList<TrafficData> trafficData) {
try {
if (FunctionBuildConfig.isDrawIdentifyData) {
ThreadUtils.getSinglePool().execute(() ->
IdentifyDataDrawer.getInstance().renderAdasRecognizedResult(trafficData)
);
} else {
IdentifyDataDrawer.getInstance().clearOldMarker();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onAutopilotWarnMessage(@Nullable AutopilotWarnMessage autopilotWarnMessage) {
}
@Override
public void onAutopilotRecordResult(AutoPilotRecordResult result) {
}
}

View File

@@ -0,0 +1,41 @@
package com.mogo.eagle.core.function.autopilot.entity;
/**
* Created by XuYong on 2021/5/28 16:12
*/
public class AutonomousDriveStatusBean {
private String sn;
private int status;
private float vehicleSpeed;
public AutonomousDriveStatusBean(String sn, int status, float vehicleSpeed) {
this.sn = sn;
this.status = status;
this.vehicleSpeed = vehicleSpeed;
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public float getVehicleSpeed() {
return vehicleSpeed;
}
public void setVehicleSpeed(float vehicleSpeed) {
this.vehicleSpeed = vehicleSpeed;
}
}

View File

@@ -0,0 +1,40 @@
package com.mogo.eagle.core.function.autopilot.entity;
/**
* Created by XuYong on 2021/5/31 16:24
*/
public class ReportSiteBean {
private String sn;
private double lon;
private double lat;
public ReportSiteBean(String sn, double lon, double lat) {
this.sn = sn;
this.lon = lon;
this.lat = lat;
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
public double getLon() {
return lon;
}
public void setLon(double lon) {
this.lon = lon;
}
public double getLat() {
return lat;
}
public void setLat(double lat) {
this.lat = lat;
}
}

View File

@@ -0,0 +1,98 @@
package com.mogo.eagle.core.function.autopilot.network;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.commons.context.ContextHolderUtil;
import com.mogo.eagle.core.data.BaseData;
import com.mogo.eagle.core.function.autopilot.entity.AutonomousDriveStatusBean;
import com.mogo.eagle.core.function.autopilot.entity.ReportSiteBean;
import com.mogo.eagle.core.network.RequestOptions;
import com.mogo.eagle.core.network.SubscribeImpl;
import com.mogo.eagle.core.network.utils.GsonUtil;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.module.common.constants.HostConst;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.RequestBody;
/**
* 将自动驾驶状态及站点信息上传给云平台,云平台会根据此数据做路线的绘制
*
* @author dongghongyu
*/
public class AdasServiceModel {
private static final String TAG = "AdasServiceModel";
private static volatile AdasServiceModel instance;
private static final byte[] obj = new byte[0];
private final IAdasApiService mAdasApiService;
private AdasServiceModel() {
this.mAdasApiService = MogoApisHandler.getInstance().getApis().getNetworkApi().create(IAdasApiService.class, HostConst.DATA_SERVICE_HOST);
}
public static AdasServiceModel getInstance() {
if (instance == null) {
synchronized (obj) {
if (instance == null) {
instance = new AdasServiceModel();
}
}
}
return instance;
}
public void updateDriveStatus(int autoPilotStatus, float autoPilotSpeed) {
String sn = MoGoAiCloudClientConfig.getInstance().getSn();
AutonomousDriveStatusBean request = new AutonomousDriveStatusBean(sn, autoPilotStatus, autoPilotSpeed);
RequestBody requestBody = RequestBody.create(MediaType.get("application/json;charset=UTF-8"), GsonUtil.jsonFromObject(request));
mAdasApiService.updateAutonomousDriveStatus(requestBody).
subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new SubscribeImpl<BaseData>(RequestOptions.create(ContextHolderUtil.getContext())) {
@Override
public void onError(String message, int code) {
super.onError(message, code);
}
@Override
public void onError(Throwable e) {
super.onError(e);
}
@Override
public void onSuccess(BaseData o) {
super.onSuccess(o);
Logger.d(TAG, "updateDriveStatus success");
}
});
}
public void reportSite(double lon, double lat) {
String sn = MoGoAiCloudClientConfig.getInstance().getSn();
ReportSiteBean reportSiteBean = new ReportSiteBean(sn, lon, lat);
RequestBody requestBody = RequestBody.create(MediaType.get("application/json;charset=UTF-8"), GsonUtil.jsonFromObject(reportSiteBean));
mAdasApiService.updateReportSite(requestBody).
subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new SubscribeImpl<BaseData>(RequestOptions.create(ContextHolderUtil.getContext())) {
@Override
public void onError(String message, int code) {
super.onError(message, code);
}
@Override
public void onError(Throwable e) {
super.onError(e);
}
@Override
public void onSuccess(BaseData o) {
super.onSuccess(o);
Logger.d(TAG, "autopilotArrive success");
}
});
}
}

View File

@@ -0,0 +1,31 @@
package com.mogo.eagle.core.function.autopilot.network;
import com.mogo.eagle.core.data.BaseData;
import io.reactivex.Observable;
import okhttp3.RequestBody;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface IAdasApiService {
/**
* 上传自动驾驶状态接口
* @param requestBody
* @return
*/
@Headers({"Content-Type:application/json;charset=UTF-8"})
@POST("/dataService/autonomousDrive/updateAutonomousDriveStatus")
Observable<BaseData> updateAutonomousDriveStatus(@Body RequestBody requestBody);
/**
* 站点上报 用于网约车业务 云平台绘制路线使用
* @param requestBody
* @return
*/
@Headers({"Content-Type:application/json;charset=UTF-8"})
@POST("/dataService/autonomousDrive/reportSite")
Observable<BaseData> updateReportSite(@Body RequestBody requestBody);
}

View File

@@ -1,10 +1,102 @@
package com.mogo.eagle.core.function.autopilot.utils
import com.mogo.eagle.core.data.autopilot.AutopilotCarStateInfo
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo
import com.mogo.eagle.core.data.autopilot.AutopilotRouteInfo
import com.mogo.eagle.core.data.autopilot.AutopilotWarnMessage
import com.mogo.eagle.core.data.enums.TrafficTypeEnum
import com.mogo.eagle.core.data.traffic.TrafficData
import com.zhidao.support.adas.high.bean.AutopilotRoute
import com.zhidao.support.adas.high.bean.CarStateInfo
import com.zhidao.support.adas.high.bean.RectInfo
import com.zhidao.support.adas.high.bean.WarnMessageInfo
import com.zhidao.support.adas.high.bean.guardian.AutopilotGuardianInfo
import com.zhidao.utils.common.GsonUtil
/**
* @author xiaoyuzhou
* @date 2021/10/18 1:32 下午
*/
class AdasObjectConvertUtils {
object AdasObjectConvertUtils {
val TAG = "AdasObjectConvertUtils"
fun fromAutopilotGuardianInfo(autopilotGuardianInfo: AutopilotGuardianInfo): AutopilotGuardianStatusInfo? {
return GsonUtil.objectFromJson(
GsonUtil.jsonFromObject(autopilotGuardianInfo),
AutopilotGuardianStatusInfo::class.java
)
}
fun fromAdasAutopilotRoute(carStateInfo: AutopilotRoute): AutopilotRouteInfo? {
return GsonUtil.objectFromJson(
GsonUtil.jsonFromObject(carStateInfo),
AutopilotRouteInfo::class.java
)
}
fun fromAdasCarStateInfoObject(carStateInfo: CarStateInfo): AutopilotCarStateInfo? {
return GsonUtil.objectFromJson(
GsonUtil.jsonFromObject(carStateInfo),
AutopilotCarStateInfo::class.java
)
}
fun fromAdasObject(info: WarnMessageInfo?): AutopilotWarnMessage? {
if (info == null) {
return null
}
val warnMessage = AutopilotWarnMessage()
warnMessage.content = info.content
warnMessage.level = info.level
try {
warnMessage.type = info.type.toInt()
} catch (e: NumberFormatException) {
return null
}
warnMessage.value = info.value
return warnMessage
}
fun regroupTrafficDataData(datums: List<RectInfo.RectBean?>?): ArrayList<TrafficData>? {
if (datums == null || datums.isEmpty()) {
return null
}
val recognizedListResults: ArrayList<TrafficData> = ArrayList()
for (model in datums) {
if (model == null) {
continue
}
val recognizedListResult = fromAdasTrafficDataObject(model)
if (recognizedListResult != null) {
recognizedListResults.add(recognizedListResult)
}
}
return recognizedListResults
}
fun fromAdasTrafficDataObject(model: RectInfo.RectBean?): TrafficData? {
if (model == null) {
return null
}
val result = TrafficData()
result.uuid = model.uuid
result.lat = model.lat
result.lon = model.lon
result.type = TrafficTypeEnum.getType(model.type.toInt())
result.heading = model.heading
result.systemTime = model.systemTime.toLong()
result.satelliteTime = model.satelliteTime.toLong()
result.alt = model.alt
result.speed = model.speed
result.carId = model.carId
result.dataAccuracy = model.dataAccuracy
result.distance = model.distance
result.threatLevel = model.drawlevel
//Log.d(TAG, "TrafficData = $result")
return result
}
}

View File

@@ -2,7 +2,6 @@ package com.mogo.eagle.core.function.check
import android.content.Context
import android.content.Intent
import android.util.Log
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.api.check.ICheckProvider
@@ -12,14 +11,11 @@ import com.mogo.eagle.core.function.check.net.CheckNetWork.checkNetWork
import com.mogo.eagle.core.function.check.net.CheckResultData
import com.mogo.eagle.core.function.check.view.CheckActivity
import com.mogo.eagle.core.function.check.view.CheckDialog
import com.mogo.eagle.core.utilcode.util.ActivityUtils
import com.mogo.eagle.core.utilcode.util.AppUtils
import com.mogo.eagle.core.utilcode.util.LogUtils
import com.mogo.eagle.core.utilcode.util.*
import com.mogo.module.common.MogoApisHandler
import com.mogo.module.service.receiver.MogoReceiver
import com.mogo.service.statusmanager.IMogoStatusChangedListener
import com.mogo.service.statusmanager.StatusDescriptor
import com.mogo.utils.ActivityLifecycleManager
import java.util.concurrent.ConcurrentHashMap
/**
@@ -89,7 +85,7 @@ class VehicleMonitoringManager : ICheckProvider, IMogoStatusChangedListener {
*/
private fun showDialog(context: Context) {
try {
if (ActivityLifecycleManager.getInstance().isAppActive && AppUtils.isAppRunning(
if (AppStateManager.isActive() && AppUtils.isAppRunning(
AppUtils.getAppPackageName()
) && ActivityUtils.getTopActivity() !is CheckActivity
) {

View File

@@ -9,13 +9,13 @@ import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsListenerManager
import com.mogo.eagle.core.network.NetConfig
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.TimeUtils
import com.mogo.module.common.MogoApisHandler
import com.mogo.service.cloud.socket.IMogoOnMessageListener
import com.mogo.utils.logger.LogLevel
import com.mogo.utils.logger.Logger
import com.zhidao.loglib.bean.RemoteLogPushContent
import com.zhidao.loglib.call.LogInfoManagerFactory
import com.zhidao.loglib.core.ILogListener
@@ -47,7 +47,7 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
MogoApisHandler.getInstance().apis
.getSocketManagerApi(AbsMogoApplication.getApp().applicationContext)
.registerOnMessageListener(LOG_PUSH_TYPE, this)
manualContent.duration = 60
manualContent.duration = 10
manualContent.pkgName = context.packageName
}
@@ -98,19 +98,23 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
private fun startCatchLog(content: RemoteLogPushContent) {
catchingList.add(content.pkgName)
var delay = (content.duration * 60 * 1000).toLong()
var delay = (content.duration).toLong()
handler.removeMessages(MSG_TRY_CLOSE_LOG)
if (delay <= 0) {
// 如果push 下来的delay小于等于0那就给个默认最大值一小时
delay = 60 * 60 * 1000L
delay = 10
}
handler.sendEmptyMessageDelayed(MSG_TRY_CLOSE_LOG, delay)
handler.sendEmptyMessageDelayed(MSG_TRY_CLOSE_LOG, delay * 1000L * 60)
openLoggerLevel()
logInfoManager = LogInfoManagerFactory.createPushLogInfoManager(
mContext,
MoGoAiCloudClientConfig.getInstance().sn + File.separator + TimeUtils.formatYMD(System.currentTimeMillis()),
content, this)
content, this
)
logInfoManager?.start()
logInfoManager?.registerLogOutListener { lineLog ->
CallerDevaToolsListenerManager.invokeDevaToolsLogCatchLines(lineLog)
}
}
private fun stopCatchLog(content: RemoteLogPushContent) {

View File

@@ -53,6 +53,10 @@ dependencies {
implementation rootProject.ext.dependencies.mogoaicloudtrafficlive
kapt rootProject.ext.dependencies.aroutercompiler
kapt rootProject.ext.dependencies.androidxroomcompiler
implementation rootProject.ext.dependencies.androidxroomruntime
implementation rootProject.ext.dependencies.androidxroomktx
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
implementation rootProject.ext.dependencies.androidxrecyclerview

View File

@@ -38,5 +38,13 @@
</intent-filter>
</receiver>
<!--转向灯和刹车-->
<receiver android:name="com.mogo.eagle.core.function.hmi.receiver.TurnLightBroadcastReceiver">
<intent-filter>
<action android:name="com.hmi.turnlight" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -1,12 +1,18 @@
package com.mogo.eagle.core.function.hmi.notification
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.view.ContextThemeWrapper
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
import com.mogo.eagle.core.function.hmi.notification.interfaces.OnFloatAnimator
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWarningStatusListener
import com.mogo.utils.WindowUtils
import com.mogo.utils.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.reminder.Reminder
import com.mogo.eagle.core.utilcode.reminder.api.impl.ViewReminder
import com.mogo.eagle.core.utilcode.util.WindowUtils
/**
* @author donghongyu
@@ -33,7 +39,6 @@ class WarningFloat {
}
/**
* 浮窗的属性构建类,支持链式调用
*/
@@ -147,6 +152,22 @@ class WarningFloat {
}
}
fun setWindowWidth(width: Int) = apply {
this.config.width = width
}
fun setWindowHeight(height: Int) = apply {
this.config.height = height
}
fun isOverride(isOverride: Boolean) = apply {
this.config.isOverride = isOverride
}
fun isEnqueue(enqueue: Boolean) = apply {
this.config.isEnqueue = enqueue
}
/**
* 创建浮窗包括Activity浮窗和系统浮窗如若系统浮窗无权限先进行权限申请
*/
@@ -156,9 +177,66 @@ class WarningFloat {
config.layoutId == null && config.layoutView == null ->
Logger.e(TAG, "需要传入 layoutId 或 layoutView ")
// 申请浮窗权限
else -> WarningFloatWindowManager.create(activity, config)
else -> {
var content: View? = null
if (config.layoutView != null) {
content = config.layoutView
} else if (config.layoutId != null) {
content = LayoutInflater.from(activity).inflate(config.layoutId!!, null)
}
if (config.isEnqueue) {
content?.let {
config.layoutId = null
config.layoutView = it
Reminder.enqueue(getLifecycleOwner(activity), WarningFloatReminder(activity, config, it))
}
} else {
WarningFloatWindowManager.create(activity, config)
}
}
}
}
private fun getLifecycleOwner(context: Context): LifecycleOwner {
if (context is LifecycleOwner) {
return context
}
if (context is ContextThemeWrapper) {
return getLifecycleOwner(context.baseContext)
}
return ProcessLifecycleOwner.get()
}
internal class WarningFloatReminder(private val activity: Context, private val config: WarningNotificationConfig, content: View): ViewReminder(content) {
private var hasShow = false
override fun show() {
hasShow = !WarningFloatWindowManager.create(activity, config)
}
override fun hide() {
dismiss(config.floatTag, false)
}
override fun isOverride(): Boolean {
return config.isOverride
}
override fun maxProtectDuration(): Long {
return if (hasShow) 0L else super.maxProtectDuration()
}
}
fun isShow(): Boolean = WarningFloatWindowManager.getHelper(config.floatTag)?.isShow() ?: false
fun resetExpireTime(expireTime: Long) {
if (!isShow()) {
return
}
WarningFloatWindowManager.getHelper(config.floatTag)?.resetDownTime(expireTime)
}
}
}

View File

@@ -12,12 +12,11 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.notification.anim.AnimatorManager
import com.mogo.eagle.core.function.hmi.notification.enums.ShowPattern
import com.mogo.eagle.core.function.hmi.notification.widget.ParentFrameLayout
import com.mogo.utils.WindowUtils
import com.mogo.utils.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.WindowUtils
/**
* @author donghongyu
@@ -66,8 +65,8 @@ internal class WarningFloatWindowHelper(
// 没有边界限制,允许窗口扩展到屏幕外
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
else WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
width = config.width
height = config.height
// 如若设置了固定坐标,直接定位
if (config.locationPair != Pair(0, 0)) {
@@ -132,6 +131,11 @@ internal class WarningFloatWindowHelper(
}
}
fun resetDownTime(expireTime: Long) {
config.countDownTime = expireTime
resetDownTime()
}
/**
* 入场动画
*/
@@ -293,5 +297,5 @@ internal class WarningFloatWindowHelper(
windowManager.updateViewLayout(view, params)
}
fun isShow(): Boolean = config.isShow
}

View File

@@ -18,15 +18,17 @@ internal object WarningFloatWindowManager {
* 创建浮窗tag不存在创建tag存在创建失败
* 创建结果通过tag添加到相应的map进行管理
*/
fun create(context: Context, config: WarningNotificationConfig) {
fun create(context: Context, config: WarningNotificationConfig): Boolean {
if (!checkTag(config)) {
val helper = WarningFloatWindowHelper(context, config)
if (helper.createWindow()) windowMap[config.floatTag!!] = helper
return true
} else {
Log.w(TAG, "存在相同的tag延长弹窗时间")
// 存在相同的tag直接创建失败
config.callbacks?.createdResult(false, "存在相同的tag延长弹窗时间", null)
windowMap[config.floatTag!!]?.resetDownTime()
return false
}
}

View File

@@ -1,6 +1,7 @@
package com.mogo.eagle.core.function.hmi.notification
import android.view.View
import android.view.WindowManager
import com.mogo.eagle.core.function.hmi.notification.anim.DefaultAnimator
import com.mogo.eagle.core.function.hmi.notification.enums.ShowPattern
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
@@ -59,4 +60,12 @@ data class WarningNotificationConfig(
// Callbacks
var callbacks: OnFloatCallbacks? = null,
)
// 窗口宽度
var width: Int = WindowManager.LayoutParams.WRAP_CONTENT,
// 窗口高度
var height: Int = WindowManager.LayoutParams.WRAP_CONTENT
) {
var isEnqueue: Boolean = false
var isOverride: Boolean = true
}

View File

@@ -7,7 +7,7 @@ import android.view.View
import android.view.WindowManager
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
import com.mogo.eagle.core.function.hmi.notification.interfaces.OnFloatAnimator
import com.mogo.utils.WindowUtils
import com.mogo.eagle.core.utilcode.util.WindowUtils
import kotlin.math.min
/**

View File

@@ -4,15 +4,11 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.module.common.enums.EventTypeEnum
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.service.IMogoServiceApis
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.utilcode.util.SharedPrefs
import com.mogo.utils.logger.Logger
/**
* 用于普通云公告的测试

View File

@@ -0,0 +1,50 @@
package com.mogo.eagle.core.function.hmi.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager.showBrakeLight
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager.showTurnLight
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.SharedPrefs
import com.mogo.service.IMogoServiceApis
/**
* 转向灯,刹车
*
* @author lixiaopeng
*/
class TurnLightBroadcastReceiver : BroadcastReceiver() {
companion object {
private const val TAG = "TurnLightBroadcastReceiver"
}
override fun onReceive(context: Context, intent: Intent) {
try {
val type = intent.getIntExtra("type", 0)
val lightInt = intent.getIntExtra("light", 0)
showTurnLight(type, lightInt)
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun showTurnLight( //type 1,转向灯2刹车
type: Int,
lightInt: Int
) {
if (type == 1) {
showTurnLight(lightInt) //设置转向灯
} else if (type == 2) {
showBrakeLight(lightInt) //设置刹车信息
}
}
}

View File

@@ -7,8 +7,8 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.service.IMogoServiceApis
import com.mogo.utils.logger.Logger
/**
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式 控制限速标志

View File

@@ -7,8 +7,8 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.service.IMogoServiceApis
import com.mogo.utils.logger.Logger
/**
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式 触发红绿灯场景

View File

@@ -7,9 +7,9 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.module.common.enums.EventTypeEnum
import com.mogo.service.IMogoServiceApis
import com.mogo.utils.logger.Logger
/**
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式弹出预警提示框
@@ -78,12 +78,6 @@ class V2XWarningBroadcastReceiver : BroadcastReceiver() {
if (EventTypeEnum.TYPE_USECASE_ID_IVP.poiType == v2xType.toString()) {
CallerHmiManager.showLimitingVelocity(1)
}
CallerHmiManager.showWarningV2X(
v2xType,
alertContent,
ttsContent,
tag,
null
)
CallerHmiManager.showWarningV2X(v2xType, alertContent, ttsContent, tag, null, true, 5000L)
}
}

View File

@@ -1,24 +1,35 @@
package com.mogo.eagle.core.function.hmi.ui
import android.animation.Animator
import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.view.*
import android.view.animation.OvershootInterpolator
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.lifecycleScope
import androidx.transition.*
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.mvp.MvpFragment
import com.mogo.commons.voice.AIAssist
import com.mogo.eagle.core.data.autopilot.AdUpgradeStateHelper
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.data.camera.CameraEntity
import com.mogo.eagle.core.data.config.HmiBuildConfig
import com.mogo.eagle.core.data.constants.MoGoFragmentPaths
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWarningStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.call.check.CallerCheckManager
import com.mogo.eagle.core.function.call.map.CallerMapDataCollectorManager
import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.WaringConst
@@ -31,12 +42,23 @@ import com.mogo.eagle.core.function.hmi.ui.notice.NoticeBannerView
import com.mogo.eagle.core.function.hmi.ui.notice.NoticeNormalBannerView
import com.mogo.eagle.core.function.hmi.ui.setting.DebugSettingView
import com.mogo.eagle.core.function.hmi.ui.tools.AutoPilotAndCheckView
import com.mogo.eagle.core.function.hmi.ui.tools.AutoPilotBadCaseView
import com.mogo.eagle.core.function.hmi.ui.tools.Repository
import com.mogo.eagle.core.function.hmi.ui.tools.post
import com.mogo.eagle.core.function.hmi.ui.widget.V2XNotificationView
import com.mogo.eagle.core.utilcode.kotlin.lifecycleOwner
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.module.common.enums.EventTypeEnum
import com.mogo.utils.logger.Logger
import kotlinx.android.synthetic.main.fragment_hmi.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
/**
* @author xiaoyuzhou
@@ -46,7 +68,7 @@ import kotlinx.android.synthetic.main.fragment_hmi.*
@Route(path = MoGoFragmentPaths.PATH_FRAGMENT_HMI)
class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>(),
IMoGoWaringProvider,
MoGoWarningContract.View {
MoGoWarningContract.View, IMoGoAutopilotIdentifyListener {
private val TAG = "MoGoHmiFragment"
// DebugSettingView
@@ -56,7 +78,6 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
// V2X、OBU、云端推送预警弹窗
private var mWarningFloat: WarningFloat.Builder? = null
// 通知、云公告弹窗
private var mNoticeFloat: WarningFloat.Builder? = null
// 超视距、路侧、前车直播
@@ -65,9 +86,133 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
private var toolsView: AutoPilotAndCheckView? = null
private var autoPilotToolsFloat: WarningFloat.Builder? = null
// 检测、自动驾驶速度设置
private var toolsViewFloat: WarningFloat.Builder? = null
@Volatile
private var autoPilotBadCaseEntrance: View? = null
private var autoPilotBadCaseView: AutoPilotBadCaseView? = null
private var onBadCaseShow: (() -> View)? = null
private var onBadCaseHide: (() -> Unit)? = null
companion object {
private const val MSG_WHAT_DISMISS_BAD_CASE_ENTRY = 0x1010
private val CASE_EXPIRE_DURATION = TimeUnit.HOURS.toMillis(4)
}
@ExperimentalCoroutinesApi
private val channel by lazy {
Channel<AutoPilotRecordResult?>(UNLIMITED).also {
lifecycleScope.launchWhenResumed {
withContext(Dispatchers.Default) {
while (!it.isClosedForReceive) {
try {
val entrance = autoPilotBadCaseEntrance
val old = entrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
if (entrance == null || old == null || old.consumed) {
Log.d("QQQ", "-- step -- 1 --")
var oldT = try {
old?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time ?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
var record: AutoPilotRecordResult? = null
var newT = try {
it.receive()?.also { record = it }?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time
?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
if (oldT == 0L || (newT > 0L && (newT - oldT > 0L) && (newT - oldT) < CASE_EXPIRE_DURATION)) {
Log.d("QQQ", "-- step -- 2 --")
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Log.d("QQQ", "record: [$record] is displaying and consuming ~~~" )
showBadCaseEntrance(it)
}
continue
}
while (oldT != 0L && newT != 0L && (newT - oldT) >= CASE_EXPIRE_DURATION) {
Log.d("QQQ", "record: [$record] has been discarded, because it has been timeout." )
oldT = newT
newT = try {
it.receive()?.also {
record = it
}?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.time ?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
}
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Log.d("QQQ", "record: [$record] is displaying for rest ..." )
showBadCaseEntrance(it)
}
} else {
withContext(Dispatchers.Main) {
entrance.takeIf { it.visibility != View.VISIBLE }?.also {
(entrance.parent as? ViewGroup)?.let { g ->
TransitionManager.beginDelayedTransition(g, AutoTransition())
}
it.visibility = View.VISIBLE
}
}
Log.d("QQQ", "record: [$old] hasn't been consumed~~~~" )
}
} finally {
delay(1000)
}
}
}
}
}
}
private val handler by lazy {
Handler(Handler.Callback { it ->
if (it.what == MSG_WHAT_DISMISS_BAD_CASE_ENTRY) {
val entrance = autoPilotBadCaseEntrance
if (entrance != null && entrance.visibility == View.VISIBLE) {
val record = entrance.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
record?.consumed = true
record?.let { itx ->
lifecycleScope.launch(Dispatchers.IO) {
try {
val i = Repository.dao().deleteRecord(itx)
Log.d("QQQ", "delete result: $i")
} catch (t: Throwable) {
Log.d("QQQ", "---- delete error: ${t.message}")
}
}
}
dismissBadCaseFloatView()
if (entrance.visibility != View.GONE) {
(entrance.parent as? ViewGroup)?.let { g ->
TransitionManager.beginDelayedTransition(g, AutoTransition())
}
entrance.visibility = View.GONE
onBadCaseHide?.invoke()
}
}
return@Callback true
}
return@Callback false
})
}
override fun vipIdentification(visible: Boolean) {
ThreadUtils.runOnUiThread {
Logger.d(TAG, "vipIdentification")
@@ -99,50 +244,243 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
viewShowDebugView.setOnLongClickListener {
Log.d(TAG, "长按显示状态工具栏")
context?.let {
if (mDebugSettingViewFloat != null) {
WarningFloat.dismiss(mDebugSettingViewFloat!!.config.floatTag, false)
mDebugSettingViewFloat = null
mDebugSettingView = null
} else {
if (mDebugSettingView == null) {
mDebugSettingView = DebugSettingView(it)
}
mDebugSettingViewFloat = WarningFloat.with(it)
.setTag("DebugSettingView")
.setLayout(mDebugSettingView!!)
.setSidePattern(SidePattern.RIGHT)
.setGravity(Gravity.RIGHT, offsetY = 70)
.setImmersionStatusBar(true)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)
?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.show()
}
}
toggleDebugView()
true
}
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
CallerAutopilotIdentifyListenerManager.addListener(TAG, this)
lifecycleScope.launchWhenResumed {
withContext(Dispatchers.IO) {
val dao = Repository.dao()
try {
dao.getAllUnConsumedRecords()?.forEach {
channel.send(it)
}
} catch (t: Throwable) {
t.printStackTrace()
}
}
}
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun onAutopilotRecordResult(record: AutoPilotRecordResult?) {
record ?: return
Log.d("QQQ", "onAutopilotRecordResult:$record")
if (record.type == 1 && record.stat == 100) {
lifecycleScope.launchWhenResumed {
withContext(Dispatchers.IO) {
val dao = Repository.dao()
try {
dao.insertRecord(record)
} catch (t: Throwable) {
t.printStackTrace()
}
record.also {
channel.send(it)
}
}
}
}
if (record.type == 2 && (record.stat == 101 || record.stat == 100)) {
CallerMapDataCollectorManager.finish(record.id, record.stat, "", record.fileName ?: "", record.note ?: "")
}
}
override fun onDestroyView() {
super.onDestroyView()
CallerAutopilotIdentifyListenerManager.removeListener(TAG)
}
private fun showBadCaseEntrance(record: AutoPilotRecordResult) {
Log.d("QQQ", "showBadCaseEntrance:$record")
lifecycleScope.launch {
if (HmiBuildConfig.isShowBadCaseView) {
if (vsBadCaseToolsView?.parent != null) {
val inflateView = vsBadCaseToolsView.inflate()
autoPilotBadCaseEntrance = inflateView
}
}
val entrance = autoPilotBadCaseEntrance
Log.d("QQQ", "show --- 1 ----")
if (entrance != null) {
if (entrance.visibility != View.VISIBLE) {
(entrance.parent as? ViewGroup)?.let { g ->
TransitionManager.beginDelayedTransition(g, AutoTransition())
}
entrance.visibility = View.VISIBLE
}
entrance.setTag(R.id.autopilot_badcase_record, record)
entrance.onClick {
showBadCasesFloat {
if (it.visibility != View.GONE) {
(entrance.parent as? ViewGroup)?.let { g ->
TransitionManager.beginDelayedTransition(g, AutoTransition())
}
it.visibility = View.GONE
onBadCaseHide?.invoke()
}
}
}
dismissBadCaseEntryAfterSomeTime()
} else {
val view = onBadCaseShow?.invoke()
if (view != null) {
view.lifecycleOwner.lifecycle.addObserver(badCaseEntranceObserver)
view.visibility = View.GONE
autoPilotBadCaseEntrance = view
showBadCaseEntrance(record)
}
}
}
}
private val badCaseEntranceObserver = LifecycleEventObserver { _, event ->
if (event == ON_DESTROY) {
onBadCaseShow = null
onBadCaseHide = null
autoPilotBadCaseEntrance = null
}
}
override fun registerBadCaseCallback(onShow: () -> View, onHide: (() -> Unit)?) {
onBadCaseShow = onShow
onBadCaseHide = onHide
}
/**
* 工控机重启返回结果
* @param code
* @param msg
*/
override fun showDockerRebootResult(code: Int, msg: String) {
ThreadUtils.runOnUiThread{
if(code>=-1){
//重启成功
ToastUtils.showShort("重启成功")
}else{
//重启失败
msg?.let {
ToastUtils.showShort(it)
}
}
}
}
private fun showBadCasesFloat(dismiss: (() -> Unit)?) {
Log.d("QQQ", "showBadCaseToolsFloat")
context?.let { it ->
if (autoPilotToolsFloat == null) {
if (autoPilotBadCaseView == null) {
autoPilotBadCaseView = AutoPilotBadCaseView(it).also { itx ->
val record =
autoPilotBadCaseEntrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
itx.tag = record
itx.onDismiss {
dismissBadCaseFloatView()
}
itx.onSelect {
lifecycleScope.launch {
try {
val params = mutableMapOf<String, String>()
autoPilotBadCaseEntrance?.apply {
params["carLicense"] =
MoGoAiCloudClientConfig.getInstance().sn
params["filename"] = record?.fileName ?: ""
params["filesize"] = record?.total.toString()
params["key"] = record?.key ?: ""
params["reason"] = it.reason ?: ""
params["duration"] = record?.duration?.toInt()?.toString() ?: ""
params["timestamp"] = record?.timestamp ?: ""
}
val response = post(params)
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
Log.e("QQQ", "返回的body是空的~~~")
return@launch
}
if (body.code == 200) {
Logger.i(TAG, "ok:${body}")
dismissBadCaseFloatView()
dismiss?.invoke()
CallerAutoPilotManager.recordCause(
record?.key,
record?.fileName,
it.id, it.reason)
ToastUtils.showShort("接管反馈成功~")
record?.also {
it.consumed = true
withContext(Dispatchers.IO) {
try {
Repository.dao().deleteRecord(record)
} catch (t: Throwable) {
Log.d("QQQ", "---- delete error 2: ${t.message}")
}
}
}
return@launch
}
Log.e("QQQ", "fail:${body}")
}
} catch (t: Throwable) {
t.printStackTrace()
ToastUtils.showShort("网络请求失败,请尝试联网~")
Log.e("QQQ", "exception:${t.message}")
}
}
}
}
}
autoPilotToolsFloat = WarningFloat.with(it)
.setTag("BadCaseCollectFloat")
.setLayout(autoPilotBadCaseView!!)
.setSidePattern(SidePattern.LEFT)
.setGravity(Gravity.LEFT, offsetY = 72)
.setImmersionStatusBar(true)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)
?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onDismiss() {
autoPilotToolsFloat = null
autoPilotBadCaseView = null
}
})
.show()
} else {
autoPilotToolsFloat?.show()
}
}
}
private fun dismissBadCaseEntryAfterSomeTime() {
handler.removeMessages(MSG_WHAT_DISMISS_BAD_CASE_ENTRY)
handler.sendEmptyMessageDelayed(MSG_WHAT_DISMISS_BAD_CASE_ENTRY, CASE_EXPIRE_DURATION)
}
private fun showToolsFloat() {
Logger.d(TAG, "showToolsFloat")
context?.let {
@@ -159,6 +497,10 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
override fun onClose(v: View) {
dismissToolsFloatView()
}
override fun showDebugPanelView() {
toggleDebugView()
}
})
}
toolsViewFloat = WarningFloat.with(it)
@@ -167,6 +509,7 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
.setSidePattern(SidePattern.LEFT)
.setGravity(Gravity.LEFT, offsetY = 72)
.setImmersionStatusBar(true)
.setWindowHeight(WindowManager.LayoutParams.MATCH_PARENT)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
@@ -233,7 +576,6 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
} else {
setToolsViewVisibility(View.GONE)
}
}
override fun getLayoutId(): Int {
@@ -264,6 +606,52 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
ivToolsIcon?.visibility = visibility
}
/**
* 开关DebugView
*/
override fun toggleDebugView() {
Log.d(TAG, "长按显示状态工具栏")
context?.let {
if (mDebugSettingViewFloat != null) {
WarningFloat.dismiss(mDebugSettingViewFloat!!.config.floatTag, false)
mDebugSettingViewFloat = null
mDebugSettingView = null
} else {
if (mDebugSettingView == null) {
mDebugSettingView = DebugSettingView(it)
}
mDebugSettingViewFloat = WarningFloat.with(it)
.setTag("DebugSettingView")
.setLayout(mDebugSettingView!!)
.setSidePattern(SidePattern.RIGHT)
.setGravity(Gravity.RIGHT, offsetY = 70)
.setImmersionStatusBar(true)
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)
?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.show()
}
}
}
/**
* 展示VR下V2X预警
*
@@ -273,73 +661,75 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
* @param tag tag绑定弹窗的标志
*/
@Synchronized
override fun showWarningV2X(
v2xType: Int,
alertContent: String?,
ttsContent: String?,
tag: String?,
listenerIMoGo: IMoGoWarningStatusListener?
) {
activity?.let {
val notificationView = V2XNotificationView(it)
notificationView.setWarningIcon(EventTypeEnum.getWarningIcon(v2xType.toString()))
val warningContent = alertContent ?: EventTypeEnum.getWarningContent(v2xType.toString())
if (warningContent.isNullOrEmpty()) {
Logger.e(TAG, "Show warningContent is null or empty!")
return
} else {
notificationView.setWarningContent(warningContent)
}
if (mWarningFloat != null && mWarningFloat!!.config.floatTag != tag) {
WarningFloat.dismiss(mWarningFloat!!.config.floatTag, true)
}
mWarningFloat = WarningFloat.with(it)
.setTag(tag)
.setLayout(notificationView)
.setSidePattern(SidePattern.RESULT_TOP)
.setCountDownTime(5000)
.setGravity(Gravity.CENTER_HORIZONTAL, offsetY = 110)
.setImmersionStatusBar(true)
.addWarningStatusListener(listenerIMoGo)
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onShow() {
// 创建弹窗成功才进行TTS播报
Logger.d(
override fun showWarningV2X(v2xType: Int, alertContent: CharSequence?, ttsContent: String?, tag: String?, listenerIMoGo: IMoGoWarningStatusListener?, playTts: Boolean, expireTime: Long) {
lifecycleScope.launchWhenResumed {
activity?.let {
val floatWindow = mWarningFloat
val showTag = floatWindow?.config?.floatTag
if (floatWindow == null || TextUtils.isEmpty(showTag) || !floatWindow.isShow() || floatWindow.config.floatTag != tag) {
val notificationView = V2XNotificationView(it)
notificationView.setWarningIcon(EventTypeEnum.getWarningIcon(v2xType.toString()))
val warningContent = alertContent ?: EventTypeEnum.getWarningContent(v2xType.toString())
if (warningContent.isEmpty()) {
Logger.e(TAG, "Show warningContent is null or empty!")
return@launchWhenResumed
} else {
notificationView.setWarningContent(warningContent)
}
if (floatWindow != null && floatWindow.isShow()) {
WarningFloat.dismiss(floatWindow.config.floatTag, true)
}
mWarningFloat = WarningFloat.with(it)
.setTag(tag)
.setLayout(notificationView)
.setSidePattern(SidePattern.RESULT_TOP)
.setCountDownTime(expireTime)
.setGravity(Gravity.CENTER_HORIZONTAL, offsetY = 110)
.setImmersionStatusBar(true)
.isEnqueue(true)
.addWarningStatusListener(listenerIMoGo)
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onShow() {
// 创建弹窗成功才进行TTS播报
Logger.d(
"MoGoWarningFragment",
"mWarningFloat = $mWarningFloat---ttsContent = $ttsContent"
)
if (mWarningFloat != null && !TextUtils.isEmpty(ttsContent)) {
Logger.d("MoGoWarningFragment", "---> ttsContent = $ttsContent")
AIAssist.getInstance(activity)
)
if (mWarningFloat != null && !TextUtils.isEmpty(ttsContent) && playTts) {
Logger.d("MoGoWarningFragment", "---> ttsContent = $ttsContent")
AIAssist.getInstance(activity)
.speakTTSVoice(ttsContent)
}
}
}
})
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
})
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
override fun exitAnim(
view: View,
params: WindowManager.LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)?.setDuration(200)
})
.show()
})
.show()
} else {
val notification = floatWindow.config.layoutView as? V2XNotificationView
if (alertContent?.isNotEmpty() == true) {
notification?.setWarningContent(alertContent)
floatWindow.resetExpireTime(expireTime)
}
}
}
}
}
@@ -434,6 +824,10 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
flV2XWarningView?.showWarning(direction, closeTime)
}
override fun dismissWarning(direction: WarningDirectionEnum) {
flV2XWarningView?.dismissWarning(direction)
}
/**
* 展示云公告顶部弹窗
* @param trafficStylePushData
@@ -641,9 +1035,76 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
/**
* 显示转向灯效果 if (HmiBuildConfig.isShowBadCaseView) {
*/
override fun showTurnLight(light: Int) {
if (HmiBuildConfig.isShowTurnLightView) {
//Log.d("liyz", "showTurnLight light = $light")
turnLightView.setTurnLight(light)
}
}
/**
* 显示刹车效果
*/
override fun showBrakeLight(light: Int) {
if (HmiBuildConfig.isShowBrakeLightView) {
// Log.d("liyz", "showBrakeLight light = $light")
brakeView.setBrakeLight(light)
}
}
override fun setTurnLightFunction(isOpen: Boolean) {
HmiBuildConfig.isShowTurnLightView = isOpen
}
override fun setBrakeLightFunction(isOpen: Boolean) {
HmiBuildConfig.isShowBrakeLightView = isOpen
}
/**
* 展示工控机下载、升级状态信息
* @param upgradeMode 升级模式(提示升级、静默升级)
* @param downloadStatus 下载状态
* @param currentProgress 当前已经下载包体大小
* @param totalProgress 下载包体总大小
* @param downloadVersion 下载版本
* @param upgradeStatus 升级状态
*/
override fun showAdUpgradeStatus(
upgradeMode: Int,
downloadStatus: Int,
currentProgress: Int,
totalProgress: Int,
downloadVersion: String,
upgradeStatus: Int
) {
ThreadUtils.runOnUiThread{
//如果工控机处于“下载中”、“可升级(下载完成)”、“升级中”、“升级失败”状态时,工具箱入口显示红色角标
if(AdUpgradeStateHelper.showUpgradeTips(downloadStatus, upgradeStatus)){
viewUpgradeTips.visibility = View.VISIBLE
}else{
viewUpgradeTips.visibility = View.GONE
}
//将状态同步到工具箱
toolsView?.showAdUpgradeStatus(upgradeMode,downloadStatus, currentProgress, totalProgress, downloadVersion, upgradeStatus)
}
}
private fun dismissBadCaseFloatView() {
autoPilotToolsFloat?.let {
WarningFloat.dismiss(it.config.floatTag, false)
autoPilotToolsFloat = null
autoPilotBadCaseView = null
}
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy")
handler.removeCallbacksAndMessages(null)
}

View File

@@ -23,8 +23,8 @@ import com.mogo.cloud.trafficlive.api.MoGoAiCloudTrafficLive
import com.mogo.eagle.core.data.camera.CameraEntity
import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.widget.media.video.SimpleVideoPlayer
import com.mogo.utils.logger.Logger
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.model.VideoOptionModel

View File

@@ -0,0 +1,496 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.graphics.PixelFormat
import android.util.Log
import android.view.*
import android.widget.FrameLayout
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.core.view.GravityCompat
import com.mogo.commons.context.ContextHolderUtil
import com.mogo.eagle.core.utilcode.util.ScreenUtils
import com.mogo.eagle.core.utilcode.util.Utils
abstract class AbsLogView : ILogView, TouchProxy.OnTouchEventListener {
class ViewArgs {
var edgePinned = false
}
private val mInnerReceiver = InnerReceiver()
@JvmField
protected var mWindowManager =
ContextHolderUtil.getContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager
/**
* 创建FrameLayout#LayoutParams 系统悬浮窗调用
*/
protected var systemLayoutParams: WindowManager.LayoutParams? = null
/**
* 整个悬浮窗的View
*/
private var mRootView: FrameLayout? = null
/**
* rootView的直接子View 一般是用户的xml布局 被添加到mRootView中
*/
private var mChildView: View? = null
val logView: View?
get() = mRootView
/**
* 手势代理
*/
@JvmField
var mTouchProxy = TouchProxy(this)
private val viewProps = ViewArgs()
/**
* 控件在布局边界发生大小变化被裁剪的原因
*/
val parentView: LogFrameLayout?
get() = if (mRootView != null) {
mRootView!!.parent as LogFrameLayout
} else null
fun show(context: Context) {
if (isShow) {
Log.d("EmArrow", "isShow : $isShow")
return
}
performCreate(context)
createView()
onResume()
}
fun dismiss() {
removeView()
performDestroy()
}
/**
* 执行floatPage create
*
* @param context 上下文环境
*/
@SuppressLint("ClickableViewAccessibility")
private fun performCreate(context: Context) {
try {
//调用onCreate方法
onCreate(context)
//系统悬浮窗的返回按键监听
mRootView = object : LogFrameLayout(context, LogFrameLayoutFlag_CHILD) {
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_UP && shouldDealBackKey()) {
//监听返回键
if (event.keyCode == KeyEvent.KEYCODE_BACK || event.keyCode == KeyEvent.KEYCODE_HOME) {
return onBackPressed()
}
}
return super.dispatchKeyEvent(event)
}
}
//添加根布局的layout回调
addViewTreeObserverListener()
//调用onCreateView抽象方法
mChildView = onCreateView(context, mRootView)
//将子View添加到rootView中
mRootView?.addView(mChildView)
//设置根布局的手势拦截
mRootView?.setOnTouchListener { v, event -> mTouchProxy.onTouchEvent(v, event) }
//调用onViewCreated回调
onViewCreated(mRootView)
mLogViewLayoutParams = LogViewLayoutParams()
//分别创建对应的LayoutParams
systemLayoutParams = WindowManager.LayoutParams()
//shouldDealBackKey : false 不自己收返回事件处理
if (shouldDealBackKey()) {
//自己处理返回按键
systemLayoutParams?.flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
mLogViewLayoutParams.flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or LogViewLayoutParams.FLAG_LAYOUT_NO_LIMITS
} else {
//设置WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE会导致RootView监听不到返回按键的监听失效 系统处理返回按键
systemLayoutParams?.flags =
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
mLogViewLayoutParams.flags =
LogViewLayoutParams.FLAG_NOT_FOCUSABLE or LogViewLayoutParams.FLAG_LAYOUT_NO_LIMITS
}
systemLayoutParams?.apply {
format = PixelFormat.TRANSPARENT
gravity = GravityCompat.START or Gravity.TOP
}
mLogViewLayoutParams.gravity = GravityCompat.START or Gravity.TOP
//动态注册关闭系统弹窗的广播
val intentFilter = IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
context.registerReceiver(mInnerReceiver, intentFilter)
initViewLayoutParams(mLogViewLayoutParams)
onSystemLayoutParamsCreated()
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun createView() {
mWindowManager.addView(logView, systemLayoutParams)
}
override fun onResume() {
mRootView?.requestLayout()
}
private fun removeView() {
mWindowManager.removeView(logView)
}
private fun performDestroy() {
context?.unregisterReceiver(mInnerReceiver)
//移除布局监听
removeViewTreeObserverListener()
mRootView = null
onDestroy()
}
/**
* 用来保存rootView的LayoutParams
*/
private lateinit var mLogViewLayoutParams: LogViewLayoutParams
/**
* 上一次LogView的位置信息
*/
private val mLastLogViewPosInfo: LastLogViewPosInfo = LastLogViewPosInfo()
/**
* 根布局的实际宽
*/
private var mLogViewWidth = 0
/**
* 根布局的实际高
*/
private var mLogViewHeight = 0
private var mViewTreeObserver: ViewTreeObserver? = null
private val mOnGlobalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener =
ViewTreeObserver.OnGlobalLayoutListener {
//每次布局发生变动的时候重新赋值
mRootView?.let {
mLogViewWidth = it.measuredWidth
mLogViewHeight = it.measuredHeight
mLastLogViewPosInfo.logViewWidth = mLogViewWidth
mLastLogViewPosInfo.logViewHeight = mLogViewHeight
}
}
private fun addViewTreeObserverListener() {
if (mViewTreeObserver == null && mRootView != null) {
mViewTreeObserver = mRootView!!.viewTreeObserver
mViewTreeObserver?.addOnGlobalLayoutListener(mOnGlobalLayoutListener)
}
}
private fun removeViewTreeObserverListener() {
mViewTreeObserver?.let {
if (it.isAlive) {
it.removeOnGlobalLayoutListener(mOnGlobalLayoutListener)
}
}
}
override fun onDestroy() {
}
/**
* 确定系统浮标的初始位置
* LayoutParams创建完以后调用
* 调用时建议放在实现下方
*/
private fun onSystemLayoutParamsCreated() {
//如果有上一个页面的位置记录 这更新位置
systemLayoutParams?.flags = mLogViewLayoutParams.flags
systemLayoutParams?.gravity = mLogViewLayoutParams.gravity
systemLayoutParams?.width = mLogViewLayoutParams.width
systemLayoutParams?.height = mLogViewLayoutParams.height
systemLayoutParams?.x = mLogViewLayoutParams.x
systemLayoutParams?.y = mLogViewLayoutParams.y
}
/**
* 默认实现为true
*
* @return
*/
override fun canDrag(): Boolean {
return true
}
/**
* 搭配shouldDealBackKey使用 自定义处理完以后需要返回true
* 默认模式的onBackPressed 拦截在NormViewManager#getRootContentView中被处理
* 系统模式下的onBackPressed 在当前类的performCreate 初始化View时被处理
* 返回false 表示交由系统处理
* 返回 true 表示当前的返回事件已由自己处理 并拦截了改返回事件
*/
override fun onBackPressed(): Boolean {
return false
}
/**
* 默认不自己处理返回按键
*
* @return
*/
override fun shouldDealBackKey(): Boolean {
return false
}
override fun onEnterBackground() {
mRootView?.let {
it.visibility = View.GONE
}
}
override fun onEnterForeground() {
mRootView?.let {
it.visibility = View.VISIBLE
}
}
override fun onMove(x: Int, y: Int, dx: Int, dy: Int) {
if (!canDrag()) {
return
}
systemLayoutParams?.apply {
this.x += dx
this.y += dy
}
//限制布局边界
resetBorderline(systemLayoutParams)
mWindowManager.updateViewLayout(mRootView, systemLayoutParams)
}
/**
* 限制边界 调用的时候必须保证是在控件能获取到宽高德前提下
*/
private fun resetBorderline(
windowLayoutParams: WindowManager.LayoutParams?
) {
//如果是系统模式或者手动关闭动态限制边界
if (!restrictBorderline()) {
return
}
if (windowLayoutParams != null) {
// 均是横向计算
if (windowLayoutParams.y >= screenShortSideLength - mLogViewHeight) {
windowLayoutParams.y = screenShortSideLength - mLogViewHeight
}
// 均是横向计算
if (windowLayoutParams.x >= screenLongSideLength - mLogViewWidth) {
windowLayoutParams.x = screenLongSideLength - mLogViewWidth
}
//系统模式
if (windowLayoutParams.y <= 0) {
windowLayoutParams.y = 0
}
if (windowLayoutParams.x <= 0) {
windowLayoutParams.x = 0
}
}
}
/**
* 手指弹起时保存当前浮标位置
*
* @param x
* @param y
*/
override fun onUp(x: Int, y: Int) {
if (!canDrag()) {
return
}
if (!viewProps.edgePinned) {
endMoveAndRecord()
return
}
animatedMoveToEdge()
}
private fun endMoveAndRecord() {
systemLayoutParams?.let {
mLastLogViewPosInfo.logViewWidth = it.x
mLastLogViewPosInfo.logViewHeight = it.y
}
}
private fun animatedMoveToEdge() {
val viewSize = mRootView?.width ?: return
systemLayoutParams?.also { layoutAttrs ->
makeAnimator(layoutAttrs.x, viewSize, ViewGroup.LayoutParams.MATCH_PARENT) {
addUpdateListener { v ->
layoutAttrs.x = v.animatedValue as Int
mWindowManager.updateViewLayout(mRootView, layoutAttrs)
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
endMoveAndRecord()
}
})
}
}
}
private inline fun makeAnimator(
from: Int,
size: Int,
containerSize: Int,
setup: ValueAnimator.() -> Unit
) {
if (size <= 0 || containerSize <= 0) return
ValueAnimator.ofInt(
from,
if (from <= (containerSize - size) / 2) 0 else (containerSize - size)
)
.apply {
duration = 150L
setup()
}
.start()
}
/**
* 手指按下时的操作
*
* @param x
* @param y
*/
override fun onDown(x: Int, y: Int) {
if (!canDrag()) {
return
}
}
/**
* home键被点击 只有系统悬浮窗控件才会被调用
*/
open fun onHomeKeyPress() {}
/**
* 菜单键被点击 只有系统悬浮窗控件才会被调用
*/
open fun onRecentAppKeyPress() {}
override fun onPause() {}
/**
* 系统悬浮窗需要调用
*
* @return
*/
val context: Context?
get() = if (mRootView != null) {
mRootView!!.context
} else {
null
}
val resources: Resources?
get() = if (context == null) {
null
} else context!!.resources
fun getString(@StringRes resId: Int): String? {
return if (context == null) {
null
} else context!!.getString(resId)
}
private val isShow: Boolean
get() = mRootView?.isShown ?: false
protected fun <T : View> findViewById(@IdRes id: Int): T? {
if (mRootView == null) {
return null
}
return mRootView?.findViewById(id)
}
/**
* 是否限制布局边界
*
* @return
*/
open fun restrictBorderline(): Boolean {
return true
}
/**
* 获取屏幕短边的长度(横屏) 不包含statusBar
*
* @return
*/
private val screenShortSideLength: Int
get() = ScreenUtils.getAppScreenHeight()
/**
* 获取屏幕长边的长度(横屏) 不包含statusBar
*
* @return
*/
private val screenLongSideLength: Int
get() = ScreenUtils.getAppScreenWidth()
/**
* 强制刷新当前view
*/
open fun immInvalidate() {
mRootView?.requestLayout()
}
/**
* 广播接收器 系统悬浮窗需要调用
*/
private inner class InnerReceiver : BroadcastReceiver() {
private val SYSTEM_DIALOG_REASON_KEY = "reason"
private val SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"
private val SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS == action) {
val reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY)
if (reason != null) {
if (reason == SYSTEM_DIALOG_REASON_HOME_KEY) {
//点击home键
onHomeKeyPress()
} else if (reason == SYSTEM_DIALOG_REASON_RECENT_APPS) {
//点击menu按钮
onRecentAppKeyPress()
}
}
}
}
}
}

View File

@@ -0,0 +1,148 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 内置一个List的通用、简化的适用于RecyclerView的Adapter。
*/
public abstract class AbsRecyclerAdapter<T extends AbsViewBinder, V> extends RecyclerView.Adapter<T> {
protected List<V> mList;
private LayoutInflater mInflater;
protected Context mContext;
public AbsRecyclerAdapter(Context context) {
if (context == null) {
return;
}
mContext = context;
mList = new ArrayList<>();
mInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public final T onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = createView(mInflater, parent, viewType);
return createViewHolder(view, viewType);
}
protected abstract T createViewHolder(View view, int viewType);
/**
* 如果是通过LayoutInflater创建的View,不要绑定到父View,RecyclerView会负责添加。
*/
protected abstract View createView(LayoutInflater inflater, ViewGroup parent, int viewType);
@Override
public final void onBindViewHolder(T holder, int position) {
V data = mList.get(position);
holder.setData(data);
holder.bind(data, position);
}
@Override
public int getItemCount() {
return mList.size();
}
/**
* 列表末尾追加一个元素
*/
@SuppressLint("NotifyDataSetChanged")
public void append(V item) {
if (item == null) {
return;
}
mList.add(item);
notifyDataSetChanged();
}
/**
* 在特定位置增加一个元素
*/
public void append(V item, int position) {
if (item == null) {
return;
}
if (position < 0) {
position = 0;
} else if (position > mList.size()) {
position = mList.size();
}
mList.add(position, item);
notifyItemChanged(position, item);
}
/**
* 追加一个集合
*/
@SuppressLint("NotifyDataSetChanged")
public final void append(Collection<V> items) {
if (items == null || items.size() == 0) {
return;
}
mList.addAll(items);
notifyDataSetChanged();
}
/**
* 清空集合
*/
@SuppressLint("NotifyDataSetChanged")
public final void clear() {
if (mList.isEmpty()) {
return;
}
mList.clear();
notifyDataSetChanged();
}
/**
* 删除一个元素
*/
@SuppressLint("NotifyDataSetChanged")
public final void remove(V item) {
if (item == null) {
return;
}
if (mList.contains(item)) {
mList.remove(item);
notifyDataSetChanged();
}
}
/**
* 删除一个元素
*/
public final void remove(int index) {
if (index < mList.size()) {
mList.remove(index);
notifyItemRemoved(index);
}
}
/**
* 删除一个集合
*/
@SuppressLint("NotifyDataSetChanged")
public final void remove(Collection<V> items) {
if (items == null || items.size() == 0) {
return;
}
if (mList.removeAll(items)) {
notifyDataSetChanged();
}
}
}

View File

@@ -0,0 +1,55 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.content.Context;
import android.view.View;
import androidx.annotation.IdRes;
import androidx.recyclerview.widget.RecyclerView;
/**
* 简单封装的适用于RecyclerView的ViewHolder
*/
public abstract class AbsViewBinder<T> extends RecyclerView.ViewHolder {
private T data;
private View mView;
public AbsViewBinder(final View view) {
super(view);
mView = view;
getViews();
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onViewClick(view, data);
}
});
}
protected final View getView() {
return mView;
}
protected abstract void getViews();
public final <V extends View> V getView(@IdRes int id) {
return (V) mView.findViewById(id);
}
public abstract void bind(T t);
public void bind(T t, int position) {
bind(t);
}
protected void onViewClick(View view, T data) {
}
protected final void setData(T data) {
this.data = data;
}
protected final Context getContext() {
return mView.getContext();
}
}

View File

@@ -0,0 +1,82 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import android.content.Context
import android.view.View
import android.widget.FrameLayout
interface ILogView {
/**
* view 创建时调用 做一些变量的初始化 当还不能进行View的操作
*
* @param context
*/
fun onCreate(context: Context?)
/**
* 传入rootView 用于创建控件
*
* @param context
* @param rootView
* @return 返回创建的childView
*/
fun onCreateView(context: Context?, rootView: FrameLayout?): View?
/**
* 将xml中的控件添加到rootView以后调用在当前方法中可以进行view的一些操作
*
* @param rootView
*/
fun onViewCreated(rootView: FrameLayout?)
/**
* 当前的View添加到根布局里时调用
*/
fun onResume()
/**
* 当前activity onPause时调用
*/
fun onPause()
/**
* 确定系统悬浮窗浮标的初始位置
* LayoutParams创建完以后调用
*
* @param params
*/
fun initViewLayoutParams(params: LogViewLayoutParams?)
/**
* app进入后台时调用 内置View 不需要实现
*/
fun onEnterBackground()
/**
* app回到前台时调用 内置view 不需要实现
*/
fun onEnterForeground()
/**
* 浮标控件是否可以拖动
*
* @return
*/
fun canDrag(): Boolean
/**
* 是否需要自己处理返回键
*
* @return
*/
fun shouldDealBackKey(): Boolean
/**
* shouldDealBackKey == true 时调用
*/
fun onBackPressed(): Boolean
/**
* 悬浮窗主动销毁时调用 不能在当前生命周期回调函数中调用 detach自己 否则会出现死循环
*/
fun onDestroy()
}

View File

@@ -0,0 +1,6 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
interface ILogViewListener {
fun onAttach()
fun onDetach()
}

View File

@@ -0,0 +1,30 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import com.mogo.eagle.core.utilcode.util.ScreenUtils
/**
* 保存上一次LogView的位置信息
*/
class LastLogViewPosInfo {
var logViewWidth = 0
var logViewHeight = 0
var leftMarginPercent = 0f
private set
var topMarginPercent = 0f
private set
fun setLeftMargin(leftMargin: Int) {
leftMarginPercent = leftMargin.toFloat() / ScreenUtils.getAppScreenWidth().toFloat()
}
fun setTopMargin(topMargin: Int) {
topMarginPercent = topMargin.toFloat() / ScreenUtils.getAppScreenHeight().toFloat()
}
override fun toString(): String {
return "LastLogViewPosInfo{" +
", leftMarginPercent=" + leftMarginPercent +
", topMarginPercent=" + topMarginPercent +
'}'
}
}

View File

@@ -0,0 +1,26 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
open class LogFrameLayout : FrameLayout {
private var mFlag = LogFrameLayoutFlag_ROOT
constructor(context: Context, flag: Int) : super(context) {
mFlag = flag
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
}
companion object {
const val LogFrameLayoutFlag_ROOT = 100
const val LogFrameLayoutFlag_CHILD = 200
}
}

View File

@@ -0,0 +1,287 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import android.annotation.SuppressLint
import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mogo.eagle.core.function.hmi.R
import java.util.*
class LogInfoView : AbsLogView() {
companion object {
const val MAX_LOG_LINE_NUM = 10000
const val UPDATE_CHECK_INTERVAL = 200
}
private var logLines = LinkedList<LogLine>()
private var counter = 0
private var mAutoScrollToBottom = true
private var mIsLoaded = false
private var mLogRv: RecyclerView? = null
private var mLogItemAdapter: LogItemAdapter? = null
private var mLogFilter: EditText? = null
private var mRadioGroup: RadioGroup? = null
private var mLinearLayout: LinearLayout? = null
/**
* 单行的log
*/
private var mLogHint: TextView? = null
private var mLogRvWrap: RelativeLayout? = null
private var logViewListener: ILogViewListener? = null
override fun onCreate(context: Context?) {}
override fun onCreateView(context: Context?, rootView: FrameLayout?): View? {
return LayoutInflater.from(context).inflate(R.layout.log_view, rootView, false)
}
override fun onViewCreated(rootView: FrameLayout?) {
initView()
}
private fun initView() {
mLogHint = findViewById(R.id.log_hint)
mLogRvWrap = findViewById(R.id.log_page)
mLogRv = findViewById(R.id.log_list)
mLogRv!!.layoutManager = LinearLayoutManager(context)
mLogItemAdapter = LogItemAdapter(context!!)
mLogRv!!.adapter = mLogItemAdapter
mLogFilter = findViewById(R.id.log_filter)
mLogFilter!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable) {
mLogItemAdapter!!.filter.filter(s)
}
})
val mTitleBar = findViewById<LogTitleBar>(R.id.title_bar)
mTitleBar!!.setListener(object : LogTitleBar.OnTitleBarClickListener {
override fun onRightClick() {
if (logViewListener != null) {
logViewListener!!.onDetach()
}
}
override fun onLeftClick() {
minimize()
}
})
mLogHint!!.setOnClickListener { v: View? -> maximize() }
mRadioGroup = findViewById(R.id.radio_group)
mRadioGroup!!.setOnCheckedChangeListener { group: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.verbose -> {
mLogItemAdapter!!.logLevelLimit = Log.VERBOSE
}
R.id.debug -> {
mLogItemAdapter!!.logLevelLimit = Log.DEBUG
}
R.id.info -> {
mLogItemAdapter!!.logLevelLimit = Log.INFO
}
R.id.warn -> {
mLogItemAdapter!!.logLevelLimit = Log.WARN
}
R.id.error -> {
mLogItemAdapter!!.logLevelLimit = Log.ERROR
}
}
mLogItemAdapter!!.filter.filter(mLogFilter!!.text)
}
mLogRv!!.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager?
// if the bottom of the list isn't visible anymore, then stop autoscrolling
mAutoScrollToBottom =
layoutManager!!.findLastCompletelyVisibleItemPosition() == recyclerView.adapter!!
.itemCount - 1
}
})
mRadioGroup!!.check(R.id.verbose)
val mBtnTop = findViewById<Button>(R.id.btn_top)
val mBtnBottom = findViewById<Button>(R.id.btn_bottom)
val mBtnClean = findViewById<Button>(R.id.btn_clean)
val mBtnExport = findViewById<Button>(R.id.btn_export)
mBtnTop!!.setOnClickListener {
if (mLogItemAdapter == null || mLogItemAdapter!!.itemCount == 0) {
return@setOnClickListener
}
mLogRv!!.scrollToPosition(0)
}
mBtnBottom!!.setOnClickListener {
if (mLogItemAdapter == null || mLogItemAdapter!!.itemCount == 0) {
return@setOnClickListener
}
mLogRv!!.scrollToPosition(mLogItemAdapter!!.itemCount - 1)
}
mBtnExport!!.setOnClickListener { }
mBtnClean!!.setOnClickListener {
if (mLogItemAdapter == null || mLogItemAdapter!!.itemCount == 0) {
return@setOnClickListener
}
counter = 0
mLogItemAdapter!!.clearLog()
}
}
override fun onResume() {
super.onResume()
if (logViewListener != null) {
logViewListener!!.onAttach()
}
}
override fun onEnterForeground() {
super.onEnterForeground()
parentView?.visibility = View.VISIBLE
}
override fun onEnterBackground() {
super.onEnterBackground()
parentView?.visibility = View.GONE
}
fun registerLogViewListener(listener: ILogViewListener?) {
logViewListener = listener
}
fun unRegisterLogViewListener() {
logViewListener = null
}
override fun initViewLayoutParams(params: LogViewLayoutParams?) {
params!!.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
params.width = LogViewLayoutParams.MATCH_PARENT
params.height = LogViewLayoutParams.MATCH_PARENT
}
@SuppressLint("NotifyDataSetChanged")
fun onLogCatch(lineLog: String) {
if (mLogRv == null || mLogItemAdapter == null) {
return
}
if (!mIsLoaded) {
mIsLoaded = true
mLinearLayout = findViewById(R.id.ll_loading)
mLinearLayout!!.visibility = View.GONE
mLogRv!!.visibility = View.VISIBLE
}
val logLine = LogLine.newLogLine(lineLog, false)
if (logLines.size > MAX_LOG_LINE_NUM) {
logLines.removeFirst()
}
logLines.add(logLine)
if (logLines.size == 1) {
mLogItemAdapter!!.addWithFilter(logLines[0], mLogFilter?.text, true)
} else {
mLogItemAdapter!!.addWithFilter(logLine, mLogFilter?.text, false)
mLogItemAdapter!!.notifyDataSetChanged()
}
if (logLines.size > 0) {
val line = logLines[logLines.size - 1]
"${line.tag} : ${line.logOutput}".also { mLogHint?.text = it }
}
if (++counter % UPDATE_CHECK_INTERVAL == 0
&& mLogItemAdapter!!.trueValues.size > MAX_LOG_LINE_NUM
) {
val numItemsToRemove = mLogItemAdapter!!.trueValues.size - MAX_LOG_LINE_NUM
mLogItemAdapter!!.removeFirst(numItemsToRemove)
}
if (mAutoScrollToBottom) {
scrollToBottom()
}
}
private fun scrollToBottom() {
mLogRv!!.scrollToPosition(mLogItemAdapter!!.itemCount - 1)
}
private val selectLogLevel: Int
get() {
return when (mRadioGroup!!.checkedRadioButtonId) {
R.id.verbose -> {
Log.VERBOSE
}
R.id.debug -> {
Log.DEBUG
}
R.id.info -> {
Log.INFO
}
R.id.warn -> {
Log.WARN
}
R.id.error -> {
Log.ERROR
}
else -> {
Log.VERBOSE
}
}
}
/**
* 最小化
*/
fun minimize() {
isMaximize = false
mLogHint!!.visibility = View.VISIBLE
mLogRvWrap!!.visibility = View.GONE
val layoutParams = systemLayoutParams ?: return
Log.d("EmArrow", "minimize , layoutParams is not null")
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
layoutParams.gravity = Gravity.START or Gravity.TOP
mWindowManager.updateViewLayout(logView, layoutParams)
}
/**
* 是否最大化
*/
private var isMaximize = true
private fun maximize() {
isMaximize = true
mLogHint!!.visibility = View.GONE
mLogRvWrap!!.visibility = View.VISIBLE
val layoutParams = systemLayoutParams ?: return
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT
layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT
layoutParams.gravity = Gravity.START or Gravity.TOP
mWindowManager.updateViewLayout(logView, layoutParams)
}
override fun onBackPressed(): Boolean {
return if (isMaximize) {
minimize()
true
} else {
false
}
}
override fun shouldDealBackKey(): Boolean {
return true
}
override fun canDrag(): Boolean {
return false
}
}

View File

@@ -0,0 +1,260 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static android.content.Context.CLIPBOARD_SERVICE;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.function.hmi.ui.utils.SearchCriteria;
import com.mogo.eagle.core.function.hmi.ui.utils.TagColorUtil;
public class LogItemAdapter extends AbsRecyclerAdapter<AbsViewBinder<LogLine>, LogLine> implements Filterable {
public LogItemAdapter(Context context) {
super(context);
mClipboard = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
}
private ArrayList<LogLine> mOriginalValues = new ArrayList<>();
private final ArrayFilter mFilter = new ArrayFilter();
private int logLevelLimit = Log.VERBOSE;
private final ClipboardManager mClipboard;
/**
* 清空log
*/
@SuppressLint("NotifyDataSetChanged")
public void clearLog() {
if (mOriginalValues != null && mOriginalValues.size() > 0) {
mOriginalValues.clear();
}
clear();
notifyDataSetChanged();
}
@Override
protected AbsViewBinder<LogLine> createViewHolder(View view, int viewType) {
return new LogInfoViewHolder(view);
}
@Override
protected View createView(LayoutInflater inflater, ViewGroup parent, int viewType) {
return inflater.inflate(R.layout.item_log, parent, false);
}
@Override
public Filter getFilter() {
return mFilter;
}
public int getLogLevelLimit() {
return logLevelLimit;
}
public void setLogLevelLimit(int logLevelLimit) {
this.logLevelLimit = logLevelLimit;
}
public List<LogLine> getTrueValues() {
return mOriginalValues != null ? mOriginalValues : mList;
}
@SuppressLint("NotifyDataSetChanged")
public void removeFirst(int n) {
if (mOriginalValues != null) {
List<LogLine> subList = mOriginalValues.subList(n, mOriginalValues.size());
for (int i = 0; i < n; i++) {
// value to delete - delete it from the mObjects as well
mList.remove(mOriginalValues.get(i));
}
mOriginalValues = new ArrayList<>(subList);
}
notifyDataSetChanged();
}
public class LogInfoViewHolder extends AbsViewBinder<LogLine> {
private TextView mLogText;
private TextView mPid;
private TextView mTime;
private TextView mTag;
private TextView mLevel;
public LogInfoViewHolder(View view) {
super(view);
}
@Override
protected void getViews() {
mLogText = getView(R.id.log_output_text);
mLevel = getView(R.id.log_level_text);
mPid = getView(R.id.pid_text);
mTime = getView(R.id.timestamp_text);
mTag = getView(R.id.tag_text);
}
@Override
protected void onViewClick(View view, final LogLine data) {
super.onViewClick(view, data);
data.setExpanded(!data.isExpanded());
if (data.isExpanded() && data.getProcessId() != -1) {
mLogText.setSingleLine(false);
mTime.setVisibility(View.VISIBLE);
mPid.setVisibility(View.VISIBLE);
view.setBackgroundColor(Color.BLACK);
mLogText.setTextColor(TagColorUtil.getTextColor(getContext(), data.getLogLevel(), true));
mTag.setTextColor(TagColorUtil.getTextColor(getContext(), data.getLogLevel(), true));
} else {
mLogText.setSingleLine(true);
mTime.setVisibility(View.GONE);
mPid.setVisibility(View.GONE);
view.setBackgroundColor(Color.WHITE);
mLogText.setTextColor(TagColorUtil.getTextColor(getContext(), data.getLogLevel(), false));
mTag.setTextColor(TagColorUtil.getTextColor(getContext(), data.getLogLevel(), false));
}
itemView.setOnLongClickListener(v -> {
ClipData clipData = ClipData.newPlainText("Label", data.getOriginalLine());
mClipboard.setPrimaryClip(clipData);
return true;
});
}
@Override
public void bind(LogLine item) {
mLevel.setText(item.getLogLevelText());
mLevel.setTextColor(TagColorUtil.getLevelColor(getContext(), item.getLogLevel()));
mLevel.setBackgroundColor(TagColorUtil.getLevelBgColor(getContext(), item.getLogLevel()));
mPid.setText(String.valueOf(item.getProcessId()));
mTime.setText(item.getTimestamp());
mLogText.setText(item.getLogOutput());
mTag.setText(item.getTag());
if (item.isExpanded() && item.getProcessId() != -1) {
mLogText.setSingleLine(false);
mTime.setVisibility(View.VISIBLE);
mPid.setVisibility(View.VISIBLE);
mLogText.setTextColor(TagColorUtil.getTextColor(getContext(), item.getLogLevel(), true));
mTag.setTextColor(TagColorUtil.getTextColor(getContext(), item.getLogLevel(), true));
itemView.setBackgroundColor(Color.BLACK);
} else {
mLogText.setSingleLine(true);
mTime.setVisibility(View.GONE);
mPid.setVisibility(View.GONE);
itemView.setBackgroundColor(Color.WHITE);
mLogText.setTextColor(TagColorUtil.getTextColor(getContext(), item.getLogLevel(), false));
mTag.setTextColor(TagColorUtil.getTextColor(getContext(), item.getLogLevel(), false));
}
}
}
/**
* 添加日志到adapter中
*/
public void addWithFilter(LogLine logObj, CharSequence text, boolean notify) {
if (mOriginalValues != null) {
List<LogLine> inputList = Collections.singletonList(logObj);
List<LogLine> filteredObjects = mFilter.performFilteringOnList(inputList, text);
mOriginalValues.add(logObj);
mList.addAll(filteredObjects);
if (notify) {
notifyItemRangeInserted(mList.size() - filteredObjects.size(), filteredObjects.size());
}
} else {
mList.add(logObj);
if (notify) {
notifyItemInserted(mList.size());
}
}
}
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
ArrayList<LogLine> allValues = performFilteringOnList(mOriginalValues, prefix);
results.values = allValues;
results.count = allValues.size();
return results;
}
public ArrayList<LogLine> performFilteringOnList(List<LogLine> inputList, CharSequence query) {
SearchCriteria searchCriteria = new SearchCriteria(query);
// search by log level
ArrayList<LogLine> allValues = new ArrayList<>();
ArrayList<LogLine> logLines = new ArrayList<>(inputList);
for (LogLine logLine : logLines) {
if (logLine != null && logLine.getLogLevel() >= logLevelLimit) {
allValues.add(logLine);
}
}
ArrayList<LogLine> finalValues = allValues;
// search by criteria
if (!searchCriteria.isEmpty()) {
final int count = allValues.size();
final ArrayList<LogLine> newValues = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
final LogLine value = allValues.get(i);
// search the logLine based on the criteria
if (searchCriteria.matches(value)) {
newValues.add(value);
}
}
finalValues = newValues;
}
return finalValues;
}
@SuppressLint("NotifyDataSetChanged")
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mList = (List<LogLine>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetChanged();
}
}
}
}

View File

@@ -0,0 +1,202 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.text.TextUtils;
import android.util.Log;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LogLine {
private static final int TIMESTAMP_LENGTH = 19;
private static final Pattern logPattern = Pattern.compile(
// log level
"(\\w)" +
"/" +
// tag
"([^(]+)" +
"\\(\\s*" +
// pid
"(\\d+)" +
// optional weird number that only occurs on ZTE blade
"(?:\\*\\s*\\d+)?" +
"\\): ");
private static final String filterPattern = "ResourceType|memtrack|android.os.Debug|BufferItemConsumer|DPM.*|MDM.*|ChimeraUtils|BatteryExternalStats.*|chatty.*|DisplayPowerController|WidgetHelper|WearableService|DigitalWidget.*|^ANDR-PERF-.*";
private int logLevel;
private String tag;
private String logOutput;
private int processId = -1;
private String timestamp;
private boolean expanded = false;
private boolean highlighted = false;
public static LogLine newLogLine(String originalLine, boolean expanded) {
LogLine logLine = new LogLine();
logLine.setExpanded(expanded);
int startIdx = 0;
// if the first char is a digit, then this starts out with a timestamp
// otherwise, it's a legacy log or the beginning of the log output or something
if (!TextUtils.isEmpty(originalLine)
&& Character.isDigit(originalLine.charAt(0))
&& originalLine.length() >= TIMESTAMP_LENGTH) {
String timestamp = originalLine.substring(0, TIMESTAMP_LENGTH - 1);
logLine.setTimestamp(timestamp);
// cut off timestamp
startIdx = TIMESTAMP_LENGTH;
}
Matcher matcher = logPattern.matcher(originalLine);
if (matcher.find(startIdx)) {
char logLevelChar = matcher.group(1).charAt(0);
String logText = originalLine.substring(matcher.end());
if (logText.matches("^maxLineHeight.*|Failed to read.*")) {
logLine.setLogLevel(convertCharToLogLevel('V'));
} else {
logLine.setLogLevel(convertCharToLogLevel(logLevelChar));
}
String tagText = matcher.group(2);
if (tagText.matches(filterPattern)) {
logLine.setLogLevel(convertCharToLogLevel('V'));
}
logLine.setTag(tagText);
logLine.setProcessId(Integer.parseInt(matcher.group(3)));
logLine.setLogOutput(logText);
} else {
logLine.setLogOutput(originalLine);
logLine.setLogLevel(-1);
}
return logLine;
}
private static int convertCharToLogLevel(char logLevelChar) {
switch (logLevelChar) {
case 'D':
return Log.DEBUG;
case 'E':
return Log.ERROR;
case 'I':
return Log.INFO;
case 'V':
case 'F':
return Log.VERBOSE;
case 'W':
return Log.WARN;
}
return -1;
}
private static char convertLogLevelToChar(int logLevel) {
switch (logLevel) {
case Log.DEBUG:
return 'D';
case Log.ERROR:
return 'E';
case Log.INFO:
return 'I';
case Log.VERBOSE:
return 'V';
case Log.WARN:
return 'W';
}
return ' ';
}
public String getOriginalLine() {
if (logLevel == -1) { // starter line like "begin of log etc. etc."
return logOutput;
}
StringBuilder stringBuilder = new StringBuilder();
if (timestamp != null) {
stringBuilder.append(timestamp).append(' ');
}
stringBuilder.append(convertLogLevelToChar(logLevel))
.append('/')
.append(tag)
.append('(')
.append(processId)
.append("): ")
.append(logOutput);
return stringBuilder.toString();
}
public String getLogLevelText() {
return Character.toString(convertLogLevelToChar(logLevel));
}
public int getLogLevel() {
return logLevel;
}
public void setLogLevel(int logLevel) {
this.logLevel = logLevel;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getLogOutput() {
return logOutput;
}
public void setLogOutput(String logOutput) {
this.logOutput = logOutput;
}
public int getProcessId() {
return processId;
}
public void setProcessId(int processId) {
this.processId = processId;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
public boolean isHighlighted() {
return highlighted;
}
public void setHighlighted(boolean highlighted) {
this.highlighted = highlighted;
}
}

View File

@@ -0,0 +1,114 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import com.mogo.eagle.core.function.hmi.R;
/**
* 日志专属TitleBar
*/
public class LogTitleBar extends FrameLayout {
private OnTitleBarClickListener mListener;
private TextView mBack;
private TextView mTitle;
private ImageView mIcon;
public LogTitleBar(@NonNull Context context) {
this(context, null);
}
public LogTitleBar(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public LogTitleBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.log_title_bar, this, true);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.LogTitleBar);
int icon = a.getResourceId(R.styleable.LogTitleBar_Icon, 0);
String title = a.getString(R.styleable.LogTitleBar_Title);
String back = a.getString(R.styleable.LogTitleBar_Back);
a.recycle();
mBack = findViewById(R.id.back);
mIcon = findViewById(R.id.icon);
mTitle = findViewById(R.id.title);
mIcon.setOnClickListener(v -> {
if (mListener != null) {
mListener.onRightClick();
}
});
mBack.setOnClickListener(v -> {
if (mListener != null) {
mListener.onLeftClick();
}
});
setBack(back);
setTitle(title);
setIcon(icon);
}
/**
* TitleBar 点击事件回调
*/
public interface OnTitleBarClickListener {
void onRightClick();
void onLeftClick();
}
public void setTitle(@StringRes int title) {
setTitle(getResources().getString(title));
}
public void setTitle(String title) {
if (TextUtils.isEmpty(title)) {
mTitle.setText("");
} else {
mTitle.setText(title);
mTitle.setAlpha(0);
mTitle.setVisibility(View.VISIBLE);
mTitle.animate().alpha(1).start();
}
}
public void setBack(String back) {
if (TextUtils.isEmpty(back)) {
mBack.setText("");
} else {
mBack.setText(back);
}
}
public void setIcon(@DrawableRes int id) {
if (id == 0) {
return;
}
mIcon.setImageResource(id);
mIcon.setVisibility(View.VISIBLE);
}
public void setListener(OnTitleBarClickListener listener) {
mListener = listener;
}
}

View File

@@ -0,0 +1,49 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.view.ViewGroup;
import android.view.WindowManager;
public class LogViewLayoutParams {
/**
* 悬浮窗不能获取焦点
*/
public static int FLAG_NOT_FOCUSABLE = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
public static int FLAG_NOT_TOUCHABLE = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
/**
* 是否允许超出屏幕
*/
public static int FLAG_LAYOUT_NO_LIMITS = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
/**
* 悬浮窗不能获取焦点并且不相应触摸
*/
public static int FLAG_NOT_FOCUSABLE_AND_NOT_TOUCHABLE = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
public static int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
public static int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
/**
* 只针对系统悬浮窗起作用 值基本上为以上2个
*/
public int flags;
/**
* 只针对系统悬浮窗起作用 值基本上为Gravity
*/
public int gravity;
public int x;
public int y;
public int width;
public int height;
@Override
public String toString() {
return "LogViewLayoutParams{" +
"flags=" + flags +
", gravity=" + gravity +
", x=" + x +
", y=" + y +
", width=" + width +
", height=" + height +
'}';
}
}

View File

@@ -0,0 +1,32 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.view.MotionEvent;
import android.view.View;
/**
* touch 事件代理 解决点击和触摸事件的冲突
*/
public class TouchProxy {
public TouchProxy(OnTouchEventListener eventListener) {
}
public void setEventListener(OnTouchEventListener eventListener) {
}
public boolean onTouchEvent(View v, MotionEvent event) {
return true;
}
public interface OnTouchEventListener {
void onMove(int x, int y, int dx, int dy);
void onUp(int x, int y);
void onDown(int x, int y);
}
}

View File

@@ -17,10 +17,10 @@ import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.function.hmi.WaringConst;
import com.mogo.eagle.core.function.hmi.notification.WarningFloat;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform;
import com.mogo.eagle.core.utilcode.util.BitmapHelper;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
import com.mogo.utils.BitmapHelper;
import com.mogo.utils.glide.GlideApp;
import com.mogo.utils.glide.GlideRoundedCornersTransform;
/**
* @author liujing

View File

@@ -10,15 +10,15 @@ import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform
import com.mogo.eagle.core.utilcode.util.BitmapHelper
import com.mogo.eagle.core.widget.media.video.NoticeSimpleVideoPlayer
import com.mogo.module.common.MogoApisHandler
import com.mogo.module.common.dialog.BaseFloatDialog
import com.mogo.service.IMogoServiceApis
import com.mogo.service.statusmanager.IMogoStatusChangedListener
import com.mogo.service.statusmanager.StatusDescriptor
import com.mogo.utils.BitmapHelper
import com.mogo.utils.glide.GlideApp
import com.mogo.utils.glide.GlideRoundedCornersTransform
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack
@@ -122,8 +122,10 @@ class NoticeCheckDialog(context: Context) : BaseFloatDialog(context), LifecycleO
pushVideo?.visibility = View.GONE
pushImageView?.visibility = View.VISIBLE
pushImageView?.let {
GlideApp.with(context).load(noticeNormal.imageUrl).optionalTransform(GlideRoundedCornersTransform(
20f, GlideRoundedCornersTransform.CornerType.ALL)).into(it)
GlideApp.with(context).load(noticeNormal.imageUrl).optionalTransform(
GlideRoundedCornersTransform(
20f, GlideRoundedCornersTransform.CornerType.ALL)
).into(it)
}
}

View File

@@ -12,14 +12,12 @@ import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.mogo.eagle.core.data.notice.NoticeNormalData;
import com.mogo.eagle.core.data.notice.NoticeTrafficStyleInfo;
import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.function.hmi.WaringConst;
import com.mogo.eagle.core.function.hmi.notification.WarningFloat;
import com.mogo.eagle.core.utilcode.util.SharedPrefs;
import com.mogo.utils.glide.GlideApp;
import com.mogo.utils.glide.GlideRoundedCornersTransform;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform;
/**
* @author lixiaopeng

View File

@@ -22,6 +22,10 @@ import com.mogo.eagle.core.function.api.notice.NoticeNetCallBack;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
import com.mogo.eagle.core.function.call.notice.CallerNoticeManager;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform;
import com.mogo.eagle.core.utilcode.util.BitmapHelper;
import com.mogo.eagle.core.utilcode.util.DateTimeUtils;
import com.mogo.eagle.core.widget.media.video.NoticeSimpleSmallVideoPlayer;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.module.common.dialog.BaseFloatDialog;
@@ -29,11 +33,7 @@ import com.mogo.service.IMogoServiceApis;
import com.mogo.service.imageloader.MogoImageView;
import com.mogo.service.statusmanager.IMogoStatusChangedListener;
import com.mogo.service.statusmanager.StatusDescriptor;
import com.mogo.utils.BitmapHelper;
import com.mogo.utils.glide.GlideApp;
import com.mogo.utils.glide.GlideRoundedCornersTransform;
import com.shuyu.gsyvideoplayer.GSYVideoManager;
import com.mogo.utils.DateTimeUtils;
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder;
import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack;
import com.shuyu.gsyvideoplayer.utils.NetworkUtils;

View File

@@ -1,6 +1,7 @@
package com.mogo.eagle.core.function.hmi.ui.setting
import android.content.Context
import android.text.Html
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
@@ -9,22 +10,20 @@ import com.mogo.cloud.passport.MoGoAiCloudClient
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.data.app.AppConfigInfo
import com.mogo.eagle.core.data.autopilot.AutopilotCarStateInfo
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.data.autopilot.*
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.data.constants.MoGoConfig
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.data.obu.ObuStatusInfo
import com.mogo.eagle.core.data.traffic.TrafficData
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotCarStateListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotPlanningListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.api.devatools.IMoGoDevaToolsListener
import com.mogo.eagle.core.function.api.map.listener.IMoGoMapLocationListener
import com.mogo.eagle.core.function.api.obu.IMoGoObuStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotCarStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.*
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsListenerManager
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
@@ -34,18 +33,15 @@ import com.mogo.eagle.core.function.call.map.CallerSmpManager
import com.mogo.eagle.core.function.call.obu.CallerOBUManager
import com.mogo.eagle.core.function.call.obu.CallerObuListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.logcatch.ILogViewListener
import com.mogo.eagle.core.function.hmi.ui.logcatch.LogInfoView
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.AppUtils
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.eagle.core.utilcode.util.LogUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
import com.mogo.eagle.core.utilcode.util.*
import com.mogo.map.MogoMap
import com.mogo.utils.DeviceIdUtils
import com.mogo.utils.NetworkUtils
import com.mogo.utils.UiThreadHandler
import com.mogo.utils.storage.SharedPrefsMgr
import kotlinx.android.synthetic.main.view_debug_setting.view.*
import java.util.*
/**
* @author xiaoyuzhou
@@ -54,33 +50,91 @@ import kotlinx.android.synthetic.main.view_debug_setting.view.*
* 展示 本机、网络、工控机、OBU等状态信息支持设置IP等参数进行调试
*/
class DebugSettingView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoObuStatusListener,
IMoGoAutopilotStatusListener, IMoGoAutopilotCarStateListener, IMoGoMapLocationListener {
IMoGoAutopilotStatusListener, IMoGoAutopilotCarStateListener,
IMoGoMapLocationListener, IMoGoAutopilotIdentifyListener,
IMoGoAutopilotPlanningListener {
private val TAG = "DebugSettingView"
private var logInfoView: LogInfoView? = null
private var logViewAttach = false
private var mAutoPilotStatusInfo: AutopilotStatusInfo? = null
private var mAutoPilotCarStateInfo: AutopilotCarStateInfo? = null
// 感知识别数据个数
private var mIdentifyDataSize = 0
// 引导线点个数
private var mTrajectoryInfoSize = 0
// 全局路径规划点个数
private var mRouteInfoSize = 0
init {
LayoutInflater.from(context).inflate(R.layout.view_debug_setting, this, true)
initView()
}
/**
* 定时刷新视图数据防止因为过快更新导致anr异常
*/
private val timerTaskRefresh = object : TimerTask() {
override fun run() {
UiThreadHandler.post {
// 绘制应用基本信息
drawAppInfo()
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
// 添加 OBU状态 监听
CallerObuListenerManager.addListener(TAG, this)
// 添加 ADAS状态 监听
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
// 添加 ADAS车辆状态&定位 监听
CallerAutopilotCarStatusListenerManager.addListener(TAG, this)
// 添加 地图样式改变 监听
CallerMapLocationListenerManager.addListener(TAG, this)
// 添加 域控制器感知数据 监听
CallerAutopilotIdentifyListenerManager.addListener(TAG, this)
// 添加 规划路径相关回调 监听
CallerAutopilotPlanningListenerManager.addListener(TAG, this)
if (logInfoView != null) {
logInfoView!!.onEnterForeground()
}
// 开启定时查询速度
Timer().schedule(timerTaskRefresh, Date(), 100)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
// 移除 OBU状态 监听
CallerObuListenerManager.removeListener(TAG)
// 移除 ADAS状态 监听
CallerAutoPilotStatusListenerManager.removeListener(TAG)
// 移除 ADAS车辆状态&定位 监听
CallerAutopilotCarStatusListenerManager.removeListener(TAG)
// 移除 地图样式改变 监听
CallerMapLocationListenerManager.removeListener(TAG)
// 移除 域控制器感知数据 监听
CallerAutopilotIdentifyListenerManager.removeListener(TAG)
// 移除 规划路径相关回调 监听
CallerAutopilotPlanningListenerManager.removeListener(TAG)
if (logInfoView != null) {
logInfoView!!.onEnterBackground()
}
try {
timerTaskRefresh.cancel()
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun initView() {
@@ -102,7 +156,6 @@ class DebugSettingView @JvmOverloads constructor(
}
}
tbSpeedView.setOnCheckedChangeListener { buttonView, isChecked ->
if (!isChecked) {
CallerHmiManager.setSpeedChartViewVisibility(View.VISIBLE)
@@ -135,16 +188,37 @@ class DebugSettingView @JvmOverloads constructor(
}
}
tbOpenLight.setOnCheckedChangeListener { buttonView, isChecked ->
if (!isChecked) {
CallerHmiManager.setTurnLightFunction(true)
} else {
CallerHmiManager.setTurnLightFunction(false)
}
}
tbOpenBrakeLight.setOnCheckedChangeListener { buttonView, isChecked ->
if (!isChecked) {
CallerHmiManager.setBrakeLightFunction(true)
} else {
CallerHmiManager.setBrakeLightFunction(false)
}
}
changesight_top_btn.setOnClickListener {
CallerHDMapManager.setMapDAngle(0);
}
tvObuInfo.text = CallerObuListenerManager.getObuStatusInfoJsonString()
tvAutopilotInfo.text =
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfoJsonString()
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfoJsonString()
// 绘制应用基本信息
drawAppInfo()
// 初始化OBU IP信息
val ipAddress =
SharedPrefsMgr.getInstance(context).getString(MoGoConfig.OBU_IP, "192.168.1.199")
SharedPrefsMgr.getInstance(context).getString(MoGoConfig.OBU_IP, "192.168.1.199")
etObuIP.setText(ipAddress)
etObuIP.text?.let { etObuIP.setSelection(it.length) }
@@ -160,7 +234,7 @@ class DebugSettingView @JvmOverloads constructor(
// 初始化工控机 IP信息
val autoPilotIpAddress =
SharedPrefsMgr.getInstance(context).getString(MoGoConfig.AUTOPILOT_IP, "192.168.1.102")
SharedPrefsMgr.getInstance(context).getString(MoGoConfig.AUTOPILOT_IP, FunctionBuildConfig.adasConnectIP)
etAutopilotIP.setText(autoPilotIpAddress)
etAutopilotIP.text?.let { etAutopilotIP.setSelection(it.length) }
@@ -169,8 +243,6 @@ class DebugSettingView @JvmOverloads constructor(
val autoPilotIp = etAutopilotIP.text.toString()
if (autoPilotIp.isNotEmpty()) {
CallerAutoPilotManager.resetIpAddress(autoPilotIp)
// 保存本地 AutoPilot IP地址
SharedPrefsMgr.getInstance(context).putString(MoGoConfig.AUTOPILOT_IP, autoPilotIp)
} else {
ToastUtils.showShort("请输入正确的IP地址")
}
@@ -181,21 +253,20 @@ class DebugSettingView @JvmOverloads constructor(
CallerAutoPilotManager.recordPackage()
}
// 初始化 GSP数据源 数据
rgGpsProvider.check(
when (FunctionBuildConfig.gpsProvider) {
0 -> {
R.id.rbGpsProviderAndroid
when (FunctionBuildConfig.gpsProvider) {
0 -> {
R.id.rbGpsProviderAndroid
}
1 -> {
R.id.rbGpsProviderRTK
}
2 -> {
R.id.rbGpsProviderOBU
}
else -> R.id.rbGpsProviderAndroid
}
1 -> {
R.id.rbGpsProviderRTK
}
2 -> {
R.id.rbGpsProviderOBU
}
else -> R.id.rbGpsProviderAndroid
}
)
rgGpsProvider.setOnCheckedChangeListener { group, checkedId ->
when (checkedId) {
@@ -211,71 +282,52 @@ class DebugSettingView @JvmOverloads constructor(
}
}
// 初始化 感知数据是否绘制 选择情况
rgIsDrawIdentifyData.check(
when (FunctionBuildConfig.isDrawIdentifyData) {
true -> {
R.id.rbDraw
}
false -> {
R.id.rbDoNotDraw
}
}
)
rgIsDrawIdentifyData.setOnCheckedChangeListener { group, checkedId ->
when (checkedId) {
R.id.rbDraw -> {
FunctionBuildConfig.isDrawIdentifyData = true
}
R.id.rbDoNotDraw -> {
FunctionBuildConfig.isDrawIdentifyData = false
}
}
// 初始化 ADAS感知数据是否绘制 选择情况
tbIsDrawIdentifyData.isChecked = FunctionBuildConfig.isDrawIdentifyData
tbIsDrawIdentifyData.setOnCheckedChangeListener { buttonView, isChecked ->
FunctionBuildConfig.isDrawIdentifyData = isChecked
}
/*// 切换地图中心点视角
tbChangeCarCenter100.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(1)
// 初始化 OBU感知数据是否绘制 选择情况
tbIsDrawOBUIdentifyData.isChecked = FunctionBuildConfig.isDrawObuIdentifyData
tbIsDrawOBUIdentifyData.setOnCheckedChangeListener { buttonView, isChecked ->
FunctionBuildConfig.isDrawObuIdentifyData = isChecked
}
tbChangeCarQuarter100.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(2)
// 演示模式,上一次勾选的数据
cbIsDrawAutopilotTrajectoryData.setOnCheckedChangeListener { buttonView, isChecked ->
FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = isChecked
}
tbChangeCarTwoFifths100.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(3)
// 演示模式,上一次勾选的数据
tbIsDemoMode.isChecked = FunctionBuildConfig.isDemoMode
// 演示模式
tbIsDemoMode.setOnCheckedChangeListener { buttonView, isChecked ->
CallerAutoPilotManager.setDemoMode(isChecked)
FunctionBuildConfig.isDemoMode = isChecked
cbIsDrawAutopilotTrajectoryData.isEnabled = !isChecked
FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = isChecked
if (!FunctionBuildConfig.isDemoMode) {
cbIsDrawAutopilotTrajectoryData.isChecked = false
}
SharedPrefsMgr.getInstance(context).putBoolean(MoGoConfig.IS_DEMO_MODE, isChecked)
}
tbChangeCarTwoFifths80.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(4)
// 模拟自动驾驶中
tbChangeAutoPilotStatus.setOnCheckedChangeListener { buttonView, isChecked ->
CallerAutoPilotManager.setControlAutopilotCarAuto(isChecked)
}
tbChangeCAR_AFTER_30_FRONT_80.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(5)
}
tbChangeCAR_AFTER_30_FRONT_100.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(6)
}
tbChangeCAR_AFTER_30_FRONT_120.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(7)
}
tbChangeCAR_AFTER_40_FRONT_80.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(8)
}
tbChangeCAR_AFTER_40_FRONT_100.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(9)
}
tbChangeCAR_AFTER_40_FRONT_120.setOnClickListener {
CallerHDMapManager.changeMaoViewAngle(10)
}*/
tbSelfLog.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
LogUtils.getConfig().isLogSwitch = false
Logger.init(LogLevel.OFF)
com.mogo.utils.logger.Logger.init(com.mogo.utils.logger.LogLevel.OFF)
com.elegant.log.simplelog.Logger.init(com.elegant.log.simplelog.LogLevel.OFF)
com.zhidao.account.sdk.utils.Logger.init(false)
} else {
LogUtils.getConfig().isLogSwitch = true
Logger.init(LogLevel.DEBUG)
com.mogo.utils.logger.Logger.init(com.mogo.utils.logger.LogLevel.DEBUG)
com.elegant.log.simplelog.Logger.init(com.elegant.log.simplelog.LogLevel.DEBUG)
com.zhidao.account.sdk.utils.Logger.init(true)
}
@@ -298,7 +350,7 @@ class DebugSettingView @JvmOverloads constructor(
}
tbLogCatch.isChecked =
SharedPrefsMgr.getInstance(context).getBoolean(MoGoConfig.CATCH_LOG,false)
SharedPrefsMgr.getInstance(context).getBoolean(MoGoConfig.CATCH_LOG, false)
tbLogCatch.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
CallerDevaToolsManager.startCatchLog()
@@ -309,12 +361,48 @@ class DebugSettingView @JvmOverloads constructor(
}
}
CallerDevaToolsListenerManager.registerDevaToolsLogCatchListener(TAG,
object : IMoGoDevaToolsListener {
override fun onLogCatchClose() {
super.onLogCatchClose()
tbLogCatch.isChecked = false
}
})
object : IMoGoDevaToolsListener {
override fun onLogCatchClose() {
super.onLogCatchClose()
tbLogCatch.isChecked = false
}
override fun onLogCatch(lineLog: String) {
logInfoView?.let {
if (logViewAttach) {
it.onLogCatch(lineLog)
}
}
}
})
tbLogDebugView.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
logInfoView = LogInfoView()
logInfoView!!.registerLogViewListener(object : ILogViewListener {
override fun onAttach() {
logViewAttach = true
}
override fun onDetach() {
logViewDestroy()
tbLogDebugView.isChecked = false
}
})
logInfoView!!.show(context)
} else {
logViewDestroy()
}
}
}
private fun logViewDestroy() {
logInfoView?.let {
it.dismiss()
it.unRegisterLogViewListener()
logInfoView = null
logViewAttach = false
}
}
/**
@@ -335,35 +423,45 @@ class DebugSettingView @JvmOverloads constructor(
AppConfigInfo.isConnectSocket = DebugConfig.isDownloadSnapshot()
// 将数据绘制
tvAppInfo.text = AppConfigInfo.toString()
tvAppInfo.text = Html.fromHtml(AppConfigInfo.toString())
tvAutopilotInfo.text = GsonUtils.toJson(mAutoPilotStatusInfo)
tvCarInfo.text =
"GPS时间${mAutoPilotCarStateInfo?.values?.satelliteTime}\n" +
"自车经纬度:\n${mAutoPilotCarStateInfo?.values?.lon}\n${mAutoPilotCarStateInfo?.values?.lat}\n"
tvIdentifyInfo.text =
"感知数据个数:${mIdentifyDataSize}"
tvTrajectoryInfoSize.text =
"引导线点个数:${mTrajectoryInfoSize}"
tvRouteInfoSize.text =
"全局路径规划点个数:${mRouteInfoSize}"
// 用完之后重制为0防止节点回掉突然没数据导致页面显示还是之前的数据情况
mIdentifyDataSize = 0
mTrajectoryInfoSize = 0
mRouteInfoSize = 0
}
/**
* OBU状态回调
*/
override fun onObuStatusResponse(obuStatusInfo: ObuStatusInfo) {
UiThreadHandler.post {
tvObuInfo.text = GsonUtils.toJson(obuStatusInfo)
tvObuInfo.text = GsonUtils.toJson(obuStatusInfo)
AppConfigInfo.obuSdkVersion = obuStatusInfo.obuSdkVersion
AppConfigInfo.isConnectObu = obuStatusInfo.obuStatus
drawAppInfo()
}
AppConfigInfo.obuSdkVersion = obuStatusInfo.obuSdkVersion
AppConfigInfo.isConnectObu = obuStatusInfo.obuStatus
}
/**
* 自动驾驶状态回调
*/
override fun onAutopilotStatusResponse(autoPilotStatusInfo: AutopilotStatusInfo) {
UiThreadHandler.post {
tvAutopilotInfo.post {
tvAutopilotInfo.text = GsonUtils.toJson(autoPilotStatusInfo)
mAutoPilotStatusInfo = autoPilotStatusInfo
AppConfigInfo.adasSdkVersion = autoPilotStatusInfo.version
AppConfigInfo.isConnectAutopilot = autoPilotStatusInfo.connectStatus
drawAppInfo()
}
}
AppConfigInfo.adasSdkVersion = autoPilotStatusInfo.version
AppConfigInfo.isConnectAutopilot = autoPilotStatusInfo.connectStatus
}
override fun onAutopilotArriveAtStation(autopilotWayArrive: AutopilotStationInfo?) {
@@ -371,7 +469,8 @@ class DebugSettingView @JvmOverloads constructor(
}
override fun onAutopilotCarStateData(autoPilotCarStateInfo: AutopilotCarStateInfo?) {
//Logger.d(TAG, "autoPilotCarStateInfo:$autoPilotCarStateInfo")
mAutoPilotCarStateInfo = autoPilotCarStateInfo
}
override fun onAutopilotSNRequest() {
@@ -383,7 +482,26 @@ class DebugSettingView @JvmOverloads constructor(
}
override fun onLocationChanged(location: MogoLocation?) {
Logger.d(TAG, "location:$location")
//Logger.d(TAG, "location:$location")
}
override fun onAutopilotIdentifyDataUpdate(trafficData: ArrayList<TrafficData>?) {
mIdentifyDataSize = trafficData?.size ?: 0
}
override fun onAutopilotWarnMessage(autopilotWarnMessage: AutopilotWarnMessage?) {
}
override fun onAutopilotRecordResult(record: AutoPilotRecordResult?) {
}
override fun onAutopilotTrajectory(trajectoryInfos: ArrayList<ADASTrajectoryInfo>) {
mTrajectoryInfoSize = trajectoryInfos.size
}
override fun onAutopilotRotting(routeList: AutopilotRouteInfo?) {
mRouteInfoSize = routeList?.models?.size ?: 0
}
}

View File

@@ -0,0 +1,60 @@
package com.mogo.eagle.core.function.hmi.ui.tools
import android.content.Context
import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.module.common.dialog.BaseFloatDialog
/**
* @author XuXinChao
* @description 工控机确认升级对话框
* @since: 2022/1/13
*/
class AdUpgradeDialog(context: Context) : BaseFloatDialog(context), LifecycleObserver {
companion object {
const val TAG = "AdUpgradeDialog"
}
private var upgradeConfirm : TextView? = null
private var upgradeCancel : TextView? = null
private var clickListener: ClickListener? = null
init {
setContentView(R.layout.dialog_ad_upgrade)
setCanceledOnTouchOutside(true)
upgradeConfirm=findViewById(R.id.tv_upgrade_confirm)
upgradeCancel=findViewById(R.id.tv_upgrade_cancel)
upgradeConfirm?.setOnClickListener{
Logger.i(TAG,"upgradeConfirm click")
clickListener?.confirm()
dismiss()
}
upgradeCancel?.setOnClickListener {
Logger.i(TAG,"upgradeCancel click")
clickListener?.cancel()
dismiss()
}
}
fun setClickListener(clickListener: ClickListener) {
this.clickListener = clickListener
}
fun showUpgradeDialog(){
if(isShowing){
return
}
show()
}
interface ClickListener{
fun confirm()
fun cancel()
}
}

View File

@@ -7,22 +7,29 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.check.CallerCheckManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.utils.KeyBoardUtil
import com.mogo.eagle.core.utilcode.util.ToastUtils
import kotlinx.android.synthetic.main.view_auto_pilot_check.view.*
import kotlinx.android.synthetic.main.view_check_system.view.*
/**
* @author ChenFufeng
* 设置自动驾驶速度和检测页入口
*/
class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
class AutoPilotAndCheckView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(
context,
attrs,
defStyleAttr
), IMoGoAutopilotStatusListener {
private val TAG = "AutoPilotAndCheckView"
@@ -30,13 +37,6 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
private var keyBoardUtil: KeyBoardUtil? = null
private var connectStatus = false
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(
context,
attrs,
defStyleAttr
)
init {
LayoutInflater.from(context).inflate(R.layout.view_auto_pilot_check, this, true)
initView()
@@ -61,7 +61,7 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
else -> {
keyBoardUtil?.hideKeyboard()
// 设置自动驾驶速度
var isSuccess = CallerAutoPilotManager.setAutoPilotSpeed(speed)
val isSuccess = CallerAutoPilotManager.setAutoPilotSpeed(speed)
when {
isSuccess -> {
ToastUtils.showShort("车速设置成功,立即生效")
@@ -81,17 +81,26 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
viewCheckStatus.setOnClickListener {
clickListener?.go2CheckPage()
}
ivDebugPanel.setOnClickListener {
clickListener?.showDebugPanelView()
}
etInputSpeed.setOnTouchListener { v, _ ->
if (etInputSpeed.hasFocusable()) {
if (keyBoardUtil == null) {
keyBoardUtil = KeyBoardUtil(sKeyBoardView, etInputSpeed)
if (!connectStatus) {
ToastUtils.showShort("设置车速失败,请启动域控制器")
keyBoardUtil?.hideKeyboard()
return@setOnTouchListener true
} else {
if (etInputSpeed.hasFocusable()) {
if (keyBoardUtil == null) {
keyBoardUtil = KeyBoardUtil(sKeyBoardView, etInputSpeed)
}
keyBoardUtil?.showKeyboard()
}
keyBoardUtil?.showKeyboard()
if (!etInputSpeed.hasFocus()) {
etInputSpeed.requestFocus()
}
return@setOnTouchListener false
}
if (!etInputSpeed.hasFocus()) {
etInputSpeed.requestFocus()
}
return@setOnTouchListener false
}
// // 比如需要设置默认速度
// val speed = "30"
@@ -103,6 +112,21 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
this.clickListener = clickListener
}
/**
* 展示工控机下载、升级状态信息
* @param upgradeMode 升级模式(提示升级、静默升级)
* @param downloadStatus 下载状态
* @param currentProgress 当前已经下载包体大小
* @param totalProgress 下载包体总大小
* @param downloadVersion 下载版本
* @param upgradeStatus 升级状态
*/
fun showAdUpgradeStatus(upgradeMode: Int,downloadStatus : Int,currentProgress : Int,totalProgress : Int
,downloadVersion : String,upgradeStatus : Int){
systemVersionView?.showAdUpgradeStatus(upgradeMode,downloadStatus,currentProgress, totalProgress, downloadVersion, upgradeStatus)
checkSystemView?.setAdUpgradeStatus(downloadStatus, upgradeStatus)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
@@ -117,10 +141,6 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
connectStatus = autoPilotStatusInfo.connectStatus
}
override fun onAutopilotArriveAtStation(autopilotWayArrive: AutopilotStationInfo?) {
}
override fun onAutopilotGuardian(guardianInfo: AutopilotGuardianStatusInfo?) {
}
@@ -128,5 +148,6 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
interface ClickListener {
fun go2CheckPage()
fun onClose(v: View)
fun showDebugPanelView()
}
}

View File

@@ -0,0 +1,404 @@
package com.mogo.eagle.core.function.hmi.ui.tools
import android.annotation.SuppressLint
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.StateListDrawable
import android.os.Handler
import android.util.AttributeSet
import android.util.StateSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.Keep
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.room.*
import com.google.gson.annotations.Expose
import com.mogo.commons.debug.DebugConfig
import com.mogo.commons.debug.DebugConfig.getNetMode
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.tools.BadCaseEntity.Reason
import com.mogo.eagle.core.network.MoGoRetrofitFactory
import com.mogo.eagle.core.network.utils.GsonUtil
import com.mogo.eagle.core.utilcode.kotlin.*
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.Utils
import kotlinx.android.synthetic.main.layout_badcase_collect.view.*
import kotlinx.coroutines.*
import kotlinx.coroutines.android.asCoroutineDispatcher
import retrofit2.Response
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.POST
import java.text.SimpleDateFormat
import java.util.*
import kotlin.Result.Companion.failure
import kotlin.Result.Companion.success
private typealias OnDismissCallback = () -> Unit
private typealias OnSelectCallback = (Reason) -> Unit
object Repository {
fun dao(): Dao {
return Room.databaseBuilder(Utils.getApp(), RecordDb::class.java, "bad-cases").build().dao()
}
@Database(entities = [
AutoPilotRecordResult::class
],
version = 1,
exportSchema = false)
abstract class RecordDb : RoomDatabase() {
abstract fun dao(): Dao
}
@androidx.room.Dao
interface Dao {
@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertRecord(record: AutoPilotRecordResult): Long
@Transaction
@Delete
suspend fun deleteRecord(record: AutoPilotRecordResult): Int
@Query("SELECT * FROM record ORDER BY timestamp ASC")
suspend fun getAllUnConsumedRecords(): List<AutoPilotRecordResult>?
}
}
interface BadCaseApi {
@FormUrlEncoded
@POST("/yycp-vehicle-management-service/tool/badcase/add")
suspend fun post(@FieldMap map: Map<String, String>): Response<PostResult>
@GET("/yycp-vehicle-management-service/tool/badcase/reasons")
suspend fun get(): Response<BadCaseEntity>
}
@Keep
class BadCaseEntity {
var code: Int = -1
var data: List<Reason>? = null
var msg: String? = null
var success: Boolean = false
var total: Int = -1
@Expose(serialize = false, deserialize = false)
var isBuildIn: Boolean = false
@Keep
class Reason {
var id: String? = null
var reason: String? = null
/**
* 业务字段,不参与序列化和反序列化
*/
@Expose(deserialize = false, serialize = false)
var isChecked: Boolean = false
}
}
@Keep
class PostResult {
var code: Int = -1
var msg: String? = null
var data: Array<String>? = null
var success: Boolean = false
override fun toString(): String {
return "Result(code=$code, msg=$msg, data=${data?.contentToString()}, success=$success)"
}
}
private fun getHost(): String = if (getNetMode() == DebugConfig.NET_MODE_RELEASE) "http://dzt.zhidaozhixing.com" else "http://front.zdjs-private-test.myghost.zhidaoauto.com"
internal suspend fun post(map: Map<String, String>): Response<PostResult> {
return MoGoRetrofitFactory.getInstance(getHost()).create(BadCaseApi::class.java).post(map)
}
private suspend fun get(): Response<BadCaseEntity>? {
return try { MoGoRetrofitFactory.getInstance(getHost()).create(BadCaseApi::class.java).get() } catch (t: Throwable) { t.printStackTrace(); null}
}
private suspend fun updateCache(entity: BadCaseEntity) = suspendCancellableCoroutine<Unit> {
try {
val future = ThreadUtils.getIoPool().submit {
try {
val gson = GsonUtil.jsonFromObject(entity)
Sp.saveBody(gson)
it.resumeWith(success(Unit))
} catch (t: Throwable) {
it.resumeWith(failure(t))
}
}
it.invokeOnCancellation {
future.cancel(true)
}
} catch (e: Throwable) {
it.resumeWith(failure(e))
}
}
private suspend fun getCache(): BadCaseEntity? = suspendCancellableCoroutine {
try {
val body = Sp.getBody()
if (body != null && body.isNotEmpty()) {
val future = ThreadUtils.getIoPool().submit {
try {
val result = GsonUtil.objectFromJson(body, BadCaseEntity::class.java)
it.resumeWith(success(result))
} catch (t: Throwable) {
it.resumeWith(success(null))
}
}
it.invokeOnCancellation {
future.cancel(true)
}
}
} catch (t: Throwable) {
it.resumeWith(success(null))
}
}
private fun getBuildIn(): BadCaseEntity = BadCaseEntity().also { itx ->
val data = mutableListOf<Reason>()
data += Reason().also {
it.id = "1"
it.reason = "变道有干扰"
}
data += Reason().also {
it.id = "2"
it.reason = "遇红绿灯未停车"
}
data += Reason().also {
it.id = "3"
it.reason = "遇障碍物未停车"
}
data += Reason().also {
it.id = "4"
it.reason = "无法绕行"
}
data += Reason().also {
it.id = "5"
it.reason = "画龙"
}
data += Reason().also {
it.id = "6"
it.reason = "转弯过于靠近路侧"
}
data += Reason().also {
it.id = "7"
it.reason = "无故退出自动驾驶"
}
data += Reason().also {
it.id = "8"
it.reason = "其它"
}
itx.data = data
itx.isBuildIn = true
}
internal object Sp {
private val sp by lazy {
Utils.getApp().getSharedPreferences("bad_case_prefs", MODE_PRIVATE)
}
@SuppressLint("ApplySharedPref")
fun saveBody(body: String) {
sp.edit().putString("prefs", body).commit()
}
fun getBody(): String? {
return sp.getString("prefs", null)
}
}
class AutoPilotBadCaseView: ConstraintLayout {
private var dismiss: OnDismissCallback? = null
private var select: OnSelectCallback? = null
private var cases: List<Reason>? = null
private var selectCase: Reason? = null
private val scope = CoroutineScope(Handler().asCoroutineDispatcher() + SupervisorJob())
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
LayoutInflater.from(context).inflate(R.layout.layout_badcase_collect, this, true)
background = ColorDrawable(Color.parseColor("#F0151D41"))
isClickable = true
layoutParams = ViewGroup.LayoutParams(960.toPixels().toInt(), 1528.toPixels().toInt())
close?.onClick {
dismiss?.invoke()
}
cancel?.also {
it.background = shape(solid = Color.parseColor("#3B4577"), radius = 16)
it.onClick {
dismiss?.invoke()
}
}
ok?.also {
val enabled = gradient(radius = 16.toPixels().toInt(), orientation = GradientDrawable.Orientation.LEFT_RIGHT, centerX = 0.06f, startColor = Color.rgb(35, 146, 252), endColor = Color.rgb(28, 75, 252))
val disabled = gradient(radius = 16.toPixels().toInt(), orientation = GradientDrawable.Orientation.LEFT_RIGHT, centerX = 0.06f, startColor = Color.rgb(24, 71, 129), endColor = Color.rgb(21, 46, 129))
it.background = object : StateListDrawable() {}.also { itx ->
itx.addState(intArrayOf(android.R.attr.state_enabled), enabled)
itx.addState(StateSet.WILD_CARD, disabled)
}
it.onClick {
val case = selectCase
if (case != null) {
select?.invoke(case)
}
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
val adapter = rv_take_over?.adapter
if (adapter != null && adapter.itemCount > 0) {
return
}
time_of_take_over?.text = "接管时间: ${(tag as? AutoPilotRecordResult)?.timestamp?.let {
try {
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).parse(it)?.let { itx ->
SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.getDefault()).format(itx)
}
} catch (e: Throwable) {
null
} ?: SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.getDefault()).format(Date())}}"
scope.launch {
showLoading()
try {
get()?.takeIf { it.isSuccessful && it.body() != null && it.body()?.code == 200 }?.let {
val entity = it.body()!!
try {
updateCache(entity)
} catch (t: Throwable) {
t.printStackTrace()
} finally {
updateBadCaseList(entity)
}
}
?:
getCache()?.also {
updateBadCaseList(it)
}
?:
updateBadCaseList(getBuildIn())
} finally {
hideLoading()
}
}
}
private fun updateBadCaseList(body: BadCaseEntity) {
cases = body.data
rv_take_over?.let {
it.layoutManager = LinearLayoutManager(it.context, LinearLayoutManager.VERTICAL, false)
it.adapter = object : RecyclerView.Adapter<BadCaseViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BadCaseViewHolder = BadCaseViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_badcase_item, parent, false))
override fun onBindViewHolder(holder: BadCaseViewHolder, position: Int) {
val cases = cases
if (cases == null || cases.isEmpty()) {
return
}
if (position >= cases.size) {
return
}
val case = cases[position]
holder.bindData(case)
}
override fun getItemCount(): Int = cases?.size ?: 0
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
scope.cancel("Cancel all for AutoPilotBadCaseView#onDetachedFromWindow")
}
private fun showLoading() {
pb?.let {
it.visibility = View.VISIBLE
}
}
private fun hideLoading() {
pb?.let {
it.visibility = View.INVISIBLE
}
}
private inner class BadCaseViewHolder(item: View) : RecyclerView.ViewHolder(item) {
private val check: ImageView = item.findViewById(R.id.check)
private val reason: TextView = item.findViewById(R.id.reason)
init {
check.background = StateListDrawable().also {
it.addState(intArrayOf(android.R.attr.state_selected), ContextCompat.getDrawable(itemView.context, R.drawable.icon_ap_badcase_check))
it.addState(StateSet.WILD_CARD, ContextCompat.getDrawable(itemView.context, R.drawable.icon_ap_badcase_default))
}
}
@SuppressLint("NotifyDataSetChanged")
fun bindData(case: Reason) {
check.isSelected = case.isChecked
reason.text = case.reason ?: ""
if (case.isChecked) {
ok?.isSelected = true
}
itemView.onClick {
case.isChecked = !case.isChecked
selectCase = case
cancelOtherChecked(case)
ok?.isEnabled = hasCheckedItem()
rv_take_over?.adapter?.notifyDataSetChanged()
}
}
private fun hasCheckedItem(): Boolean = cases?.find { it.isChecked } != null
private fun cancelOtherChecked(case: Reason) {
val cases = cases
if (cases == null || cases.isEmpty()) {
return
}
cases.filterNot { it == case }.forEach {
it.isChecked = false
}
}
}
fun onDismiss(dismiss:() -> Unit) {
this.dismiss = dismiss
}
fun onSelect(cb:(Reason) -> Unit) {
this.select = cb
}
}

View File

@@ -0,0 +1,60 @@
package com.mogo.eagle.core.function.hmi.ui.tools
import android.content.Context
import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.module.common.dialog.BaseFloatDialog
/**
* @author XuXinChao
* @description 重启自动驾驶docker确认对话框
* @since: 2022/2/17
*/
class DockerRebootDialog(context: Context): BaseFloatDialog(context), LifecycleObserver {
companion object {
const val TAG = "DockerRebootDialog"
}
private var rebootConfirm : TextView? = null
private var rebootCancel : TextView? = null
private var clickListener: ClickListener? = null
init{
setContentView(R.layout.dialog_docker_reboot)
setCanceledOnTouchOutside(true)
rebootConfirm=findViewById(R.id.tv_reboot_confirm)
rebootCancel=findViewById(R.id.tv_reboot_cancel)
rebootConfirm?.setOnClickListener{
Logger.i(TAG,"rebootConfirm click")
clickListener?.confirm()
dismiss()
}
rebootCancel?.setOnClickListener {
Logger.i(TAG,"rebootCancel click")
clickListener?.cancel()
dismiss()
}
}
fun setClickListener(clickListener: ClickListener) {
this.clickListener = clickListener
}
fun showUpgradeDialog(){
if(isShowing){
return
}
show()
}
interface ClickListener{
fun confirm()
fun cancel()
}
}

View File

@@ -0,0 +1,111 @@
package com.mogo.eagle.core.function.hmi.ui.turnlight
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.os.Looper
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.DecelerateInterpolator
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import kotlinx.android.synthetic.main.view_brake_light_status.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
/**
* @description
*
* @author lixiaopeng
* @since 2022/1/10
*/
class BrakeViewStatus @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
init {
LayoutInflater.from(context).inflate(R.layout.view_brake_light_status, this, true)
}
private var isBrake: Boolean = false
/**
* 刹车动画
*/
fun setBrakeLight(brakeLight: Int) {
if (brakeLight == 1) { //刹车灯亮
if (!isBrake) {
GlobalScope.launch(Dispatchers.Main) {
var appearAnimation = AlphaAnimation(0f, 1f)
appearAnimation.duration = 300
layout_brake.startAnimation(appearAnimation)
image_brake.startAnimation(appearAnimation)
tv_brake.startAnimation(appearAnimation)
layout_brake.visibility = View.VISIBLE
image_brake.visibility = View.VISIBLE
tv_brake.visibility = View.VISIBLE
isBrake = true
}
}
} else { //不踩刹车,就消失
if (isBrake) {
isBrake = false
GlobalScope.launch(Dispatchers.Main) {
scaleImageAndTv()
var disappearAnimation = AlphaAnimation(1f, 0f)
disappearAnimation.duration = 1200
layout_brake.startAnimation(disappearAnimation)
image_brake.startAnimation(disappearAnimation)
tv_brake.startAnimation(disappearAnimation)
disappearAnimation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(p0: Animation?) {
}
override fun onAnimationStart(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
layout_brake.visibility = View.GONE
image_brake.visibility = View.GONE
tv_brake.visibility = View.GONE
stopAnimate()
}
})
}
}
}
}
private fun scaleImageAndTv() {
val animatorSuofang = AnimatorSet() //组合动画
val scaleX = ObjectAnimator.ofFloat(image_brake, "scaleX", 1f, 1.3f)
val scaleY = ObjectAnimator.ofFloat(image_brake, "scaleY", 1f, 1.3f)
animatorSuofang.duration = 200
animatorSuofang.interpolator = DecelerateInterpolator()
animatorSuofang.play(scaleX).with(scaleY)
animatorSuofang.start()
val scaleXTv = ObjectAnimator.ofFloat(tv_brake, "scaleX", 1f, 1.3f)
val scaleYTv = ObjectAnimator.ofFloat(tv_brake, "scaleY", 1f, 1.3f)
animatorSuofang.duration = 200
animatorSuofang.interpolator = DecelerateInterpolator()
animatorSuofang.play(scaleXTv).with(scaleYTv)
animatorSuofang.start()
}
private fun stopAnimate() {
tv_brake.clearAnimation()
image_brake.clearAnimation()
}
}

View File

@@ -0,0 +1,171 @@
package com.mogo.eagle.core.function.hmi.ui.turnlight
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import kotlinx.android.synthetic.main.view_brake_light_status.view.*
import kotlinx.android.synthetic.main.view_turn_light_status.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
/**
* @description
*
* @author lixiaopeng
* @since 2022/1/10
*/
class TurnLightViewStatus @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
init {
LayoutInflater.from(context).inflate(R.layout.view_turn_light_status, this, true)
}
private var isShowNormalBg :Boolean = false
private var isLeftLight :Boolean = false
private var isRightLight :Boolean = false
private var isDisappare :Boolean = false
/**
* 转向灯动画
*/
fun setTurnLight(directionLight: Int) {
if (!isShowNormalBg && (directionLight == 1 || directionLight == 2)) {
GlobalScope.launch(Dispatchers.Main) {
showNormalAnimation()
}
isShowNormalBg = true
}
//根据左右进行显示和隐藏,实际要判断每个来的时间和频度
if (directionLight == 1) { //左转向
if (!isLeftLight) {
GlobalScope.launch(Dispatchers.Main) {
left_select_image.visibility = View.VISIBLE
right_select_image.visibility = View.GONE
right_select_image.clearAnimation()
setAnimation(left_select_image)
}
isLeftLight = true
isRightLight = false
isDisappare = false
}
} else if (directionLight == 2) { //右转向
if (!isRightLight) {
GlobalScope.launch(Dispatchers.Main) {
left_select_image.visibility = View.GONE
right_select_image.visibility = View.VISIBLE
left_select_image.clearAnimation()
setAnimation(right_select_image)
}
isRightLight = true
isLeftLight = false
isDisappare = false
}
} else { //消失
if (!isDisappare) {
GlobalScope.launch(Dispatchers.Main) {
animationDisappear()
}
isDisappare = true
isShowNormalBg = false
isLeftLight = false
isRightLight = false
isDisappare = false
}
}
}
//显示背景
private fun showNormalAnimation() {
val appearAnimation = AlphaAnimation(0f, 1.0f)
appearAnimation.duration = 600
val appearAnimationImage = AlphaAnimation(0f, 1.0f)
appearAnimation.duration = 1000
turn_light_layout.startAnimation(appearAnimation)
left_nor_image.startAnimation(appearAnimationImage)
right_nor_image.startAnimation(appearAnimationImage)
turn_light_layout.visibility = View.VISIBLE
left_nor_image.visibility = View.VISIBLE
right_nor_image.visibility = View.VISIBLE
}
//消失动画,当转向等数据为空时候
private fun animationDisappear() {
left_select_image.visibility = View.GONE
right_select_image.visibility = View.GONE
left_select_image.clearAnimation()
right_select_image.clearAnimation()
left_nor_image.clearAnimation()
right_nor_image.clearAnimation()
turn_light_layout.clearAnimation()
val disappearAnimationLeft = AlphaAnimation(1.0f, 0f)
disappearAnimationLeft.duration = 300
val disappearAnimationBg = AlphaAnimation(1.0f, 0f)
disappearAnimationBg.duration = 1200
left_nor_image.startAnimation(disappearAnimationLeft)
right_nor_image.startAnimation(disappearAnimationLeft)
turn_light_layout.startAnimation(disappearAnimationBg)
disappearAnimationLeft.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(p0: Animation?) {
}
override fun onAnimationStart(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
left_nor_image.visibility = View.GONE
right_nor_image.visibility = View.GONE
}
})
disappearAnimationBg.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(p0: Animation?) {
}
override fun onAnimationStart(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
turn_light_layout.visibility = View.GONE
// stopAnimate()
}
})
}
//实现图片闪烁效果
private fun setAnimation(imageView: ImageView) {
val animation = AlphaAnimation(1.0f, 0f)
animation.duration = 600
animation.interpolator = LinearInterpolator()
animation.repeatCount = Animation.INFINITE
animation.repeatMode = Animation.REVERSE
imageView.startAnimation(animation)
}
private fun stopAnimate() {
turn_light_layout.clearAnimation()
left_nor_image.clearAnimation()
right_nor_image.clearAnimation()
}
}

View File

@@ -0,0 +1,120 @@
package com.mogo.eagle.core.function.hmi.ui.utils
import android.text.TextUtils
import com.mogo.eagle.core.function.hmi.ui.logcatch.LogLine
import java.util.regex.Pattern
class SearchCriteria(inputQuery: CharSequence?) {
private var pid = -1
private var tag: String? = null
private val searchText: String
private var searchTextAsInt = -1
val isEmpty: Boolean
get() = pid == -1 && TextUtils.isEmpty(tag) && TextUtils.isEmpty(searchText)
fun matches(logLine: LogLine): Boolean {
// consider the criteria to be ANDed
if (!checkFoundPid(logLine)) {
return false
}
return if (!checkFoundTag(logLine)) {
false
} else checkFoundText(logLine)
}
private fun checkFoundText(logLine: LogLine): Boolean {
return (TextUtils.isEmpty(searchText)
|| searchTextAsInt != -1 && searchTextAsInt == logLine.processId
|| logLine.tag != null && containsIgnoreCase(logLine.tag, searchText)
|| logLine.logOutput != null && containsIgnoreCase(
logLine.logOutput,
searchText
))
}
private fun checkFoundTag(logLine: LogLine): Boolean {
return (TextUtils.isEmpty(tag)
|| logLine.tag != null && containsIgnoreCase(logLine.tag, tag))
}
private fun checkFoundPid(logLine: LogLine): Boolean {
return pid == -1 || logLine.processId == pid
}
fun nullToEmpty(str: CharSequence?): String {
return str?.toString() ?: ""
}
/**
* same as String.contains, but ignores case.
*
* @param str
* @param query
* @return
*/
fun containsIgnoreCase(str: String?, query: String?): Boolean {
if (str != null && query != null) {
val limit = str.length - query.length + 1
for (i in 0 until limit) {
if (matchesIgnoreCase(str, query, i)) {
return true
}
}
}
return false
}
private fun matchesIgnoreCase(str: String, query: String, startingAt: Int): Boolean {
val len = query.length
for (i in 0 until len) {
if (Character.toUpperCase(query[i]) != Character.toUpperCase(str[startingAt + i])) {
return false
}
}
return true
}
companion object {
private const val PID_KEYWORD = "pid:"
private const val TAG_KEYWORD = "tag:"
private val PID_PATTERN = Pattern.compile("$PID_KEYWORD:(\\d+)", Pattern.CASE_INSENSITIVE)
private val TAG_PATTERN = Pattern.compile("$TAG_KEYWORD:(\"[^\"]+\"|\\S+)", Pattern.CASE_INSENSITIVE)
}
init {
// check for the "pid" keyword
val query = StringBuilder(nullToEmpty(inputQuery))
val pidMatcher = PID_PATTERN.matcher(query)
if (pidMatcher.find()) {
try {
pid = pidMatcher.group(1).toInt()
query.replace(pidMatcher.start(), pidMatcher.end(), "") // detach
// from
// search
// string
} catch (ignore: NumberFormatException) {
}
}
// check for the "tag" keyword
val tagMatcher = TAG_PATTERN.matcher(query)
if (tagMatcher.find()) {
tag = tagMatcher.group(1)
tag?.let {
if (it.startsWith("\"") && it.endsWith("\"")) {
tag = it.substring(1, it.length - 1) // detach quotes
}
}
query.replace(tagMatcher.start(), tagMatcher.end(), "") // detach
}
// everything else becomes a search term
searchText = query.toString().trim { it <= ' ' }
try {
searchTextAsInt = searchText.toInt()
} catch (ignore: NumberFormatException) {
}
}
}

View File

@@ -0,0 +1,68 @@
package com.mogo.eagle.core.function.hmi.ui.utils
import android.content.Context
import android.util.Log
import android.util.SparseIntArray
import androidx.core.content.ContextCompat
import com.mogo.eagle.core.function.hmi.R
object TagColorUtil {
private val TEXT_COLOR = SparseIntArray(6)
private val TEXT_COLOR_EXPAND = SparseIntArray(6)
private val LEVEL_COLOR = SparseIntArray(6)
private val LEVEL_BG_COLOR = SparseIntArray(6)
@JvmStatic
fun getTextColor(context: Context?, level: Int, expand: Boolean): Int {
val map = if (expand) TEXT_COLOR_EXPAND else TEXT_COLOR
var result = map[level]
if (result == null) {
result = map[Log.VERBOSE]
}
return ContextCompat.getColor(context!!, result)
}
@JvmStatic
fun getLevelBgColor(context: Context?, level: Int): Int {
var result = LEVEL_BG_COLOR[level]
if (result == null) {
result = LEVEL_BG_COLOR[Log.VERBOSE]
}
return ContextCompat.getColor(context!!, result)
}
@JvmStatic
fun getLevelColor(context: Context?, level: Int): Int {
var result = LEVEL_COLOR[level]
if (result == null) {
result = LEVEL_COLOR[Log.VERBOSE]
}
return ContextCompat.getColor(context!!, result)
}
init {
TEXT_COLOR.put(Log.DEBUG, R.color.color_000000)
TEXT_COLOR.put(Log.INFO, R.color.color_000000)
TEXT_COLOR.put(Log.VERBOSE, R.color.color_000000)
TEXT_COLOR.put(Log.ASSERT, R.color.color_8F0005)
TEXT_COLOR.put(Log.ERROR, R.color.color_FF0006)
TEXT_COLOR.put(Log.WARN, R.color.color_0099dd)
TEXT_COLOR_EXPAND.put(Log.DEBUG, R.color.color_FFFFFF)
TEXT_COLOR_EXPAND.put(Log.INFO, R.color.color_FFFFFF)
TEXT_COLOR_EXPAND.put(Log.VERBOSE, R.color.color_FFFFFF)
TEXT_COLOR_EXPAND.put(Log.ASSERT, R.color.color_8F0005)
TEXT_COLOR_EXPAND.put(Log.ERROR, R.color.color_FF0006)
TEXT_COLOR_EXPAND.put(Log.WARN, R.color.color_0099dd)
LEVEL_BG_COLOR.put(Log.DEBUG, R.color.background_debug)
LEVEL_BG_COLOR.put(Log.ERROR, R.color.background_error)
LEVEL_BG_COLOR.put(Log.INFO, R.color.background_info)
LEVEL_BG_COLOR.put(Log.VERBOSE, R.color.background_verbose)
LEVEL_BG_COLOR.put(Log.WARN, R.color.background_warn)
LEVEL_BG_COLOR.put(Log.ASSERT, R.color.background_wtf)
LEVEL_COLOR.put(Log.DEBUG, R.color.foreground_debug)
LEVEL_COLOR.put(Log.ERROR, R.color.foreground_error)
LEVEL_COLOR.put(Log.INFO, R.color.foreground_info)
LEVEL_COLOR.put(Log.VERBOSE, R.color.foreground_verbose)
LEVEL_COLOR.put(Log.WARN, R.color.foreground_warn)
LEVEL_COLOR.put(Log.ASSERT, R.color.foreground_wtf)
}
}

View File

@@ -7,7 +7,6 @@ import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
@@ -16,8 +15,8 @@ import com.mogo.eagle.core.function.call.hmi.CallerHmiListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.util.LogUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.module.common.MogoApisHandler
import com.mogo.utils.UiThreadHandler
import kotlinx.android.synthetic.main.view_autopilot_status.view.*
/**
@@ -26,11 +25,11 @@ import kotlinx.android.synthetic.main.view_autopilot_status.view.*
* 自动驾驶状态按钮
*/
class AutoPilotStatusView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet
context: Context,
attrs: AttributeSet
) : ConstraintLayout(context, attrs),
View.OnClickListener,
IMoGoAutopilotStatusListener {
View.OnClickListener,
IMoGoAutopilotStatusListener {
private val TAG = "AutopilotStatusView"
@@ -80,14 +79,17 @@ class AutoPilotStatusView @JvmOverloads constructor(
private fun startAutoPilot() {
// TODO 测试数据,真实情况需要业务自己传入控制数据
val currentAutopilot =
AutopilotControlParameters()
AutopilotControlParameters()
currentAutopilot.startName = "HYKXC"
currentAutopilot.endName = "HYJC"
currentAutopilot.isSpeakVoice = false
currentAutopilot.startLatLon =
AutopilotControlParameters.AutoPilotLonLat(MogoApisHandler.getInstance().apis.adasControllerApi.lastLat, MogoApisHandler.getInstance().apis.adasControllerApi.lastLon)
AutopilotControlParameters.AutoPilotLonLat(
MogoApisHandler.getInstance().apis.adasControllerApi.lastLat,
MogoApisHandler.getInstance().apis.adasControllerApi.lastLon
)
currentAutopilot.endLatLon =
AutopilotControlParameters.AutoPilotLonLat(26.819716071924688, 112.57715442110867)
AutopilotControlParameters.AutoPilotLonLat(26.819716071924688, 112.57715442110867)
currentAutopilot.vehicleType = 10
CallerAutoPilotManager.startAutoPilot(currentAutopilot)
@@ -126,11 +128,6 @@ class AutoPilotStatusView @JvmOverloads constructor(
setAutoPilotStatus(autoPilotStatusInfo.state)
}
override fun onAutopilotArriveAtStation(autopilotWayArrive: AutopilotStationInfo?) {
}
override fun onAutopilotGuardian(guardianInfo: AutopilotGuardianStatusInfo?) {
}

View File

@@ -0,0 +1,117 @@
package com.mogo.eagle.core.function.hmi.ui.widget
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.data.autopilot.AdUpgradeStateHelper
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.notification.WarningFloat
import com.mogo.eagle.core.function.hmi.ui.tools.DockerRebootDialog
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.ToastUtils
import kotlinx.android.synthetic.main.view_check_system.view.*
class CheckSystemView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoAutopilotStatusListener {
companion object {
const val TAG = "CheckSystemView"
}
private var connectStatus = false //是否连接工控机
private var autopilotStatus:Int ?=null //自动驾驶状态 0代表不可自动驾驶,1代表可自动驾驶,2代表自动驾驶中
private var dockerRebootDialog: DockerRebootDialog? = null
private var downloadStatus: Int=-1 //下载状态
private var upgradeStatus: Int=-1 //升级状态
init {
LayoutInflater.from(context).inflate(R.layout.view_check_system, this, true)
initView()
}
private fun initView() {
viewCheckShutDown.setOnClickListener {
//dialog
// showSystemOperationWindow()
}
viewCheckReboot.setOnClickListener {
//dialog
if(dockerRebootDialog == null){
dockerRebootDialog = DockerRebootDialog(context)
dockerRebootDialog?.setClickListener(object : DockerRebootDialog.ClickListener{
override fun confirm() {
if(autopilotStatus==2){
//当前处于自动驾驶状态不可进行重启Toast提示
ToastUtils.showShort("请先退出自动驾驶状态")
}else if(AdUpgradeStateHelper.showCannotReboot(downloadStatus, upgradeStatus)){
//当工控机处于下载或者升级状态,需要先进行升级
ToastUtils.showShort("请先完成新自动驾驶系统的下载/升级")
} else{
//确认重启
Logger.i(TAG,"reboot confirm")
CallerAutoPilotManager.setIPCReboot()
}
}
override fun cancel() {
//取消重启
Logger.i(TAG,"reboot cancel")
}
})
}
dockerRebootDialog?.showUpgradeDialog()
}
}
fun setAdUpgradeStatus(downloadStatus : Int,upgradeStatus : Int){
this.downloadStatus=downloadStatus
this.upgradeStatus=upgradeStatus
}
private fun showSystemOperationWindow(view: View) {
WarningFloat.with(context).setGravity(Gravity.CENTER).setLayout(view)
.setImmersionStatusBar(true).show()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerAutoPilotStatusListenerManager.removeListener(TAG)
}
override fun onAutopilotStatusResponse(autoPilotStatusInfo: AutopilotStatusInfo) {
connectStatus = autoPilotStatusInfo.connectStatus
autopilotStatus = autoPilotStatusInfo.state
setViewStatus()
}
private fun setViewStatus() {
if (connectStatus) {
viewCheckShutDown.requestFocus()
viewCheckShutDown.isClickable = true
viewCheckReboot.requestFocus()
viewCheckReboot.isClickable = true
} else {
viewCheckShutDown.isClickable = false
viewCheckReboot.isClickable = false
}
}
}

View File

@@ -0,0 +1,191 @@
package com.mogo.eagle.core.function.hmi.ui.widget
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.TypedArray
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.OvershootInterpolator
import androidx.core.content.ContextCompat
import com.mogo.eagle.core.function.hmi.R
/**
* @author XuXinChao
* @description 自定义圆形进度条
* @since: 2022/1/14
*/
class CircularProgressView @JvmOverloads constructor(
context: Context, attrs: AttributeSet?, defStyleAttr : Int)
: View(context, attrs, defStyleAttr){
val typedArray : TypedArray = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressView)
// 绘制画笔
private val mBackPaint : Paint = Paint()
private val mProgPaint : Paint = Paint()
// 绘制区域
private var mRectF : RectF? = null
// 圆环渐变色
private var mColorArray : IntArray?=null
// 圆环进度(0-100) 初始化进度
private var mProgress : Int = typedArray.getInteger(R.styleable.CircularProgressView_progress, 0)
constructor(context : Context) : this(context,null)
constructor(context : Context,attrs : AttributeSet?) :this(context, attrs, 0)
init {
// 初始化背景圆环画笔
mBackPaint.style = Paint.Style.STROKE // 只描边,不填充
mBackPaint.strokeCap = Paint.Cap.ROUND // 设置圆角
mBackPaint.isAntiAlias = true // 设置抗锯齿
mBackPaint.isDither = true // 设置抖动
mBackPaint.strokeWidth = typedArray.getDimension(R.styleable.CircularProgressView_backWidth, 5.0f)
mBackPaint.color = typedArray.getColor(R.styleable.CircularProgressView_backColor, Color.LTGRAY)
// 初始化进度圆环画笔
mProgPaint.style = Paint.Style.STROKE // 只描边,不填充
mProgPaint.strokeCap = Paint.Cap.ROUND // 设置圆角
mProgPaint.isAntiAlias = true // 设置抗锯齿
mProgPaint.isDither = true // 设置抖动
mProgPaint.strokeWidth = typedArray.getDimension(R.styleable.CircularProgressView_progWidth, 10.0f)
mProgPaint.color = typedArray.getColor(R.styleable.CircularProgressView_progColor, Color.BLUE)
// 初始化进度圆环渐变色
val startColor = typedArray.getColor(R.styleable.CircularProgressView_progStartColor, -1)
val firstColor = typedArray.getColor(R.styleable.CircularProgressView_progFirstColor, -1)
if(startColor != -1 && firstColor != -1){
mColorArray = intArrayOf(startColor,firstColor)
}else{
mColorArray = null
}
typedArray.recycle();
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val viewWide = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
val viewHigh = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
val mRectLength =
((if (viewWide > viewHigh) viewHigh else viewWide) - if (mBackPaint.strokeWidth > mProgPaint.strokeWidth) mBackPaint.strokeWidth else mProgPaint.strokeWidth).toInt()
val mRectL = getPaddingLeft() + (viewWide - mRectLength) / 2
val mRectT = getPaddingTop() + (viewHigh - mRectLength) / 2
mRectF = RectF(mRectL.toFloat(), mRectT.toFloat(), (mRectL + mRectLength).toFloat(),
(mRectT + mRectLength).toFloat())
// 设置进度圆环渐变色
mColorArray?.let {
mProgPaint.shader = LinearGradient(
0.0f, 0.0f, 0.0f,
measuredWidth.toFloat(), it, null, Shader.TileMode.MIRROR)
}
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.let {
mRectF?.let { it1 -> it.drawArc(it1, 0.0f, 360.0f, false, mBackPaint) }
mRectF?.let { it1 -> it.drawArc(it1, 275.0f,
(360 * mProgress / 100).toFloat(), false, mProgPaint) }
}
}
/**
* 获取当前进度
* @return 当前进度0-100
*/
fun getProgress() : Int{
return mProgress
}
/**
* 设置当前进度
* @param progress 当前进度0-100
*/
fun setProgress(progress : Int){
mProgress = progress
invalidate()
}
/**
* 设置当前进度并展示进度动画。如果动画时间小于等于0则不展示动画
* @param progress 当前进度0-100
* @param animTime 动画时间(毫秒)
*/
fun setProgress(progress : Int, animTime : Long){
if (animTime<=0){
setProgress(progress)
} else{
val animator = ValueAnimator.ofInt(mProgress, progress)
animator.addUpdateListener{
mProgress = it.animatedValue as Int
invalidate()
}
animator.interpolator = OvershootInterpolator()
animator.duration = animTime
animator.start()
}
}
/**
* 设置背景圆环宽度
* @param width 背景圆环宽度
*/
fun setBackWidth(width : Int){
mBackPaint.strokeWidth = width.toFloat()
invalidate()
}
/**
* 设置背景圆环颜色
* @param color 背景圆环颜色
*/
fun setBackColor(color : Int){
mBackPaint.color = ContextCompat.getColor(context,color)
invalidate()
}
/**
* 设置进度圆环宽度
* @param width 进度圆环宽度
*/
fun setProgWidth(width : Int){
mProgPaint.strokeWidth = width.toFloat()
invalidate()
}
/**
* 设置进度圆环颜色
* @param color 景圆环颜色
*/
fun setProgColor(color : Int){
mProgPaint.color = ContextCompat.getColor(context,color)
mProgPaint.shader = null
invalidate()
}
fun setProgColor(startColor : Int,endColor: Int){
mColorArray = intArrayOf(ContextCompat.getColor(context,startColor),ContextCompat.getColor(context,endColor))
mColorArray?.let {
mProgPaint.shader = LinearGradient(0f, 0f, 0f,
getMeasuredWidth().toFloat(), it, null, Shader.TileMode.MIRROR)
}
}
fun setProgColor(colorArray : IntArray){
colorArray.let {
if(it.size<2){
return
}
mColorArray = it.copyOf()
mColorArray?.let{
mProgPaint.shader = LinearGradient(0f, 0f, 0f,
getMeasuredWidth().toFloat(), it, null, Shader.TileMode.MIRROR)
}
}
}
}

View File

@@ -1,28 +1,19 @@
package com.mogo.eagle.core.function.hmi.ui.widget
import android.animation.Animator
import android.content.Context
import android.graphics.Color
import android.location.Location
import android.util.AttributeSet
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.view.animation.OvershootInterpolator
import android.widget.FrameLayout
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.data.map.MogoLatLng
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.notification.WarningFloat
import com.mogo.eagle.core.function.hmi.notification.anim.DefaultAnimator
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
import com.mogo.eagle.core.function.hmi.ui.setting.DebugSettingView
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.map.navi.IMogoCarLocationChangedListener2
import com.mogo.service.IMogoServiceApis
import com.mogo.service.statusmanager.StatusDescriptor
import com.mogo.utils.UiThreadHandler
import java.util.*
/**
@@ -63,18 +54,24 @@ class SpeedPanelView @JvmOverloads constructor(
}
private fun initEvent(context: Context) {
setOnLongClickListener {
Logger.d(TAG, "长按显示状态工具栏")
CallerHmiManager.toggleDebugView()
false
}
}
private val timerTask = object : TimerTask() {
override fun run() {
if (mLatLng != null) {
mSpeedLimmit = mMogoServiceApis.mapServiceApi.mapUIController.getSpeedLimmit(mLatLng!!.longitude, mLatLng!!.latitude, mLatLng!!.bearing)
UiThreadHandler.post {
val speed = (mLatLng!!.speed * 3.6f).toInt()
mSpeedChartView.setArcColor(Color.parseColor(if (speed > mSpeedLimmit) "#DB3137" else "#3E77F6"))
mSpeedChartView.setValues(speed)
setBackgroundResource(if (speed > mSpeedLimmit) R.drawable.yi_biao_pan_bg_speeding else R.drawable.yi_biao_pan_bg_nor)
if (mMogoServiceApis.mapServiceApi != null && mMogoServiceApis.mapServiceApi.mapUIController != null) {
mSpeedLimmit = mMogoServiceApis.mapServiceApi.mapUIController.getSpeedLimmit(mLatLng!!.longitude, mLatLng!!.latitude, mLatLng!!.bearing)
UiThreadHandler.post {
val speed = (mLatLng!!.speed * 3.6f).toInt()
mSpeedChartView.setArcColor(Color.parseColor(if (speed > mSpeedLimmit) "#DB3137" else "#3E77F6"))
mSpeedChartView.setValues(speed)
setBackgroundResource(if (speed > mSpeedLimmit) R.drawable.yi_biao_pan_bg_speeding else R.drawable.yi_biao_pan_bg_nor)
}
}
}
}
@@ -103,18 +100,8 @@ class SpeedPanelView @JvmOverloads constructor(
}
override fun onCarLocationChanged(latLng: MogoLatLng?) {
}
override fun onCarLocationChanged2(latLng: Location) {
mLatLng = latLng
// UiThreadHandler.post {
// val speed = (latLng.speed * 3.6f).toInt()
// mSpeedChartView.setArcColor(Color.parseColor(if (speed > 60) "#DB3137" else "#3E77F6"))
// mSpeedChartView.setValues(speed)
// setBackgroundResource(if (speed > 60) R.drawable.yi_biao_pan_bg_speeding else R.drawable.yi_biao_pan_bg_nor)
// }
}
}

View File

@@ -0,0 +1,243 @@
package com.mogo.eagle.core.function.hmi.ui.widget
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.data.autopilot.AdUpgradeStateHelper
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.tools.AdUpgradeDialog
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.AppUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.view_system_version.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
/**
* @author XuXinChao
* @description 工具箱-系统版本(鹰眼版本、工控机版本)视图
* @since: 2022/1/13
*/
class SystemVersionView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoAutopilotStatusListener {
companion object {
const val TAG = "SystemVersionView"
}
private var connectStatus = false
private var dockerVersion :String ?=null //工控机版本
private var autopilotStatus:Int ?=null //自动驾驶状态 0代表不可自动驾驶,1代表可自动驾驶,2代表自动驾驶中
private var adUpgradeDialog : AdUpgradeDialog? = null
private var upgradeMode: Int=-1 //升级模式
private var downloadStatus: Int=-1 //下载状态
private var currentProgress: Int=-1 //当前已下载包体大小
private var previousProgress: Int=-1 //前一秒的下载进度,用于计算下载剩余时间
private var totalProgress: Int=-1 //包体总大小
private var downloadVersion: String?=null //工控机docker版本
private var upgradeStatus: Int=-1 //升级状态
init {
LayoutInflater.from(context).inflate(R.layout.view_system_version, this, true)
initView()
}
private fun initView(){
showCurrentPadVersion()
showCurrentAdVersion()
//鹰眼版本视图点击事件
ivPadVersion.setOnClickListener {
Logger.i(TAG,"pad version view clicked")
}
//工控机版本视图点击事件
ivAdVersion.setOnClickListener {
Logger.i(TAG,"ad version view clicked")
Logger.i(TAG,"upgradeMode="+upgradeMode+" downloadStatus="+downloadStatus+" upgradeStatus="+upgradeStatus)
if(AdUpgradeStateHelper.isDownloading(downloadStatus)){
//点击Toast提示下载剩余时间
ToastUtils.showShort("预计"+AdUpgradeStateHelper.getRemainingTime(totalProgress,previousProgress,currentProgress)+"下载完成")
}else if(AdUpgradeStateHelper.isHintUpgradeMode(upgradeMode) && AdUpgradeStateHelper.isDownloadFinish(downloadStatus,upgradeStatus)){
//如果升级模式为“提示升级”,并且下载状态为已经下载完成,点击弹出升级确认弹窗
if(adUpgradeDialog == null){
adUpgradeDialog = AdUpgradeDialog(context)
adUpgradeDialog?.setClickListener(object : AdUpgradeDialog.ClickListener{
override fun confirm() {
if(autopilotStatus==2){
//当前处于自动驾驶状态不可进行升级Toast提示
ToastUtils.showShort("升级前请先退出自动驾驶模式")
}else{
//确认升级
Logger.i(TAG,"upgrade confirm")
//设置当前状态为“升级中”
AdUpgradeStateHelper.setUpgradeStatus(true)
CallerAutoPilotManager.setIPCUpgradeAffirm()
}
}
override fun cancel() {
//取消升级
Logger.i(TAG,"upgrade cancel")
//取消升级命令不下发
// CallerAutoPilotManager.setIPCUpgradeCancel()
}
})
}
adUpgradeDialog?.showUpgradeDialog()
}else if(AdUpgradeStateHelper.getUpgradeStatus()){
//工控机状态为“升级中”
ToastUtils.showShort("新版本升级中预计5分钟升级完成")
}else if(AdUpgradeStateHelper.isUpgradeFailed(upgradeStatus)){
//如果升级失败则Toast提示升级失败请联系运维人员
ToastUtils.showShort("升级失败,请联系运维人员")
}
}
}
/**
* 设置工控机下载、升级状态信息
* @param upgradeMode 升级模式(提示升级、静默升级)
* @param downloadStatus 下载状态
* @param currentProgress 当前已经下载包体大小
* @param totalProgress 下载包体总大小
* @param downloadVersion 下载版本
* @param upgradeStatus 升级状态
*/
fun setAdUpgradeInfo(upgradeMode: Int,downloadStatus: Int,currentProgress: Int,totalProgress: Int,
downloadVersion: String,upgradeStatus: Int){
this.upgradeMode=upgradeMode
this.downloadStatus=downloadStatus
this.previousProgress=this.currentProgress
this.currentProgress=currentProgress
this.totalProgress=totalProgress
this.downloadVersion=downloadVersion
this.upgradeStatus=upgradeStatus
}
/**
* 展示工控机下载、升级状态信息
* @param upgradeMode 升级模式(提示升级、静默升级)
* @param downloadStatus 下载状态
* @param currentProgress 当前已经下载包体大小
* @param totalProgress 下载包体总大小
* @param downloadVersion 下载版本
* @param upgradeStatus 升级状态
*/
fun showAdUpgradeStatus(upgradeMode: Int,downloadStatus: Int,currentProgress: Int,totalProgress: Int
,downloadVersion: String,upgradeStatus: Int){
GlobalScope.launch(Dispatchers.Main){
//设置工控机下载、升级状态信息
setAdUpgradeInfo(upgradeMode, downloadStatus, currentProgress, totalProgress, downloadVersion, upgradeStatus)
if(AdUpgradeStateHelper.isDownloading(downloadStatus)){
//正在下载,展示“下载中”角标,展示进度条,并设置当前下载进度
ivAdStatus?.setImageResource(R.drawable.icon_downloading)
adCircularProgressView?.let {
it.visibility = View.VISIBLE
Logger.i(TAG,"下载中="+"currentProgress="+currentProgress+" totalProgress="+totalProgress+" downloadProgress="+AdUpgradeStateHelper.downloadProgress(currentProgress,totalProgress))
it.setProgress(AdUpgradeStateHelper.downloadProgress(currentProgress,totalProgress))
}
}else if(AdUpgradeStateHelper.isDownloadFinish(downloadStatus,upgradeStatus)){
//下载完成处于可升级状态展示“可升级”角标将AD背景变为蓝色并隐藏下载进度条
ivAdStatus?.setImageResource(R.drawable.icon_upgradeable)
ivAdVersion?.setBackgroundResource(R.drawable.version_upgradeable_background)
adCircularProgressView?.visibility = View.GONE
if(AdUpgradeStateHelper.isQuietUpgradeMode(upgradeMode)){
//如果升级模式为“静默升级”,则下载完成后,调用升级命令进行升级
CallerAutoPilotManager.setIPCUpgradeAffirm()
AdUpgradeStateHelper.setUpgradeStatus(true)
}
}else if(AdUpgradeStateHelper.isDownloadFailed(downloadStatus)){
//下载失败,将状态设为“最新版”角标,并隐藏进度条
ivAdStatus?.setImageResource(R.drawable.icon_latest_version)
adCircularProgressView?.visibility = View.GONE
ivAdVersion?.setBackgroundResource(R.drawable.version_latest_background)
} else if(AdUpgradeStateHelper.isUpgradeSuccess(upgradeStatus)){
//升级成功,将状态设为“最新版”角标,并隐藏进度条
ivAdStatus?.setImageResource(R.drawable.icon_latest_version)
adCircularProgressView?.visibility = View.GONE
AdUpgradeStateHelper.setUpgradeStatus(false)
ivAdVersion?.setBackgroundResource(R.drawable.version_latest_background)
}else if(AdUpgradeStateHelper.isUpgradeFailed(upgradeStatus)){
//升级失败,将状态设为“升级失败”角标,并隐藏进度条
ivAdStatus?.setImageResource(R.drawable.icon_upgrade_failed)
adCircularProgressView?.visibility = View.GONE
AdUpgradeStateHelper.setUpgradeStatus(false)
ivAdVersion?.setBackgroundResource(R.drawable.version_latest_background)
}else{
//其他状态,均显示“最新版”,并隐藏进度条
ivAdStatus?.setImageResource(R.drawable.icon_latest_version)
adCircularProgressView?.visibility = View.GONE
ivAdVersion?.setBackgroundResource(R.drawable.version_latest_background)
}
}
}
/**
* 展示当前鹰眼版本
*/
private fun showCurrentPadVersion(){
tvPadVersionContent?.let {
it.text = AppUtils.getAppVersionName()
}
}
/**
* 展示当前工控机版本
*/
private fun showCurrentAdVersion(){
UiThreadHandler.post {
tvAdVersionContent?.let {
// it.text = AdasManager.getInstance().getAdasConfig().getDockVersion())
if(!dockerVersion.isNullOrEmpty()){
it.text = dockerVersion
}
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerAutoPilotStatusListenerManager.removeListener(TAG)
}
override fun onAutopilotStatusResponse(autoPilotStatusInfo: AutopilotStatusInfo) {
connectStatus = autoPilotStatusInfo.connectStatus
dockerVersion = autoPilotStatusInfo.dockVersion
autopilotStatus = autoPilotStatusInfo.state
Logger.i(TAG,"onAutopilotStatusResponse connectStatus="+connectStatus+" dockerVersion="+dockerVersion+" autopilotStatus="+autopilotStatus)
setViewStatus()
}
private fun setViewStatus(){
showCurrentAdVersion()
}
}

View File

@@ -6,7 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import com.mogo.utils.UiThreadHandler
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.view_traffic_light_vr.view.*
/**

View File

@@ -9,7 +9,7 @@ import androidx.annotation.Nullable
import androidx.annotation.StringRes
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import com.mogo.utils.UiThreadHandler
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.notification_v2x_msg_vr.view.*
/**
@@ -39,7 +39,7 @@ class V2XNotificationView @JvmOverloads constructor(
}
}
fun setWarningContent(@Nullable warningContent: String) {
fun setWarningContent(@Nullable warningContent: CharSequence) {
UiThreadHandler.post {
tvWaringContent.text = warningContent
}

View File

@@ -2,13 +2,24 @@ package com.mogo.eagle.core.function.hmi.ui.widget
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.RelativeLayout
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_ALL
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_BOTTOM
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_BOTTOM_LEFT
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_BOTTOM_RIGHT
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_LEFT
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_NON
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_RIGHT
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_TOP
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_TOP_LEFT
import com.mogo.eagle.core.data.enums.WarningDirectionEnum.ALERT_WARNING_TOP_RIGHT
import com.mogo.eagle.core.function.hmi.R
import com.mogo.utils.UiThreadHandler
import com.mogo.utils.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.module_hmi_warning_v2x.view.*
/**
@@ -25,7 +36,7 @@ class V2XWarningView @JvmOverloads constructor(
private val closeWarningTask: Runnable = Runnable {
Logger.d("V2XWarningView", "预警红边:倒计时结束")
showWarning(WarningDirectionEnum.ALERT_WARNING_NON)
showWarning(ALERT_WARNING_NON)
}
init {
@@ -52,67 +63,67 @@ class V2XWarningView @JvmOverloads constructor(
UiThreadHandler.post {
// 如果传入的不是关闭显示,则设置倒计时,定时关闭红框警示
if (direction != WarningDirectionEnum.ALERT_WARNING_NON) {
if (direction != ALERT_WARNING_NON) {
removeCallbacks(closeWarningTask)
postDelayed(closeWarningTask, closeTime)
}
when (direction) {
WarningDirectionEnum.ALERT_WARNING_NON -> {
ALERT_WARNING_NON -> {
removeCallbacks(closeWarningTask)
hmiWarningTopImg.visibility = View.GONE
hmiWarningRightImg.visibility = View.GONE
hmiWarningBottomImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.GONE
}
WarningDirectionEnum.ALERT_WARNING_TOP -> {
ALERT_WARNING_TOP -> {
hmiWarningTopImg.visibility = View.VISIBLE
hmiWarningRightImg.visibility = View.GONE
hmiWarningBottomImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.GONE
}
WarningDirectionEnum.ALERT_WARNING_RIGHT -> {
ALERT_WARNING_RIGHT -> {
hmiWarningTopImg.visibility = View.GONE
hmiWarningRightImg.visibility = View.VISIBLE
hmiWarningBottomImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.GONE
}
WarningDirectionEnum.ALERT_WARNING_BOTTOM -> {
ALERT_WARNING_BOTTOM -> {
hmiWarningTopImg.visibility = View.GONE
hmiWarningRightImg.visibility = View.GONE
hmiWarningBottomImg.visibility = View.VISIBLE
hmiWarningLeftImg.visibility = View.GONE
}
WarningDirectionEnum.ALERT_WARNING_LEFT -> {
ALERT_WARNING_LEFT -> {
hmiWarningTopImg.visibility = View.GONE
hmiWarningRightImg.visibility = View.GONE
hmiWarningBottomImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.VISIBLE
}
WarningDirectionEnum.ALERT_WARNING_TOP_RIGHT -> {
ALERT_WARNING_TOP_RIGHT -> {
hmiWarningTopImg.visibility = View.VISIBLE
hmiWarningRightImg.visibility = View.VISIBLE
hmiWarningBottomImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.GONE
}
WarningDirectionEnum.ALERT_WARNING_BOTTOM_RIGHT -> {
ALERT_WARNING_BOTTOM_RIGHT -> {
hmiWarningTopImg.visibility = View.GONE
hmiWarningRightImg.visibility = View.VISIBLE
hmiWarningBottomImg.visibility = View.VISIBLE
hmiWarningLeftImg.visibility = View.GONE
}
WarningDirectionEnum.ALERT_WARNING_BOTTOM_LEFT -> {
ALERT_WARNING_BOTTOM_LEFT -> {
hmiWarningTopImg.visibility = View.GONE
hmiWarningRightImg.visibility = View.GONE
hmiWarningBottomImg.visibility = View.VISIBLE
hmiWarningLeftImg.visibility = View.VISIBLE
}
WarningDirectionEnum.ALERT_WARNING_TOP_LEFT -> {
ALERT_WARNING_TOP_LEFT -> {
hmiWarningTopImg.visibility = View.VISIBLE
hmiWarningRightImg.visibility = View.GONE
hmiWarningBottomImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.VISIBLE
}
WarningDirectionEnum.ALERT_WARNING_ALL -> {
ALERT_WARNING_ALL -> {
hmiWarningTopImg.visibility = View.VISIBLE
hmiWarningRightImg.visibility = View.VISIBLE
hmiWarningBottomImg.visibility = View.VISIBLE
@@ -121,4 +132,50 @@ class V2XWarningView @JvmOverloads constructor(
}
}
}
fun dismissWarning(direction: WarningDirectionEnum) {
if (direction == ALERT_WARNING_NON) {
return
}
removeCallbacks(closeWarningTask)
when (direction) {
ALERT_WARNING_TOP -> {
hmiWarningTopImg.visibility = View.GONE
}
ALERT_WARNING_RIGHT -> {
hmiWarningRightImg.visibility = View.GONE
}
ALERT_WARNING_BOTTOM -> {
hmiWarningBottomImg.visibility = View.GONE
}
ALERT_WARNING_LEFT -> {
hmiWarningLeftImg.visibility = View.GONE
}
ALERT_WARNING_TOP_RIGHT -> {
hmiWarningTopImg.visibility = View.GONE
hmiWarningRightImg.visibility = View.GONE
}
ALERT_WARNING_BOTTOM_RIGHT -> {
hmiWarningRightImg.visibility = View.GONE
hmiWarningBottomImg.visibility = View.GONE
}
ALERT_WARNING_BOTTOM_LEFT -> {
hmiWarningBottomImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.GONE
}
ALERT_WARNING_TOP_LEFT -> {
hmiWarningTopImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.GONE
}
ALERT_WARNING_ALL -> {
hmiWarningTopImg.visibility = View.GONE
hmiWarningRightImg.visibility = View.GONE
hmiWarningBottomImg.visibility = View.GONE
hmiWarningLeftImg.visibility = View.GONE
}
else -> {
Log.d("XXX", "Not Support Direction")
}
}
}
}

View File

@@ -5,7 +5,7 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import com.mogo.utils.UiThreadHandler
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.view_vip_identification.view.*
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:width="2dp"/>
<solid android:color="#CCCCCC"/>
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="#FFFFFF" />
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_checked_background" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_normal_background" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_checked_background_left" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_normal_background_left" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_checked_background_middle" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_normal_background_middle" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_checked_background_right" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_normal_background_right" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#337CC4"/>
<stroke
android:width="1px"
android:color="#337CC4"/>
<padding
android:bottom="1px"
android:left="1px"
android:right="1px"
android:top="1px"/>
</shape>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#337CC4" />
<stroke
android:width="1px"
android:color="#337CC4" />
<padding
android:bottom="1px"
android:left="1px"
android:right="1px"
android:top="1px" />
<corners android:topLeftRadius="4dp" android:bottomLeftRadius="4dp" />
</shape>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#337CC4" />
<stroke
android:width="1px"
android:color="#337CC4" />
<padding
android:bottom="1px"
android:left="1px"
android:right="1px"
android:top="1px" />
</shape>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#337CC4" />
<stroke
android:width="1px"
android:color="#337CC4" />
<padding
android:bottom="1px"
android:left="1px"
android:right="1px"
android:top="1px" />
<corners android:topRightRadius="4dp" android:bottomRightRadius="4dp" />
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="1px"
android:color="#337CC4" />
<padding
android:bottom="1px"
android:left="1px"
android:right="1px"
android:top="1px" />
</shape>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="1px"
android:color="#337CC4" />
<padding
android:bottom="1px"
android:left="1px"
android:right="1px"
android:top="1px" />
<corners android:topLeftRadius="4dp" android:bottomLeftRadius="4dp" />
</shape>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="1px"
android:color="#337CC4" />
<padding
android:bottom="1px"
android:left="1px"
android:right="1px"
android:top="1px" />
</shape>

Some files were not shown because too many files have changed in this diff Show More