Merge branch 'dev_robotaxi-d-app-module_251_220125_2.5.1'
# Conflicts: # modules.txt
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 办公室调试使用,强制开启自动驾驶,将 status,pilotmode,control_pilotmode,强追设置为 1
|
||||
* isEnable = true 开启
|
||||
* isEnable = false 关闭
|
||||
*/
|
||||
override fun setControlAutopilotCarAuto(isEnable: Boolean) {
|
||||
if (isEnable) {
|
||||
AdasManager.getInstance().controlAutopilotCarAuto()
|
||||
} else {
|
||||
AdasManager.getInstance().controlAutopilotCarHead()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
* 用于普通云公告的测试
|
||||
|
||||
@@ -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) //设置刹车信息
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式 控制限速标志
|
||||
|
||||
@@ -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 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式 触发红绿灯场景
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui.logcatch
|
||||
|
||||
interface ILogViewListener {
|
||||
fun onAttach()
|
||||
fun onDetach()
|
||||
}
|
||||
@@ -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 +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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?) {
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.*
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.*
|
||||
|
||||
/**
|
||||
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 157 B |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||