levelList) {
+ this.levelList = levelList;
+ }
+
+ public static class LevelListBean {
+ /**
+ * level : NORMAL
+ * minMinute : 0
+ * maxMinute : 120
+ * content : 已连续驾驶2小时,请注意休息
+ * tts : 已连续驾驶2小时,听歌可以缓解疲劳,可以对我说打开音乐
+ * showSeconds : 15
+ * highSpeed : {"recommend":"REST_AREA","direction":"FRONT","angle":180,"distance":30}
+ * lowSpeed : {"recommend":"PARK","direction":"FRONT","angle":180,"distance":5}
+ * noReTts : 已连续驾驶3小时,为避免事故发生,请尽快进入停车区休息
+ */
+
+ private String level;
+ private int minMinute;
+ private int maxMinute;
+ private String content;
+ private String tts;
+ private int showSeconds;
+ private HighSpeedBean highSpeed;
+ private LowSpeedBean lowSpeed;
+ private String noReTts;
+
+ public String getLevel() {
+ return level;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public int getMinMinute() {
+ return minMinute;
+ }
+
+ public void setMinMinute(int minMinute) {
+ this.minMinute = minMinute;
+ }
+
+ public int getMaxMinute() {
+ return maxMinute;
+ }
+
+ public void setMaxMinute(int maxMinute) {
+ this.maxMinute = maxMinute;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public String getTts() {
+ return tts;
+ }
+
+ public void setTts(String tts) {
+ this.tts = tts;
+ }
+
+ public int getShowSeconds() {
+ return showSeconds;
+ }
+
+ public void setShowSeconds(int showSeconds) {
+ this.showSeconds = showSeconds;
+ }
+
+ public HighSpeedBean getHighSpeed() {
+ return highSpeed;
+ }
+
+ public void setHighSpeed(HighSpeedBean highSpeed) {
+ this.highSpeed = highSpeed;
+ }
+
+ public LowSpeedBean getLowSpeed() {
+ return lowSpeed;
+ }
+
+ public void setLowSpeed(LowSpeedBean lowSpeed) {
+ this.lowSpeed = lowSpeed;
+ }
+
+ public String getNoReTts() {
+ return noReTts;
+ }
+
+ public void setNoReTts(String noReTts) {
+ this.noReTts = noReTts;
+ }
+
+ public static class HighSpeedBean {
+ /**
+ * recommend : REST_AREA
+ * direction : FRONT
+ * angle : 180
+ * distance : 30
+ */
+
+ private String recommend;
+ private String direction;
+ private int angle;
+ private int distance;
+
+ public String getRecommend() {
+ return recommend;
+ }
+
+ public void setRecommend(String recommend) {
+ this.recommend = recommend;
+ }
+
+ public String getDirection() {
+ return direction;
+ }
+
+ public void setDirection(String direction) {
+ this.direction = direction;
+ }
+
+ public int getAngle() {
+ return angle;
+ }
+
+ public void setAngle(int angle) {
+ this.angle = angle;
+ }
+
+ public int getDistance() {
+ return distance;
+ }
+
+ public void setDistance(int distance) {
+ this.distance = distance;
+ }
+ }
+
+ public static class LowSpeedBean {
+ /**
+ * recommend : PARK
+ * direction : FRONT
+ * angle : 180
+ * distance : 5
+ */
+
+ private String recommend;
+ private String direction;
+ private int angle;
+ private int distance;
+
+ public String getRecommend() {
+ return recommend;
+ }
+
+ public void setRecommend(String recommend) {
+ this.recommend = recommend;
+ }
+
+ public String getDirection() {
+ return direction;
+ }
+
+ public void setDirection(String direction) {
+ this.direction = direction;
+ }
+
+ public int getAngle() {
+ return angle;
+ }
+
+ public void setAngle(int angle) {
+ this.angle = angle;
+ }
+
+ public int getDistance() {
+ return distance;
+ }
+
+ public void setDistance(int distance) {
+ this.distance = distance;
+ }
+ }
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/entity/net/V2XUserInfoRes.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/entity/net/V2XUserInfoRes.java
new file mode 100644
index 0000000000..bc5a567ec6
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/entity/net/V2XUserInfoRes.java
@@ -0,0 +1,344 @@
+
+package com.mogo.module.v2x.entity.net;
+
+import com.google.gson.annotations.Expose;
+import com.mogo.commons.data.BaseData;
+
+import java.io.Serializable;
+
+public class V2XUserInfoRes extends BaseData implements Serializable {
+
+ @Expose
+ private Result result;
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public Result getResult() {
+ return result;
+ }
+
+ public void setResult(Result result) {
+ this.result = result;
+ }
+
+ public class Result {
+
+ @Expose
+ private Info info;
+
+ public Info getInfo() {
+ return info;
+ }
+
+ public void setInfo(Info info) {
+ this.info = info;
+ }
+
+
+ public class Info {
+
+ @Expose
+ private Long activeStatus;
+ @Expose
+ private Long bindStatus;
+ @Expose
+ private String bindType;
+ @Expose
+ private String bindUserId;
+ @Expose
+ private String erpSnGroup;
+ @Expose
+ private String erpSnType;
+ @Expose
+ private String iccid;
+ @Expose
+ private String ifCarcorder;
+ @Expose
+ private String ifImdemo;
+ @Expose
+ private String ifSocketservice;
+ @Expose
+ private String lastActiveCity;
+ @Expose
+ private Long lastBrandId;
+ @Expose
+ private String lastBrandName;
+ @Expose
+ private String lastCarNum;
+ @Expose
+ private String lastCarNumEn;
+ @Expose
+ private String lastFortaVersion;
+ @Expose
+ private Long lastModelId;
+ @Expose
+ private String lastModelName;
+ @Expose
+ private String sn;
+ @Expose
+ private String snGroupDetail;
+ @Expose
+ private String songTypeTop2;
+ @Expose
+ private String userNickName;
+ @Expose
+ private String cardIdAge;
+ @Expose
+ private String headImgUrl;
+ @Expose
+ private String cardIdSex;
+
+ public String getHeadImgUrl() {
+ return headImgUrl;
+ }
+
+ public void setHeadImgUrl(String headImgUrl) {
+ this.headImgUrl = headImgUrl;
+ }
+
+ public String getCardIdSex() {
+ return cardIdSex;
+ }
+
+ public void setCardIdSex(String cardIdSex) {
+ this.cardIdSex = cardIdSex;
+ }
+
+ public String getCardIdAge() {
+ return cardIdAge;
+ }
+
+ public void setCardIdAge(String cardIdAge) {
+ this.cardIdAge = cardIdAge;
+ }
+
+ public Long getActiveStatus() {
+ return activeStatus;
+ }
+
+ public void setActiveStatus(Long activeStatus) {
+ this.activeStatus = activeStatus;
+ }
+
+ public Long getBindStatus() {
+ return bindStatus;
+ }
+
+ public void setBindStatus(Long bindStatus) {
+ this.bindStatus = bindStatus;
+ }
+
+ public String getBindType() {
+ return bindType;
+ }
+
+ public void setBindType(String bindType) {
+ this.bindType = bindType;
+ }
+
+ public String getBindUserId() {
+ return bindUserId;
+ }
+
+ public void setBindUserId(String bindUserId) {
+ this.bindUserId = bindUserId;
+ }
+
+ @Override
+ public String toString() {
+ return "Info{" +
+ "activeStatus=" + activeStatus +
+ ", bindStatus=" + bindStatus +
+ ", bindType='" + bindType + '\'' +
+ ", bindUserId='" + bindUserId + '\'' +
+ ", erpSnGroup='" + erpSnGroup + '\'' +
+ ", erpSnType='" + erpSnType + '\'' +
+ ", iccid='" + iccid + '\'' +
+ ", ifCarcorder='" + ifCarcorder + '\'' +
+ ", ifImdemo='" + ifImdemo + '\'' +
+ ", ifSocketservice='" + ifSocketservice + '\'' +
+ ", lastActiveCity='" + lastActiveCity + '\'' +
+ ", lastBrandId=" + lastBrandId +
+ ", lastBrandName='" + lastBrandName + '\'' +
+ ", lastCarNum='" + lastCarNum + '\'' +
+ ", lastCarNumEn='" + lastCarNumEn + '\'' +
+ ", lastFortaVersion='" + lastFortaVersion + '\'' +
+ ", lastModelId=" + lastModelId +
+ ", lastModelName='" + lastModelName + '\'' +
+ ", sn='" + sn + '\'' +
+ ", snGroupDetail='" + snGroupDetail + '\'' +
+ ", songTypeTop2='" + songTypeTop2 + '\'' +
+ ", userNickName='" + userNickName + '\'' +
+ ", cardIdAge='" + cardIdAge + '\'' +
+ ", headImgUrl='" + headImgUrl + '\'' +
+ ", cardIdSex='" + cardIdSex + '\'' +
+ '}';
+ }
+
+ public String getErpSnGroup() {
+ return erpSnGroup;
+ }
+
+ public void setErpSnGroup(String erpSnGroup) {
+ this.erpSnGroup = erpSnGroup;
+ }
+
+ public String getErpSnType() {
+ return erpSnType;
+ }
+
+ public void setErpSnType(String erpSnType) {
+ this.erpSnType = erpSnType;
+ }
+
+ public String getIccid() {
+ return iccid;
+ }
+
+ public void setIccid(String iccid) {
+ this.iccid = iccid;
+ }
+
+ public String getIfCarcorder() {
+ return ifCarcorder;
+ }
+
+ public void setIfCarcorder(String ifCarcorder) {
+ this.ifCarcorder = ifCarcorder;
+ }
+
+ public String getIfImdemo() {
+ return ifImdemo;
+ }
+
+ public void setIfImdemo(String ifImdemo) {
+ this.ifImdemo = ifImdemo;
+ }
+
+ public String getIfSocketservice() {
+ return ifSocketservice;
+ }
+
+ public void setIfSocketservice(String ifSocketservice) {
+ this.ifSocketservice = ifSocketservice;
+ }
+
+ public String getLastActiveCity() {
+ return lastActiveCity;
+ }
+
+ public void setLastActiveCity(String lastActiveCity) {
+ this.lastActiveCity = lastActiveCity;
+ }
+
+ public Long getLastBrandId() {
+ return lastBrandId;
+ }
+
+ public void setLastBrandId(Long lastBrandId) {
+ this.lastBrandId = lastBrandId;
+ }
+
+ public String getLastBrandName() {
+ return lastBrandName;
+ }
+
+ public void setLastBrandName(String lastBrandName) {
+ this.lastBrandName = lastBrandName;
+ }
+
+ public String getLastCarNum() {
+ return lastCarNum;
+ }
+
+ public void setLastCarNum(String lastCarNum) {
+ this.lastCarNum = lastCarNum;
+ }
+
+ public String getLastCarNumEn() {
+ return lastCarNumEn;
+ }
+
+ public void setLastCarNumEn(String lastCarNumEn) {
+ this.lastCarNumEn = lastCarNumEn;
+ }
+
+ public String getLastFortaVersion() {
+ return lastFortaVersion;
+ }
+
+ public void setLastFortaVersion(String lastFortaVersion) {
+ this.lastFortaVersion = lastFortaVersion;
+ }
+
+ public Long getLastModelId() {
+ return lastModelId;
+ }
+
+ public void setLastModelId(Long lastModelId) {
+ this.lastModelId = lastModelId;
+ }
+
+ public String getLastModelName() {
+ return lastModelName;
+ }
+
+ public void setLastModelName(String lastModelName) {
+ this.lastModelName = lastModelName;
+ }
+
+ public String getSn() {
+ return sn;
+ }
+
+ public void setSn(String sn) {
+ this.sn = sn;
+ }
+
+ public String getSnGroupDetail() {
+ return snGroupDetail;
+ }
+
+ public void setSnGroupDetail(String snGroupDetail) {
+ this.snGroupDetail = snGroupDetail;
+ }
+
+ public String getSongTypeTop2() {
+ return songTypeTop2;
+ }
+
+ public void setSongTypeTop2(String songTypeTop2) {
+ this.songTypeTop2 = songTypeTop2;
+ }
+
+ public String getUserNickName() {
+ return userNickName;
+ }
+
+ public void setUserNickName(String userNickName) {
+ this.userNickName = userNickName;
+ }
+
+ }
+
+ @Override
+ public String toString() {
+ return "Result{" +
+ "info=" + info +
+ '}';
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "UserInfoEntity{" +
+ "result=" + result +
+ '}';
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/CarStatusListener.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/CarStatusListener.java
new file mode 100644
index 0000000000..f147a94a7c
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/CarStatusListener.java
@@ -0,0 +1,23 @@
+package com.mogo.module.v2x.listener;
+
+import com.mogo.map.location.MogoLocation;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/13 11:02 AM
+ * desc : 车辆速度状态回调
+ *
+ * version: 1.0
+ */
+public interface CarStatusListener {
+ /**
+ * 当车辆从 行驶状态 进入 的时候回调
+ */
+ void onCarDriving2Stop(MogoLocation location);
+
+ /**
+ * 当车辆从 停车状态 进入 行驶状态 的时候回调
+ */
+ void onCarStop2Driving(MogoLocation location);
+}
\ No newline at end of file
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XLocationListener.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XLocationListener.java
new file mode 100644
index 0000000000..b0b18824c8
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XLocationListener.java
@@ -0,0 +1,296 @@
+package com.mogo.module.v2x.listener;
+
+import android.content.Context;
+
+import com.mogo.map.MogoLatLng;
+import com.mogo.map.location.IMogoLocationListener;
+import com.mogo.map.location.MogoLocation;
+import com.mogo.map.overlay.IMogoPolyline;
+import com.mogo.map.search.geo.IMogoGeoSearchListener;
+import com.mogo.map.search.geo.MogoGeocodeResult;
+import com.mogo.map.search.geo.MogoRegeocodeResult;
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.common.entity.MarkerResponse;
+import com.mogo.module.service.Utils;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.alarm.V2XAlarmServer;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.network.V2XRefreshCallback;
+import com.mogo.module.v2x.scenario.impl.V2XScenarioManager;
+import com.mogo.module.v2x.utils.ADASUtils;
+import com.mogo.module.v2x.utils.DrivingDirectionUtils;
+import com.mogo.module.v2x.utils.LocationUtils;
+import com.mogo.module.v2x.utils.MarkerUtils;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/9 2:31 PM
+ * desc : V2X中用到的位置监听。处理刷新频率,以及位置改变是否触发道路事件警报
+ * version: 1.0
+ */
+public class V2XLocationListener implements IMogoLocationListener, CarStatusListener {
+ private String TAG = "V2XLocationListener";
+
+ private MogoLocation mLastCarLocation;
+ // 记录历史行驶轨迹,用于车行驶方向计算,只保留两个数据
+ private MogoLocation[] historyPath = new MogoLocation[2];
+ // 触发刷新的距离
+ private final int minDistance = 200;
+
+ private static V2XLocationListener mV2XLocationListener;
+
+ private V2XLocationListener() {
+ }
+
+ public synchronized static V2XLocationListener getInstance() {
+ synchronized (V2XLocationListener.class) {
+ if (mV2XLocationListener == null) {
+ mV2XLocationListener = new V2XLocationListener();
+ }
+ }
+ return mV2XLocationListener;
+ }
+
+ @Override
+ public void onLocationChanged(MogoLocation location) {
+ try {
+ Logger.d(V2XConst.MODULE_NAME, "V2X预警--onLocationChanged: " + GsonUtil.jsonFromObject(location));
+
+ // 刷新角度
+ getCurrentCarAngle(location);
+
+ // 只有车速大于 5 的时候进行计算
+ if (location.getSpeed() >= 5) {
+ refreshCurrentCarState(location);
+ }
+
+ // 车速从10降为0 这里统一交到 计算中心服务 进行计算,相应的状态会通过回调的方式调用
+ V2XServiceManager.getV2XCalculateServer().addCarStatusListener(TAG, this);
+ V2XServiceManager.getV2XCalculateServer().addCarTrajectory(location);
+
+ // 下面是道路刷新逻辑
+ if (mLastCarLocation == null) {
+ V2XServiceManager.getV2XMarkerService().refreshMarkerData(location);
+ mLastCarLocation = location;
+ }
+ // 计算车辆距离指定气泡的距离
+ float calculateDistance = Utils.calculateLineDistance(
+ new MogoLatLng(location.getLatitude(), location.getLongitude()),
+ new MogoLatLng(mLastCarLocation.getLatitude(), mLastCarLocation.getLongitude())
+ );
+
+ // 行驶距离 > 200M 重新请求
+ if (calculateDistance > minDistance) {
+ // Logger.d(MODULE_NAME, "V2X预警--移动距离>100米,重新刷新道路事件");
+ V2XServiceManager.getV2XMarkerService().refreshMarkerData(location);
+ mLastCarLocation = location;
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @param location 刷新当前车辆状态
+ */
+ private void refreshCurrentCarState(MogoLocation location) {
+ // 如果有道路事件正在预警的时候这里实时进行更新绘制
+ IMogoPolyline mMogoPolyline = V2XServiceManager.getMoGoV2XPolylineManager().getMogoPolyline();
+ if (mMogoPolyline != null && (V2XServiceManager.getMoGoV2XStatusManager().isRoadEventPOIShow()
+ || V2XServiceManager.getMoGoV2XStatusManager().isOtherSeekHelpPOIShow())
+ && V2XServiceManager.getV2XStatusManager().getTargetMoGoLatLng() != null) {
+ mMogoPolyline.setPoints(Arrays.asList(new MogoLatLng(location.getLatitude(), location.getLongitude()),
+ V2XServiceManager.getV2XStatusManager().getTargetMoGoLatLng()));
+
+ float zoomLevel = V2XServiceManager.getMapUIController().getZoomLevel();
+ //Logger.d(V2XConst.MODULE_NAME, "当前地图的缩放比例为:" + zoomLevel);
+ if (zoomLevel <= 17 && !V2XServiceManager.getMoGoV2XStatusManager().isRoadEventWindowShow()) {
+ // 缩放地图
+ Context context = V2XServiceManager.getContext();
+ MarkerUtils.zoomMap(V2XServiceManager.getV2XStatusManager().getTargetMoGoLatLng(), context);
+ }
+ }
+
+ // 疲劳驾驶检测
+ V2XAlarmServer.getFatigueDrivingShow(location, drivingShowEntity -> {
+ Logger.i(V2XConst.MODULE_NAME, "疲劳驾驶POI查询结果为: " + GsonUtil.jsonFromObject(drivingShowEntity));
+
+ String style = V2XServiceManager.getMoGoStatusManager().isMainPageOnResume() ? "1" : "2";
+ trackWithType(V2XPoiTypeEnum.ALERT_FATIGUE_DRIVING, drivingShowEntity.getLon(), drivingShowEntity.getLat(), style);
+
+ V2XMessageEntity v2XMessageEntity = new V2XMessageEntity<>();
+ v2XMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_FATIGUE_DRIVING);
+ v2XMessageEntity.setContent(drivingShowEntity);
+ v2XMessageEntity.setShowState(drivingShowEntity.isShowWindow());
+ // 广播给ADAS Launcher
+ ADASUtils.broadcastToADAS(V2XServiceManager.getContext(), drivingShowEntity);
+ V2XScenarioManager.getInstance().handlerMessage(v2XMessageEntity);
+ });
+
+ // 巡航处理
+ V2XRoadEventEntity v2XRoadEventEntity =
+ V2XAlarmServer.getDriveFrontAlarmEvent(
+ V2XServiceManager.getMoGoV2XMarkerManager().getV2XRoadEventEntityList(),
+ V2XServiceManager.getV2XStatusManager().getLocation());
+ // 距离是否大于10米 && 消息是否不为空
+ if (v2XRoadEventEntity != null && v2XRoadEventEntity.getDistance() >= 5) {
+ Logger.w(MODULE_NAME,
+ //"\nV2X预警--当前导航状态:" + V2XServiceManager.getNavi().isNaviing() +
+ //"\nV2X预警--roadEventIsNullCount:" + roadEventIsNullCount +
+ "\nV2X预警--当前预警事件:" + v2XRoadEventEntity
+ );
+ // Logger.w(MODULE_NAME, "V2X预警--前方数据距离:" + v2XRoadEventEntity.getDistance());
+ // 触发展示操作
+ V2XMessageEntity v2XMessageEntity = new V2XMessageEntity<>();
+ v2XMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING);
+ v2XMessageEntity.setContent(v2XRoadEventEntity);
+ v2XMessageEntity.setShowState(true);
+ V2XScenarioManager.getInstance().handlerMessage(v2XMessageEntity);
+ }
+ }
+
+ public MogoLocation getLastCarLocation() {
+ try {
+ if (mLastCarLocation == null) {
+ MogoLatLng mogoLatLng = V2XServiceManager.getNavi().getCarLocation();
+ mLastCarLocation = new MogoLocation();
+ if (mogoLatLng != null) {
+ mLastCarLocation.setLongitude(mogoLatLng.getLon());
+ mLastCarLocation.setLatitude(mogoLatLng.getLat());
+ } else {
+ mLastCarLocation.setLongitude(0.0);
+ mLastCarLocation.setLatitude(0.0);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return mLastCarLocation;
+ }
+
+ /**
+ * 获取当前车辆的行驶方向 0<=Angle<=360
+ *
+ * @return 0<= Angle < = 3 6 0
+ */
+ private void getCurrentCarAngle(MogoLocation location) {
+ // 记录位置轨迹
+ if (historyPath[0] != null) {
+ historyPath[1] = historyPath[0];
+ }
+ historyPath[0] = location;
+
+ if (historyPath[1] != null && historyPath[0] != null) {
+ double carAngle =
+ DrivingDirectionUtils.getCarAngle(
+ historyPath[1].getLatitude(), historyPath[1].getLongitude(),
+ historyPath[0].getLatitude(), historyPath[0].getLongitude()
+ );
+
+ //Logger.d(MODULE_NAME,
+ // "\n车辆经纬度:" + Arrays.toString(historyPath) +
+ // "\n车辆角度:" + carAngle);
+ // 这里是真实的车辆角度
+ location.setBearing((float) carAngle);
+ } else {
+ //Logger.e(MODULE_NAME,
+ // "\n首次获取经纬度,默认车头朝北:" + Arrays.toString(historyPath));
+ location.setBearing(0.0f);
+ }
+ V2XServiceManager.getV2XStatusManager().setLocation(location);
+ }
+
+ /**
+ * 获取定位相关信息
+ */
+ private void getLocationInfo(MogoLocation location) {
+ // 定位当前位置是否是高速
+ LocationUtils.geoCodeSearch(location, new IMogoGeoSearchListener() {
+ @Override
+ public void onRegeocodeSearched(MogoRegeocodeResult regeocodeResult) {
+ Logger.i(MODULE_NAME, " 根据经纬度查询结果为:" + regeocodeResult.getRegeocodeAddress().getFormatAddress());
+ location.setAddress(regeocodeResult.getRegeocodeAddress().getFormatAddress());
+ // 如果有 "高速"、"环线"、"快速路"等字眼的,视为封闭式道路,流程结束;
+ if (regeocodeResult.getRegeocodeAddress().getFormatAddress().contains("高速")
+ || regeocodeResult.getRegeocodeAddress().getFormatAddress().contains("环线")
+ || regeocodeResult.getRegeocodeAddress().getFormatAddress().contains("快速路")) {
+ } else {
+ searchIllegalParkData();
+ }
+ }
+
+ @Override
+ public void onGeocodeSearched(MogoGeocodeResult geocodeResult) {
+ }
+ });
+ }
+
+ /**
+ * 查询50米的违章停车数据
+ */
+ private void searchIllegalParkData() {
+ double lon = V2XServiceManager.getV2XStatusManager().getLocation().getLongitude();
+ double lat = V2XServiceManager.getV2XStatusManager().getLocation().getLatitude();
+
+ V2XServiceManager.getV2XRefreshModel()
+ .queryIllegalPark(new V2XRefreshCallback() {
+ @Override
+ public void onSuccess(MarkerResponse result) {
+ Logger.i(MODULE_NAME, "搜索附近的违章停车点 成功:" + GsonUtil.jsonFromObject(result));
+ if (result != null) {
+ if (result.getResult().getExploreWay().size() > 0) {
+ V2XMessageEntity> entity = new V2XMessageEntity<>();
+ entity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_ILLEGAL_PARK_WARNING);
+ entity.setContent(result.getResult().getExploreWay());
+ entity.setShowState(true);
+ V2XScenarioManager.getInstance().handlerMessage(entity);
+ }
+ }
+ }
+
+ @Override
+ public void onFail(String msg) {
+ Logger.e(MODULE_NAME, "搜索附近的违章停车点 失败:" + msg);
+ }
+ }, lon, lat);
+ }
+
+
+ @Override
+ public void onCarDriving2Stop(MogoLocation location) {
+ // 推迟30s主Launcher中的大而全的POI刷新时间
+ V2XServiceManager.getIMogoRefreshStrategyController().restartAutoRefreshAtTime(30000);
+ getLocationInfo(location);
+ }
+
+ @Override
+ public void onCarStop2Driving(MogoLocation location) {
+
+ }
+
+ //埋点
+ private static void trackWithType(String type, double lon, double lat, String style) {
+ Map properties = new HashMap<>();
+ properties.put("type", type);
+ properties.put("lng", lon);
+ properties.put("lat", lat);
+ properties.put("style", style);
+ V2XServiceManager.getMogoAnalytics().track(V2XConst.V2X_ROAD_SHOW, properties);
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMarkerClickListener.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMarkerClickListener.java
new file mode 100644
index 0000000000..cb79510fc4
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMarkerClickListener.java
@@ -0,0 +1,129 @@
+package com.mogo.module.v2x.listener;
+
+import android.text.TextUtils;
+
+import com.mogo.map.marker.IMogoMarker;
+import com.mogo.map.marker.IMogoMarkerClickListener;
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.common.entity.MarkerLocation;
+import com.mogo.module.common.entity.MarkerOnlineCar;
+import com.mogo.module.common.entity.MarkerShowEntity;
+import com.mogo.module.service.ServiceConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.utils.ChartingUtil;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/7 7:13 PM
+ * desc :
+ * version: 1.0
+ */
+public class V2XMarkerClickListener implements IMogoMarkerClickListener {
+
+ private static V2XMarkerClickListener markerClickListener;
+
+ // 最后一次选中的气泡
+ private IMogoMarker mLastCheckMarker;
+
+ private V2XMarkerClickListener() {
+ }
+
+ public synchronized static V2XMarkerClickListener getInstance() {
+ synchronized (V2XMarkerClickListener.class) {
+ if (markerClickListener == null) {
+ markerClickListener = new V2XMarkerClickListener();
+ }
+ }
+ return markerClickListener;
+ }
+
+ @Override
+ public boolean onMarkerClicked(IMogoMarker marker) {
+ try {
+ if (mLastCheckMarker != null) {
+ // 判断点击的是否是同一个
+ if (marker.equals(mLastCheckMarker)) {
+ Logger.d(MODULE_NAME, "onMarkerClicked 与上一次点击的Marker一样,不做处理:" + marker);
+ // 判断是在线车辆的时候处理打电话场景
+ try {
+ Object object = marker.getObject();
+ if (object != null) {
+ // 修改数据
+ MarkerShowEntity showEntity = (MarkerShowEntity) object;
+ if (showEntity.getMarkerType().equals(ServiceConst.CARD_TYPE_USER_DATA)) {
+ Object bindObj = showEntity.getBindObj();
+ if (bindObj instanceof MarkerOnlineCar) {
+ MarkerLocation location = new MarkerLocation();
+ location.setLon(marker.getPosition().getLon());
+ location.setLat(marker.getPosition().getLat());
+ ChartingUtil.callChatting(
+ ((MarkerOnlineCar) bindObj).getUserInfo(),
+ location
+ );
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+ // 关闭上次的气泡
+ V2XServiceManager.getMoGoV2XMarkerManager().closeMarker(V2XServiceManager.getContext(), mLastCheckMarker);
+ }
+ // 将当前的Marker设置为选中
+ mLastCheckMarker = V2XServiceManager.getMoGoV2XMarkerManager().openMarker(V2XServiceManager.getContext(), marker);
+
+ // marker点击展示上半部分的浮窗,只加了道路事件
+ Object nov = marker.getObject();
+ Logger.d(MODULE_NAME, "marker点击====" + nov);
+ if (nov != null) {
+ MarkerShowEntity showEntity = (MarkerShowEntity) nov;
+ if (showEntity.getMarkerType().equals(ServiceConst.CARD_TYPE_NOVELTY)) {
+ Object bindObj = showEntity.getBindObj();
+ if (bindObj instanceof MarkerExploreWay) {
+ MarkerExploreWay noveltyInfo = (MarkerExploreWay) bindObj;
+ Logger.d(MODULE_NAME, "新鲜事marker点击===" + bindObj);
+ V2XRoadEventEntity v2XRoadEventEntity = new V2XRoadEventEntity();
+ v2XRoadEventEntity.setLocation(noveltyInfo.getLocation());
+ v2XRoadEventEntity.setPoiType(noveltyInfo.getPoiType());
+ v2XRoadEventEntity.setNoveltyInfo(noveltyInfo);
+ v2XRoadEventEntity.setExpireTime(20000);
+ v2XRoadEventEntity.setDistance(1000);
+ //V2XServiceManager.getMoGoV2XScenarioManager().showRoadEventWindow(v2XRoadEventEntity, false);
+ return true;
+ }
+ } else if (showEntity.getMarkerType().equals(ServiceConst.CARD_TYPE_ROAD_CONDITION)) {
+ Object bindObj = showEntity.getBindObj();
+ if (bindObj instanceof MarkerExploreWay) {
+ MarkerExploreWay markerExploreWay = (MarkerExploreWay) bindObj;
+ if (markerExploreWay.getItems().size() > 0
+ && !TextUtils.isEmpty(markerExploreWay.getItems().get(0).getUrl())) {
+ Logger.d(MODULE_NAME, "MarkerExploreWay点击===" + bindObj);
+ // 记录道路事件
+ V2XRoadEventEntity v2XRoadEventEntity = new V2XRoadEventEntity();
+ v2XRoadEventEntity.setLocation(markerExploreWay.getLocation());
+ // 探路目前只有上报拥堵
+ v2XRoadEventEntity.setPoiType(V2XPoiTypeEnum.FOURS_BLOCK_UP);
+
+ v2XRoadEventEntity.setDistance(1000);
+ v2XRoadEventEntity.setNoveltyInfo(markerExploreWay);
+ v2XRoadEventEntity.setExpireTime(20000);
+ //V2XServiceManager.getMoGoV2XScenarioManager().showRoadEventWindow(v2XRoadEventEntity, false);
+ return true;
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401003.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401003.java
new file mode 100644
index 0000000000..3b179e600f
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401003.java
@@ -0,0 +1,123 @@
+package com.mogo.module.v2x.listener;
+
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.scenario.impl.V2XScenarioManager;
+import com.mogo.module.v2x.utils.ADASUtils;
+import com.mogo.module.v2x.utils.MarkerUtils;
+import com.mogo.module.v2x.utils.ToastUtils;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.service.connection.IMogoOnMessageListener;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/1 10:23 AM
+ * desc :
+ * ADAS & V2X 场景触发push
+ * TODO 前瞻需求,这里是后台下发 http://h5service.zhidaohulian.com/v2x_remoteControl/#/
+ * version: 1.0
+ */
+public class V2XMessageListener_401003 implements IMogoOnMessageListener {
+
+ @Override
+ public Class target() {
+ return V2XPushMessageEntity.class;
+ }
+
+ @Override
+ public void onMsgReceived(V2XPushMessageEntity message) {
+ Logger.i(MODULE_NAME, "V2XMessageListener_401003:" + GsonUtil.jsonFromObject(message));
+ V2XUtils.runOnUiThread(() -> {
+ try {
+ handlerV2XAlarm(message);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ /**
+ * 处理V2X场景
+ *
+ * @param alarmMessage V2X消息
+ */
+ private void handlerV2XAlarm(V2XPushMessageEntity alarmMessage) {
+ // 绘制道路事件
+ //Logger.w(MODULE_NAME, "处理V2X场景SceneId==" + alarmMessage.getSceneId());
+
+ // 修改地图比例尺
+ if (alarmMessage.getZoomScale() > 0) {
+ V2XServiceManager.getMoGoStatusManager().setUserInteractionStatus(MODULE_NAME, true, true);
+ MarkerUtils.resetMapZoom(alarmMessage.getZoomScale());
+ }
+
+ V2XMessageEntity v2XMessageEntity = new V2XMessageEntity<>();
+ switch (alarmMessage.getSceneId()) {
+ case "100000"://前车碰撞预警
+ case "100001"://车道偏离预警
+ //V2XWindowManager.removeAllV2XView();
+ break;
+
+ // 下面的场景,地图缩小,显示30秒直播小视频,视频播放结束恢复地图比例尺
+ case "100002"://盲区换道警告
+ case "100003"://盲区行人碰撞预警
+ case "100009"://危险路段(施工)告警
+ case "100010"://拥堵告警
+ case "100004"://逆向超车碰撞预警
+ case "100011"://失控车辆告警
+ v2XMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_PUSH_LIVE_CAR_WARNING);
+ v2XMessageEntity.setContent(alarmMessage);
+ v2XMessageEntity.setShowState(true);
+ V2XScenarioManager.getInstance().handlerMessage(v2XMessageEntity);
+ break;
+ // 下面的场景禁行动画效果展示
+ case "100005"://前车紧急制动告警
+ case "100006"://十字路口碰撞预警
+ case "100007"://岔路口碰撞预警
+ case "100008"://禁行车道预警
+ case "100012"://应急车辆优先通行
+ case "100013"://闯红灯预警
+ v2XMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_ANIMATION_WARNING);
+ v2XMessageEntity.setContent(alarmMessage);
+ v2XMessageEntity.setShowState(true);
+ V2XScenarioManager.getInstance().handlerMessage(v2XMessageEntity);
+ break;
+ case "100015"://取快递
+ case "100016"://顺风车
+ case "100017"://政府公告
+ v2XMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_PUSH_WINDOW_WARNING);
+ v2XMessageEntity.setContent(alarmMessage);
+ v2XMessageEntity.setShowState(true);
+ V2XScenarioManager.getInstance().handlerMessage(v2XMessageEntity);
+ break;
+ case "100014"://行人点赞
+ showTip(alarmMessage.getTts());
+ ADASUtils.broadcastToADAS_TTS(V2XServiceManager.getContext(), alarmMessage);
+ break;
+ default:
+ ADASUtils.broadcastToADAS_TTS(V2XServiceManager.getContext(), alarmMessage);
+ break;
+ }
+ }
+
+ private void showTip(String msg) {
+ ToastUtils.setGravity(Gravity.CENTER, 0, 0);
+ View toastView = LayoutInflater.from(V2XServiceManager.getContext()).inflate(R.layout.toast_view, null);
+ TextView msgView = toastView.findViewById(R.id.tvFeedbackContent);
+ msgView.setText(msg);
+ ToastUtils.showCustomShort(toastView);
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401005.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401005.java
new file mode 100644
index 0000000000..304be7162f
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401005.java
@@ -0,0 +1,94 @@
+package com.mogo.module.v2x.listener;
+
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.v2x.scenario.impl.V2XScenarioManager;
+import com.mogo.module.v2x.utils.MarkerUtils;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.service.connection.IMogoOnMessageListener;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/1 10:23 AM
+ * desc : 特殊车辆推送绘制
+ * version: 1.0
+ */
+public class V2XMessageListener_401005 implements IMogoOnMessageListener {
+ @Override
+ public Class target() {
+ return V2XSpecialCarRes.class;
+ }
+
+ @Override
+ public void onMsgReceived(V2XSpecialCarRes message) {
+ Logger.i(MODULE_NAME, "V2XMessageListener_401005:" + GsonUtil.jsonFromObject(message));
+ if (!V2XServiceManager.getMoGoV2XStatusManager().isRoadEventWindowShow()) {
+ V2XUtils.runOnBackgroundThread(() -> {
+ try {
+ MarkerUtils.handlerV2XSpecialVehicleMarker(
+ message,
+ V2XServiceManager.getContext(),
+ V2XMarkerClickListener.getInstance());
+
+ //获取经纬度信息
+ //根据sn获取用户信息
+ //V2XWindowManager展示
+ handleSeekHelp(message);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+ }
+
+
+ /**
+ * 处理故障求助
+ */
+ private void handleSeekHelp(V2XSpecialCarRes message) {
+ if (message == null) {
+ return;
+ }
+ V2XServiceManager.getMarkerManager().removeMarkers(V2XConst.V2X_MARKER_SPECIAL_CAR);
+ List coordinates = message.getCoordinates();
+ List entityList = new ArrayList<>();
+ // 移除上一次的数据
+ //Context context = V2XServiceManager.getContext();
+
+ for (V2XMarkerEntity coordinate : coordinates) {
+ //故障车机
+ if (coordinate.getTargetId() == V2XPoiTypeEnum.ALERT_CAR_TROUBLE_WARNING) {
+ //V2XMarkerEntity.UserInfoBean userInfoBean = coordinate.getUserInfo();
+ //if (userInfoBean != null) {
+ entityList.add(coordinate);
+ //}
+ //绘制
+ //V2XServiceManager
+ // .getMoGoV2XMarkerManager()
+ // .drawableSpecialCarPOI(context, coordinate, V2XMarkerClickListener.getInstance());
+ }
+ }
+ if (!entityList.isEmpty()) {
+ //V2XUtils.runOnUiThread(() -> V2XServiceManager.getMoGoV2XScenarioManager().showOtherSeekHelpWindow(entityList));
+ V2XUtils.runOnUiThread(() -> {
+ V2XMessageEntity> v2XMessageEntity = new V2XMessageEntity<>();
+ v2XMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_SEEK_WARNING);
+ v2XMessageEntity.setContent(entityList);
+ V2XScenarioManager.getInstance().handlerMessage(v2XMessageEntity);
+ });
+ }
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401006.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401006.java
new file mode 100644
index 0000000000..def6c3032b
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401006.java
@@ -0,0 +1,30 @@
+package com.mogo.module.v2x.listener;
+
+import com.mogo.module.v2x.entity.net.V2XAlarmEventRes;
+import com.mogo.service.connection.IMogoOnMessageListener;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/1 10:23 AM
+ * desc :
+ * 3.2.1、前方静止or慢速车辆报警
+ * 3.2.2、道路危险状况告警 / 前方拥堵告警
+ * version: 1.0
+ */
+@Deprecated
+public class V2XMessageListener_401006 implements IMogoOnMessageListener {
+
+ @Override
+ public Class target() {
+ return V2XAlarmEventRes.class;
+ }
+
+ @Override
+ public void onMsgReceived(V2XAlarmEventRes message) {
+ Logger.i(MODULE_NAME, "iMogoOnMessageListener_401006:" + message);
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401007.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401007.java
new file mode 100644
index 0000000000..fe6d581741
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401007.java
@@ -0,0 +1,48 @@
+package com.mogo.module.v2x.listener;
+
+import com.mogo.module.service.MarkerServiceHandler;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.entity.net.V2XAlarmEventRes;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.v2x.utils.ADASUtils;
+import com.mogo.service.connection.IMogoOnMessageListener;
+import com.mogo.utils.logger.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/1 10:23 AM
+ * desc : 限行通知
+ * version: 1.0
+ */
+public class V2XMessageListener_401007 implements IMogoOnMessageListener {
+ @Override
+ public Class target() {
+ return V2XAlarmEventRes.class;
+ }
+
+ @Override
+ public void onMsgReceived(V2XAlarmEventRes message) {
+ Logger.i(MODULE_NAME, "V2XMessageListener_401007:" + message);
+ try {
+ if (message != null) {
+
+ // 发送广播
+ V2XAlarmEventRes.AlarmInfo alarmInfo = message.getResult().getAlarmInfo();
+ ADASUtils.broadcastToADAS_TTS(V2XServiceManager.getContext(), alarmInfo);
+
+ // 统计代码
+ final Map properties = new HashMap<>();
+ properties.put("warning_id", V2XPoiTypeEnum.ALERT_TRAFFIC_CONTROL);
+ MarkerServiceHandler.getMogoAnalytics().track("v2x_warning", properties);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401009.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401009.java
new file mode 100644
index 0000000000..2b081aba24
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401009.java
@@ -0,0 +1,50 @@
+package com.mogo.module.v2x.listener;
+
+import android.view.Gravity;
+import android.view.LayoutInflater;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.entity.net.V2XGiveLike;
+import com.mogo.module.v2x.utils.ADASUtils;
+import com.mogo.module.v2x.utils.ToastUtils;
+import com.mogo.service.connection.IMogoOnMessageListener;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/1 10:23 AM
+ * desc :
+ * * 2.0.2
+ * * http://wiki.zhidaohulian.com/pages/viewpage.action?pageId=35786774
+ * * 注册点赞通信接收
+ * version: 1.0
+ */
+public class V2XMessageListener_401009 implements IMogoOnMessageListener {
+ @Override
+ public Class target() {
+ return V2XGiveLike.class;
+ }
+
+ @Override
+ public void onMsgReceived(V2XGiveLike message) {
+ Logger.i(MODULE_NAME, "V2XMessageListener_401009:" + message);
+ try {
+ V2XPushMessageEntity v2XAlarmMessage = new V2XPushMessageEntity();
+ v2XAlarmMessage.setTts(message.getResult());
+ v2XAlarmMessage.setAlarmContent(message.getResult());
+ v2XAlarmMessage.setExpireTime(20000);
+ v2XAlarmMessage.setSceneId("000000");
+ ADASUtils.broadcastToADAS_TTS(V2XServiceManager.getContext(), v2XAlarmMessage);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ ToastUtils.setGravity(Gravity.CENTER, 0, 0);
+ ToastUtils.showCustomLong(LayoutInflater.from(V2XServiceManager.getContext()).inflate(R.layout.toast_view_feedback_img_text, null));
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401010.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401010.java
new file mode 100644
index 0000000000..16883842ff
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401010.java
@@ -0,0 +1,47 @@
+package com.mogo.module.v2x.listener;
+
+import com.mogo.module.v2x.entity.net.V2XAlarmEventRes;
+import com.mogo.service.connection.IMogoOnMessageListener;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/1 10:23 AM
+ * desc :
+ * * 2.0.3
+ * * http://wiki.zhidaohulian.com/pages/viewpage.action?pageId=35788659
+ * * TODO 停车场、加油站推送心跳,这里是前瞻的功能,目前仅在分体机使用,
+ * version: 1.0
+ */
+@Deprecated
+public class V2XMessageListener_401010 implements IMogoOnMessageListener {
+
+ @Override
+ public Class target() {
+ return V2XAlarmEventRes.class;
+ }
+
+ @Override
+ public void onMsgReceived(V2XAlarmEventRes message) {
+ Logger.i(MODULE_NAME, "V2XMessageListener_401010:" + message);
+ try {
+ if (message != null
+ && message.getResult() != null) {
+
+ // 处理报警事件
+ handlerAlarmInfo();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 处理道路事件提醒
+ */
+ private void handlerAlarmInfo() {
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401011.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401011.java
new file mode 100644
index 0000000000..55afafbd77
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XMessageListener_401011.java
@@ -0,0 +1,43 @@
+package com.mogo.module.v2x.listener;
+
+import com.mogo.module.common.entity.MarkerCardResult;
+import com.mogo.module.common.entity.MarkerResponse;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.service.connection.IMogoOnMessageListener;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/1 10:23 AM
+ * desc : 道路事件,实际就是大而全的数据进行的过滤
+ * version: 1.0
+ */
+public class V2XMessageListener_401011 implements IMogoOnMessageListener {
+
+ @Override
+ public Class target() {
+ return MarkerResponse.class;
+ }
+
+ @Override
+ public void onMsgReceived(MarkerResponse response) {
+ Logger.d(MODULE_NAME, "V2XMessageListener_401011==V2X地图气泡数据刷新:\n" + GsonUtil.jsonFromObject(response));
+ if (!V2XServiceManager.getMoGoV2XStatusManager().isRoadEventWindowShow()) {
+ V2XUtils.runOnBackgroundThread(() -> {
+ // 解析不同的Marker类型,然后对应的进行绘制
+ if (response != null && response.getResult() != null) {
+ MarkerCardResult markerCardResult = response.getResult();
+ // 解析存储道路事件
+ //V2XServiceManager.getMoGoV2XMarkerManager().drawableV2XMarker(markerCardResult);
+ V2XServiceManager.getMoGoV2XMarkerManager().analysisV2XRoadEvent(markerCardResult);
+ }
+ });
+ }
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XWindowStatusListener.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XWindowStatusListener.java
new file mode 100644
index 0000000000..41d26f5d34
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/listener/V2XWindowStatusListener.java
@@ -0,0 +1,14 @@
+package com.mogo.module.v2x.listener;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/15 10:20 AM
+ * desc : V2X 窗体状态回调
+ * version: 1.0
+ */
+public interface V2XWindowStatusListener {
+ void onViewShow();
+
+ void onViewClose();
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XMarkerManager.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XMarkerManager.java
new file mode 100644
index 0000000000..f1e987d46d
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XMarkerManager.java
@@ -0,0 +1,163 @@
+package com.mogo.module.v2x.manager;
+
+import android.content.Context;
+
+import com.alibaba.android.arouter.facade.template.IProvider;
+import com.mogo.map.marker.IMogoMarker;
+import com.mogo.map.marker.IMogoMarkerClickListener;
+import com.mogo.module.common.entity.MarkerCardResult;
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.common.entity.MarkerNoveltyInfo;
+import com.mogo.module.common.entity.MarkerOnlineCar;
+import com.mogo.module.common.entity.MarkerShowEntity;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/17 8:15 PM
+ * desc : V2X 涉及到的地图 POI 点的绘制
+ * version: 1.0
+ */
+public interface IMoGoV2XMarkerManager extends IProvider {
+
+ /**
+ * 重新绘制最后一次的POI点
+ */
+ void drawableLastAllPOI();
+
+ /**
+ * 获取所有的道路事件点,探路事件,返回结果是按照距离当前车辆从近到远排列好的
+ *
+ * @return 按从近到远排列好的道路事件
+ */
+ CopyOnWriteArrayList getV2XRoadEventEntityList();
+
+ /**
+ * 从探路数据和新鲜事儿的路况信息中解析出道路事件信息
+ */
+ void analysisV2XRoadEvent(MarkerCardResult markerCardResult);
+
+ /**
+ * 绘制V2X 地图点事件
+ *
+ * @param markerCardResult 大而全中过滤出来的点事件
+ */
+ void drawableV2XMarker(MarkerCardResult markerCardResult);
+
+ /**
+ * 绘制 在线车辆 POI
+ *
+ * @param markerOnlineCars
+ * @param clickListener
+ */
+ void drawableOnlineCarPOI(List markerOnlineCars, IMogoMarkerClickListener clickListener);
+
+ /**
+ * 清除 在线车辆 POI
+ */
+ void clearOnlineCarPOI();
+
+ /**
+ * 绘制 特殊车辆
+ *
+ * @param context
+ * @param v2XMarkerEntity
+ * @param clickListener
+ */
+ void drawableSpecialCarPOI(Context context, V2XMarkerEntity v2XMarkerEntity, IMogoMarkerClickListener clickListener);
+
+ /**
+ * 清除 特殊车辆 POI
+ */
+ void clearSpecialCarPOI();
+
+ /**
+ * 绘制可以直播的在线车辆
+ *
+ * @param markerOnlineCars
+ * @param clickListener
+ */
+ void drawableLiveCarPOI(List markerOnlineCars, IMogoMarkerClickListener clickListener);
+
+ /**
+ * 清除 可以直播的在线车辆 POI
+ */
+ void clearLiveCarPOI();
+
+ /**
+ * 绘制 探路
+ *
+ * @param exploreWayList
+ * @param clickListener
+ */
+ void drawableExplorePOI(List exploreWayList, IMogoMarkerClickListener clickListener);
+
+ /**
+ * 清除 探路 POI
+ */
+ void clearExplorePOI();
+
+ /**
+ * 绘制 新鲜事儿
+ *
+ * @param noveltyInfoList
+ * @param clickListener
+ */
+ void drawableNoveltyPOI(List noveltyInfoList, IMogoMarkerClickListener clickListener);
+
+ /**
+ * 清除 新鲜事儿 POI
+ */
+ void clearNoveltyPOI();
+
+ /**
+ * 绘制Marker,上面调用的都是这个方法
+ *
+ * @param context
+ * @param markerShowEntity
+ * @param clickListener
+ */
+ void drawableMarker(Context context, MarkerShowEntity markerShowEntity, IMogoMarkerClickListener clickListener);
+
+
+ /**
+ * 展开气泡
+ *
+ * @param context
+ * @param currentMarker
+ */
+ IMogoMarker openMarker(Context context, IMogoMarker currentMarker);
+
+ /**
+ * 关闭气泡
+ *
+ * @param context
+ * @param currentMarker
+ */
+ void closeMarker(Context context, IMogoMarker currentMarker);
+
+ /**
+ * 绘制正在预警的道路事件的POI点
+ *
+ * @param context
+ * @param roadEventEntity
+ */
+ void drawableAlarmPOI(Context context, V2XRoadEventEntity roadEventEntity, IMogoMarkerClickListener clickListener);
+
+ /**
+ * 清除 道路事件 POI
+ */
+ void clearAlarmPOI();
+
+ /**
+ * 清除 所有的 POI
+ */
+ void clearALLPOI();
+
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XPolylineManager.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XPolylineManager.java
new file mode 100644
index 0000000000..237dd6d0f3
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XPolylineManager.java
@@ -0,0 +1,31 @@
+package com.mogo.module.v2x.manager;
+
+import android.content.Context;
+
+import com.alibaba.android.arouter.facade.template.IProvider;
+import com.mogo.map.overlay.IMogoPolyline;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/17 9:49 PM
+ * desc : 当前车辆与道路事件的连接线
+ * version: 1.0
+ */
+public interface IMoGoV2XPolylineManager extends IProvider {
+ /**
+ * 绘制连接线,目标车,与当前车辆间连线
+ *
+ * @param context
+ * @param roadEventEntity
+ */
+ void drawablePolyline(Context context, V2XRoadEventEntity roadEventEntity);
+
+ /**
+ * 移除连接线
+ */
+ void clearLine();
+
+ IMogoPolyline getMogoPolyline();
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XStatusChangedListener.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XStatusChangedListener.java
new file mode 100644
index 0000000000..b200d52777
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XStatusChangedListener.java
@@ -0,0 +1,19 @@
+package com.mogo.module.v2x.manager;
+
+import com.alibaba.android.arouter.facade.template.IProvider;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/17 7:44 PM
+ * desc : 状态控制器监听
+ * version: 1.0
+ */
+public interface IMoGoV2XStatusChangedListener extends IProvider {
+
+ /**
+ * @param descriptor 状态类型
+ * @param isTrue true - v2x ui show
+ */
+ void onStatusChanged(V2XStatusDescriptor descriptor, boolean isTrue);
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XStatusManager.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XStatusManager.java
new file mode 100644
index 0000000000..ca47257765
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/IMoGoV2XStatusManager.java
@@ -0,0 +1,188 @@
+package com.mogo.module.v2x.manager;
+
+import com.alibaba.android.arouter.facade.template.IProvider;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/17 7:20 PM
+ * desc : V2X 状态管理
+ * version: 1.0
+ */
+public interface IMoGoV2XStatusManager extends IProvider {
+ /**
+ * 地图区域 V2X道路预警POI 是否在展示
+ */
+ boolean isRoadEventPOIShow();
+
+ /**
+ * 按钮区 V2X道路预警 是否在展示
+ */
+ boolean isRoadEventButtonShow();
+
+ /**
+ * 头部 V2X道路预警 UI 是否在展示
+ */
+ boolean isRoadEventWindowShow();
+
+ /**
+ * 头部 V2X的道路直播 UI 是否在展示
+ */
+ boolean isRoadLiveCarWindowShow();
+
+ /**
+ * 头部 他人车辆故障求助 UI 是否在展示
+ */
+ boolean isOtherSeekHelpWindowShow();
+
+ /**
+ * 地图区域 自身的道路求助按钮 是否在展示
+ */
+ boolean isMeSeekHelpButtonShow();
+
+ /**
+ * 地图区域 他人车辆故障求助POI 是否在展示
+ */
+ boolean isOtherSeekHelpPOIShow();
+
+ /**
+ * 前瞻需求
+ * 右侧2/3 V2X 场景动画 UI 是否在展示
+ */
+ boolean isV2XAnimationShow();
+
+ /**
+ * 前瞻需求
+ * 右侧2/3 左下角 模拟直播车机 UI 是否在展示
+ */
+ boolean isLeftLiveVideoShow();
+
+ /**
+ * 推送弹窗的状态
+ */
+ boolean isPushWindowShow();
+
+ /**
+ * 推送的 POI 状态
+ */
+ boolean isPushPOIShow();
+
+ /**
+ * 疲劳驾驶 状态
+ */
+ boolean isFatigueDrivingWindowShow();
+
+ /**
+ * 设置 V2X道路预警POI 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setRoadEventPOIShow(String tag, boolean show);
+
+ /**
+ * 设置 V2X道路预警 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setRoadEventButtonShow(String tag, boolean show);
+
+ /**
+ * 设置 右侧2/3 头部 V2X道路预警 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setRoadEventWindowShow(String tag, boolean show);
+
+ /**
+ * 设置 右侧2/3 头部 V2X的道路直播 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setRoadLiveCarWindowShow(String tag, boolean show);
+
+ /**
+ * 设置 他人车辆故障求助 UI 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setOtherSeekHelpWindowShow(String tag, boolean show);
+
+ /**
+ * 地图区域 自身的道路求助按钮 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setMeSeekHelpButtonShow(String tag, boolean show);
+
+ /**
+ * 地图区域 他人车辆故障求助POI 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setOtherSeekHelpPOIShow(String tag, boolean show);
+
+ /**
+ * 设置 右侧2/3 全屏 V2X 场景动画 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setV2XAnimationWindowShow(String tag, boolean show);
+
+ /**
+ * 设置 模拟直播车机 UI 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setLiveCarWindowShow(String tag, boolean show);
+
+ /**
+ * 推送弹窗的状态
+ *
+ * @param tag
+ * @param show
+ */
+ void setPushWindowShow(String tag, boolean show);
+
+ /**
+ * 地图区域 推送的 POI 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setPushPOIShow(String tag, boolean show);
+
+ /**
+ * 地图区域 疲劳驾驶 是否在展示
+ *
+ * @param tag
+ * @param show
+ */
+ void setFatigueDrivingWindowShow(String tag, boolean show);
+
+ /**
+ * 注册监听
+ *
+ * @param tag 业务类型
+ * @param descriptor 监听类型
+ * @param listener 监听回调
+ */
+ void registerStatusChangedListener(String tag, V2XStatusDescriptor descriptor, IMoGoV2XStatusChangedListener listener);
+
+ /**
+ * 注销
+ *
+ * @param tag 业务类型
+ * @param descriptor 注销类型
+ * @param listener 注销回调
+ */
+ void unregisterStatusChangedListener(String tag, V2XStatusDescriptor descriptor, IMoGoV2XStatusChangedListener listener);
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/V2XStatusDescriptor.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/V2XStatusDescriptor.java
new file mode 100644
index 0000000000..0cf7973e0a
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/V2XStatusDescriptor.java
@@ -0,0 +1,70 @@
+package com.mogo.module.v2x.manager;
+
+/**
+ * @author congtaowang
+ * @since 2020-01-04
+ *
+ * 状态描述
+ */
+public enum V2XStatusDescriptor {
+
+ /**
+ * 地图 V2X道路预警POI
+ */
+ RoadEventPOI_UI,
+
+ /**
+ * 按钮 V2X道路预警
+ */
+ RoadEventButton_UI,
+
+ /**
+ * 头部 V2X道路预警 UI
+ */
+ RoadEventWindow_UI,
+
+ /**
+ * 头部 V2X的道路直播 UI
+ */
+ RoadLiveCarWindow_UI,
+
+ /**
+ * 地图 他人车辆故障求助POI
+ */
+ OtherSeekHelpPOI_UI,
+
+ /**
+ * 头部 他人车辆故障求助 UI 是否在展示
+ */
+ OtherSeekHelpWindow_UI,
+
+ /**
+ * 头部 自身的道路求助按钮 UI 是否在展示
+ */
+ MeSeekHelpButton_UI,
+
+ /**
+ * 右侧2/3 V2X 场景动画 UI 是否在展示
+ */
+ V2XAnimationWindow_UI,
+
+ /**
+ * 右侧2/3 左下角 模拟直播车机 UI 是否在展示
+ */
+ LiveCarWindow_UI,
+
+ /**
+ * 推送的POI
+ */
+ PushWindowPOI_UI,
+
+ /**
+ * 推送的弹窗
+ */
+ PushWindow_UI,
+
+ /**
+ * 疲劳驾驶弹窗
+ */
+ FatigueDrivingWindow_UI,
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XMarkerManager.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XMarkerManager.java
new file mode 100644
index 0000000000..72aac3e4d9
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XMarkerManager.java
@@ -0,0 +1,572 @@
+package com.mogo.module.v2x.manager.impl;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.mogo.map.MogoLatLng;
+import com.mogo.map.location.MogoLocation;
+import com.mogo.map.marker.IMogoMarker;
+import com.mogo.map.marker.IMogoMarkerClickListener;
+import com.mogo.map.marker.MogoMarkerOptions;
+import com.mogo.map.uicontroller.EnumMapUI;
+import com.mogo.module.common.entity.MarkerCardResult;
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.common.entity.MarkerLocation;
+import com.mogo.module.common.entity.MarkerNoveltyInfo;
+import com.mogo.module.common.entity.MarkerOnlineCar;
+import com.mogo.module.common.entity.MarkerShowEntity;
+import com.mogo.module.service.ServiceConst;
+import com.mogo.module.service.Utils;
+import com.mogo.module.service.marker.IMarkerView;
+import com.mogo.module.service.marker.MapMarkerAdapter;
+import com.mogo.module.service.utils.ViewUtils;
+import com.mogo.module.v2x.MoGoV2XServicePaths;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.listener.V2XMarkerClickListener;
+import com.mogo.module.v2x.manager.IMoGoV2XMarkerManager;
+import com.mogo.module.v2x.marker.V2XMarkerAdapter;
+import com.mogo.module.v2x.utils.EventTypeUtils;
+import com.mogo.module.v2x.utils.MarkerUtils;
+import com.mogo.utils.logger.Logger;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+import static com.mogo.module.v2x.V2XConst.V2X_EVENT_ALARM_POI;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/17 9:57 PM
+ * desc : V2X 涉及到的地图 POI 点的绘制
+ * version: 1.0
+ */
+@Route(path = MoGoV2XServicePaths.PATH_V2X_MARKER_MANAGER)
+public class MoGoV2XMarkerManager implements IMoGoV2XMarkerManager {
+ private static final String TAG = "MoGoV2XMarkerManager";
+
+ // 记录所有的:新鲜事儿的道路事件点、探路事件
+ private static CopyOnWriteArraySet mV2XRoadEventEntityArrayList = new CopyOnWriteArraySet<>();
+ // 上次的道路事件的预警Marker
+ private static IMogoMarker mAlarmInfoMarker;
+
+ @Override
+ public void drawableLastAllPOI() {
+ //Logger.w(MODULE_NAME, "V2X---绘制上一次的POI,回调给Launcher底层逻辑让其进行绘制");
+ // 清除连接线
+ V2XServiceManager.getMoGoV2XPolylineManager().clearLine();
+ clearAlarmPOI();
+ clearSpecialCarPOI();
+ // 开启主Launcher刷新
+ V2XServiceManager.getIMogoRefreshStrategyController().restartAutoRefreshAtTime(500);
+ }
+
+ @Override
+ public CopyOnWriteArrayList getV2XRoadEventEntityList() {
+ CopyOnWriteArrayList roadEventEntities = new CopyOnWriteArrayList<>();
+ // 当前车辆数据
+ MogoLocation currentLocation = V2XServiceManager.getV2XStatusManager().getLocation();
+
+ if (currentLocation != null) {
+ // 重新计算距离
+ for (V2XRoadEventEntity v2XRoadEventEntity : mV2XRoadEventEntityArrayList) {
+ // 事件位置
+ MarkerLocation location = v2XRoadEventEntity.getLocation();
+ if (location != null) {
+ float calculateDistance = Utils.calculateLineDistance(
+ new MogoLatLng(location.getLat(), location.getLon()),
+ new MogoLatLng(currentLocation.getLatitude(), currentLocation.getLongitude())
+ );
+ v2XRoadEventEntity.setDistance(calculateDistance);
+ }
+ roadEventEntities.add(v2XRoadEventEntity);
+ }
+
+ // 按照与当前车辆距离排序
+ for (int i = 0; i < roadEventEntities.size(); i++) {
+ for (int j = i; j > 0; j--) {
+ if (roadEventEntities.get(j).getDistance() < roadEventEntities.get(j - 1).getDistance()) {
+ V2XRoadEventEntity v2XRoadEventEntity = roadEventEntities.get(j - 1);
+ roadEventEntities.set(j - 1, roadEventEntities.get(j));
+ roadEventEntities.set(j, v2XRoadEventEntity);
+ }
+ }
+ }
+ //输出日志查看结果
+// Log.w(TAG, "V2X===============================");
+// for (int i = 0; i < roadEventEntities.size(); i++) {
+// Log.w(TAG, "V2X===" +
+// "事件名称:" + roadEventEntities.get(i).getNoveltyInfo() +
+// "\t 事件距离:" + roadEventEntities.get(i).getDistance());
+// }
+ }
+
+ return roadEventEntities;
+ }
+
+ @Override
+ public void analysisV2XRoadEvent(MarkerCardResult markerCardResult) {
+ // 预警Window状态
+ boolean isShowEventWindow = V2XServiceManager.getMoGoV2XStatusManager().isRoadEventWindowShow();
+ // 预警按钮状态
+ boolean isShowEventButton = V2XServiceManager.getMoGoV2XStatusManager().isRoadEventButtonShow();
+ // 道路求助的window
+ boolean isOtherSeekHelpWindowShow = V2XServiceManager.getMoGoV2XStatusManager().isOtherSeekHelpWindowShow();
+ try {
+ //当没有预警提示的时候重新绘制地图POI点
+ if (markerCardResult != null && !isShowEventWindow && !isShowEventButton && !isOtherSeekHelpWindowShow) {
+ // 清除上次的道路事件, 这里注意,道路事件的触发和这里是异步操作会触发异常
+ mV2XRoadEventEntityArrayList.clear();
+
+ // 获取探路以及新鲜事儿
+ List exploreWayList = markerCardResult.getExploreWay();
+
+ if (exploreWayList != null) {
+ for (MarkerExploreWay markerExploreWay : exploreWayList) {
+ // 因为目前探路卡片不支持直播,所以这里做了过滤 @李小鹏
+ if (!markerExploreWay.getCanLive()) {
+ if (EventTypeUtils.isRoadEvent(markerExploreWay.getPoiType())) {
+ MarkerLocation markerLocation = markerExploreWay.getLocation();
+ // 记录道路事件
+ V2XRoadEventEntity v2XRoadEventEntity = new V2XRoadEventEntity();
+ v2XRoadEventEntity.setLocation(markerLocation);
+ // 探路目前只有上报拥堵
+ v2XRoadEventEntity.setPoiType(markerExploreWay.getPoiType());
+
+ v2XRoadEventEntity.setNoveltyInfo(markerExploreWay);
+ v2XRoadEventEntity.setExpireTime(20000);
+ mV2XRoadEventEntityArrayList.add(v2XRoadEventEntity);
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void drawableV2XMarker(MarkerCardResult markerCardResult) {
+ // 预警Window状态
+ boolean isShowEventWindow = V2XServiceManager.getMoGoV2XStatusManager().isRoadEventWindowShow();
+ // 预警按钮状态
+ boolean isShowEventButton = V2XServiceManager.getMoGoV2XStatusManager().isRoadEventButtonShow();
+ // 道路求助的window
+ boolean isOtherSeekHelpWindowShow = V2XServiceManager.getMoGoV2XStatusManager().isOtherSeekHelpWindowShow();
+ try {
+ //当没有预警提示的时候重新绘制地图POI点
+ if (!isShowEventWindow && !isShowEventButton && !isOtherSeekHelpWindowShow) {
+ // 清除上次的道路事件,TODO 这里注意,道路事件的触发和这里是异步操作会触发异常
+ //mV2XRoadEventEntityArrayList.clear();
+ // 清除原来的大而全的新鲜事儿
+ try {
+ clearALLPOI();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ // 清除连接线
+ V2XServiceManager.getMoGoV2XPolylineManager().clearLine();
+ if (markerCardResult != null) {
+ // 绘制新的地图气泡
+ List onlineCarList = markerCardResult.getOnlineCar();
+ List exploreWayList = markerCardResult.getExploreWay();
+ List noveltyInfoList = markerCardResult.getNoveltyInfo();
+
+ drawableOnlineCarPOI(onlineCarList, V2XMarkerClickListener.getInstance());
+ drawableExplorePOI(exploreWayList, V2XMarkerClickListener.getInstance());
+ drawableNoveltyPOI(noveltyInfoList, V2XMarkerClickListener.getInstance());
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void drawableOnlineCarPOI(List markerOnlineCars, IMogoMarkerClickListener clickListener) {
+ try {
+ if (markerOnlineCars != null) {
+ for (MarkerOnlineCar markerOnlineCar : markerOnlineCars) {
+ MarkerLocation markerLocation = markerOnlineCar.getLocation();
+
+ MarkerShowEntity markerShowEntity = new MarkerShowEntity();
+ markerShowEntity.setBindObj(markerOnlineCar);
+ markerShowEntity.setMarkerLocation(markerLocation);
+ markerShowEntity.setMarkerType(markerOnlineCar.getType());
+ markerShowEntity.setTextContent(markerOnlineCar.getUserInfo().getSafeLabel());
+ markerShowEntity.setIconUrl(markerOnlineCar.getUserInfo().getUserHead());
+
+ drawableMarker(V2XServiceManager.getContext(), markerShowEntity, clickListener);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearOnlineCarPOI() {
+ V2XServiceManager.getMarkerManager().removeMarkers(V2XConst.V2X_CARD_TYPE_USER_DATA);
+ }
+
+ @Override
+ public void drawableSpecialCarPOI(Context context, V2XMarkerEntity v2XMarkerEntity, IMogoMarkerClickListener clickListener) {
+ try {
+ MarkerUtils.resetMapZoom(15);
+ V2XServiceManager.getMapUIController().changeMapMode(EnumMapUI.NorthUP_2D);
+ V2XServiceManager.getMoGoV2XStatusManager().setOtherSeekHelpPOIShow(TAG, true);
+
+ // 绘制特殊车辆
+ if (v2XMarkerEntity == null) {
+ return;
+ }
+ MarkerLocation markerLocation = new MarkerLocation();
+ markerLocation.setLon(v2XMarkerEntity.getLon());
+ markerLocation.setLat(v2XMarkerEntity.getLat());
+
+ // 进行数据转换,用于Marker展示
+ V2XRoadEventEntity roadEventEntity = new V2XRoadEventEntity();
+ roadEventEntity.setPoiType(v2XMarkerEntity.getTargetId() + "");
+ roadEventEntity.setLocation(markerLocation);
+ roadEventEntity.setBindObj(v2XMarkerEntity);
+
+ // 重置告警信息
+ V2XServiceManager.getV2XStatusManager().setAlarmInfo(roadEventEntity);
+
+ // 清除原来的大而全的新鲜事儿
+ clearALLPOI();
+ if (roadEventEntity.getLocation() != null) {
+ // 道路事件,或者水波纹扩散效果
+ MogoMarkerOptions optionsRipple = new MogoMarkerOptions()
+ .object(roadEventEntity)
+ .latitude(roadEventEntity.getLocation().getLat())
+ .longitude(roadEventEntity.getLocation().getLon());
+ optionsRipple.anchor(0.5f, 0.5f);
+
+ // 由于性能问题,D车机不使用事件扩散动画
+ optionsRipple.icon(V2XMarkerAdapter.getV2XRoadEventViewPng(context, roadEventEntity));
+ mAlarmInfoMarker = V2XServiceManager.getMarkerManager().addMarker(V2X_EVENT_ALARM_POI, optionsRipple);
+ // 当前Marker设置为最上面
+ mAlarmInfoMarker.setToTop();
+// if (clickListener != null) {
+// mAlarmInfoMarker.setOnMarkerClickListener(clickListener);
+// }
+ // 绘制连接线
+ V2XServiceManager.getMoGoV2XPolylineManager().drawablePolyline(context, roadEventEntity);
+ } else {
+ Logger.e(MODULE_NAME, "Location 必须进行初始化!!!!!");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearSpecialCarPOI() {
+ boolean isOtherSeekHelpPOIShow = V2XServiceManager.getMoGoV2XStatusManager().isOtherSeekHelpPOIShow();
+ if (isOtherSeekHelpPOIShow) {
+ V2XServiceManager.getMoGoV2XStatusManager().setOtherSeekHelpPOIShow(TAG, false);
+ // 重置告警信息
+ V2XServiceManager.getV2XStatusManager().setAlarmInfo(null);
+ if (mAlarmInfoMarker != null) {
+ mAlarmInfoMarker.remove();
+ }
+ }
+ }
+
+ @Override
+ public void drawableLiveCarPOI(List markerOnlineCars, IMogoMarkerClickListener clickListener) {
+ try {
+ Logger.w(MODULE_NAME, "V2X===事件周边的直播车机:" + markerOnlineCars);
+ if (markerOnlineCars != null) {
+ for (MarkerOnlineCar markerOnlineCar : markerOnlineCars) {
+ // 设置车类型
+ markerOnlineCar.getUserInfo().setSafeLabelType(4);
+ markerOnlineCar.getCarInfo().setVehicleType(0);
+
+ MarkerLocation markerLocation = markerOnlineCar.getLocation();
+
+ MarkerShowEntity markerShowEntity = new MarkerShowEntity();
+ markerShowEntity.setBindObj(markerOnlineCar);
+ markerShowEntity.setMarkerLocation(markerLocation);
+ markerShowEntity.setMarkerType(V2XConst.V2X_MARKER_LIVE_CAR);
+ markerShowEntity.setTextContent(markerOnlineCar.getUserInfo().getSafeLabel());
+ markerShowEntity.setIconUrl(markerOnlineCar.getUserInfo().getUserHead());
+
+ drawableMarker(
+ V2XServiceManager.getContext(),
+ markerShowEntity,
+ clickListener);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearLiveCarPOI() {
+ V2XServiceManager.getMarkerManager().removeMarkers(V2XConst.V2X_MARKER_LIVE_CAR);
+ }
+
+ @Override
+ public void drawableExplorePOI(List exploreWayList, IMogoMarkerClickListener clickListener) {
+ try {
+ if (exploreWayList != null) {
+ for (MarkerExploreWay markerExploreWay : exploreWayList) {
+ // 因为目前探路卡片不支持直播,所以这里做了过滤 @李小鹏
+ if (!markerExploreWay.getCanLive()) {
+ MarkerLocation markerLocation = markerExploreWay.getLocation();
+
+ MarkerShowEntity markerShowEntity = new MarkerShowEntity();
+ markerShowEntity.setBindObj(markerExploreWay);
+ markerShowEntity.setMarkerLocation(markerLocation);
+ markerShowEntity.setMarkerType(markerExploreWay.getType());
+ markerShowEntity.setTextContent(markerExploreWay.getAddr());
+
+ drawableMarker(
+ V2XServiceManager.getContext(),
+ markerShowEntity,
+ clickListener);
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearExplorePOI() {
+ V2XServiceManager.getMarkerManager().removeMarkers(V2XConst.V2X_CARD_TYPE_ROAD_CONDITION);
+ }
+
+ @Override
+ public void drawableNoveltyPOI(List noveltyInfoList, IMogoMarkerClickListener clickListener) {
+ try {
+ if (noveltyInfoList != null) {
+ for (MarkerNoveltyInfo noveltyInfo : noveltyInfoList) {
+ MarkerLocation markerLocation = noveltyInfo.getLocation();
+
+ MarkerShowEntity markerShowEntity = new MarkerShowEntity();
+ markerShowEntity.setBindObj(noveltyInfo);
+ markerShowEntity.setMarkerLocation(markerLocation);
+ markerShowEntity.setMarkerType(noveltyInfo.getType());
+ markerShowEntity.setTextContent(noveltyInfo.getLocation().getAddress());
+
+ // 这里只绘制道路事件相关
+ switch (noveltyInfo.getPoiType()) {
+ case V2XPoiTypeEnum.TRAFFIC_CHECK:
+ case V2XPoiTypeEnum.ROAD_CLOSED:
+ case V2XPoiTypeEnum.FOURS_ROAD_WORK:
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP:
+ case V2XPoiTypeEnum.FOURS_PONDING:
+ case V2XPoiTypeEnum.FOURS_PARKING:
+ case V2XPoiTypeEnum.FOURS_ICE:
+ case V2XPoiTypeEnum.FOURS_FOG:
+ case V2XPoiTypeEnum.FOURS_ACCIDENT:
+ drawableMarker(
+ V2XServiceManager.getContext(),
+ markerShowEntity,
+ clickListener);
+ break;
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearNoveltyPOI() {
+ V2XServiceManager.getMarkerManager().removeMarkers(V2XConst.V2X_CARD_TYPE_NOVELTY);
+ }
+
+ @Override
+ public void drawableMarker(Context context, MarkerShowEntity markerShowEntity, IMogoMarkerClickListener clickListener) {
+// Logger.i(MODULE_NAME, "绘制Marker====drawMapMarker:" + markerShowEntity);
+ try {
+ if (markerShowEntity.getMarkerLocation() != null) {
+ MogoMarkerOptions options = new MogoMarkerOptions()
+ .object(markerShowEntity)
+ .latitude(markerShowEntity.getMarkerLocation().getLat())
+ .longitude(markerShowEntity.getMarkerLocation().getLon());
+
+ // 这里对Marker做下持有者转换,与大而全的区分开,由于绘制的时候用到了低层能力所以不去修改markerShowEntity中的type
+ switch (markerShowEntity.getMarkerType()) {
+ case ServiceConst.CARD_TYPE_USER_DATA:
+ options.owner(V2XConst.V2X_CARD_TYPE_USER_DATA);
+ break;
+ case ServiceConst.CARD_TYPE_NOVELTY:
+ options.owner(V2XConst.V2X_CARD_TYPE_NOVELTY);
+ break;
+ case ServiceConst.CARD_TYPE_ROAD_CONDITION:
+ options.owner(V2XConst.V2X_CARD_TYPE_ROAD_CONDITION);
+ break;
+ case V2XConst.V2X_MARKER_SPECIAL_CAR:
+ options.owner(V2XConst.V2X_MARKER_SPECIAL_CAR);
+ break;
+ case V2XConst.V2X_MARKER_LIVE_CAR:
+ options.owner(V2XConst.V2X_MARKER_LIVE_CAR);
+ break;
+ default:
+ options.owner(markerShowEntity.getMarkerType());
+ break;
+ }
+
+ IMogoMarker marker;
+ Bitmap bitmap;
+ if (markerShowEntity.getMarkerType().equals(ServiceConst.CARD_TYPE_USER_DATA)
+ || markerShowEntity.getMarkerType().equals(V2XConst.V2X_MARKER_SPECIAL_CAR)
+ || markerShowEntity.getMarkerType().equals(V2XConst.V2X_MARKER_LIVE_CAR)
+ || markerShowEntity.getMarkerType().equals(V2XConst.V2X_MARKER_EXPRESS)
+ ) {
+ bitmap = V2XMarkerAdapter.getV2XCarMarkerView(context, markerShowEntity);
+ options.icon(bitmap);
+ marker = V2XServiceManager.getMarkerManager().addMarker(markerShowEntity.getMarkerType(), options);
+ } else {
+ IMarkerView iMarkerView = MapMarkerAdapter.getMarkerView(context, markerShowEntity, options);
+ bitmap = ViewUtils.fromView(iMarkerView.getView());
+ options.icon(bitmap);
+ marker = V2XServiceManager.getMarkerManager().addMarker(markerShowEntity.getMarkerType(), options);
+ iMarkerView.setMarker(marker);
+ }
+ //marker.setOnMarkerClickListener(clickListener);
+ } else {
+ Logger.e(MODULE_NAME, "Location 必须进行初始化!!!!!");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public IMogoMarker openMarker(Context context, IMogoMarker currentMarker) {
+ try {
+ if (currentMarker != null) {
+ Object object = currentMarker.getObject();
+ if (object != null) {
+ // 修改数据
+ MarkerShowEntity showEntity = (MarkerShowEntity) object;
+ showEntity.setChecked(true);
+
+ // 获取数据对应的View
+ IMarkerView markerView = MapMarkerAdapter.getMarkerView(context,
+ showEntity,
+ currentMarker.getMogoMarkerOptions());
+
+ Bitmap bitmap;
+ if (showEntity.getMarkerType().equals(ServiceConst.CARD_TYPE_USER_DATA)
+ || showEntity.getMarkerType().equals(V2XConst.V2X_MARKER_SPECIAL_CAR)) {
+ bitmap = V2XMarkerAdapter.getV2XCarMarkerView(context, showEntity);
+ } else {
+ bitmap = ViewUtils.fromView(markerView.getView());
+ }
+
+ currentMarker.setIcon(bitmap);
+ currentMarker.setToTop();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return currentMarker;
+ }
+
+ @Override
+ public void closeMarker(Context context, IMogoMarker currentMarker) {
+ try {
+ if (currentMarker != null) {
+ Object object = currentMarker.getObject();
+ if (object != null) {
+ MarkerShowEntity showEntity = (MarkerShowEntity) object;
+ showEntity.setChecked(false);
+ IMarkerView markerView = MapMarkerAdapter.getMarkerView(context,
+ showEntity,
+ currentMarker.getMogoMarkerOptions());
+
+ Bitmap bitmap;
+ if (showEntity.getMarkerType().equals(ServiceConst.CARD_TYPE_USER_DATA)
+ || showEntity.getMarkerType().equals(V2XConst.V2X_MARKER_SPECIAL_CAR)) {
+ bitmap = V2XMarkerAdapter.getV2XCarMarkerView(context, showEntity);
+ } else {
+ bitmap = ViewUtils.fromView(markerView.getView());
+ }
+ currentMarker.setIcon(bitmap);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void drawableAlarmPOI(Context context, V2XRoadEventEntity roadEventEntity, IMogoMarkerClickListener clickListener) {
+ try {
+ MarkerUtils.resetMapZoom(15);
+ V2XServiceManager.getMapUIController().changeMapMode(EnumMapUI.NorthUP_2D);
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadEventPOIShow(TAG, true);
+ //Logger.i(MODULE_NAME, "绘制道路事件====drawableAlarmPOI:");
+ // 清除原来的大而全的新鲜事儿
+ clearALLPOI();
+ if (roadEventEntity.getLocation() != null) {
+ // 道路事件,或者水波纹扩散效果
+ MogoMarkerOptions optionsRipple = new MogoMarkerOptions()
+ .object(roadEventEntity)
+ .latitude(roadEventEntity.getLocation().getLat())
+ .longitude(roadEventEntity.getLocation().getLon());
+ optionsRipple.anchor(0.5f, 0.5f);
+
+ // 由于性能问题,D车机不使用事件扩散动画
+ optionsRipple.icon(V2XMarkerAdapter.getV2XRoadEventViewPng(context, roadEventEntity));
+
+ mAlarmInfoMarker = V2XServiceManager.getMarkerManager().addMarker(V2X_EVENT_ALARM_POI, optionsRipple);
+ // 当前Marker设置为最上面
+ mAlarmInfoMarker.setToTop();
+// if (clickListener != null) {
+// mAlarmInfoMarker.setOnMarkerClickListener(clickListener);
+// }
+ // 绘制连接线
+ V2XServiceManager.getMoGoV2XPolylineManager().drawablePolyline(context, roadEventEntity);
+ } else {
+ Logger.e(MODULE_NAME, "Location 必须进行初始化!!!!!");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearAlarmPOI() {
+ boolean isRoadEventPOIShow = V2XServiceManager.getMoGoV2XStatusManager().isRoadEventPOIShow();
+ if (isRoadEventPOIShow) {
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadEventPOIShow(TAG, false);
+ if (mAlarmInfoMarker != null) {
+ mAlarmInfoMarker.remove();
+ }
+ }
+ }
+
+ @Override
+ public void clearALLPOI() {
+ try {
+ V2XServiceManager.getMarkerManager().removeMarkers();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void init(Context context) {
+
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XPolylineManager.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XPolylineManager.java
new file mode 100644
index 0000000000..ffbcecf49e
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XPolylineManager.java
@@ -0,0 +1,104 @@
+package com.mogo.module.v2x.manager.impl;
+
+import android.content.Context;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.mogo.map.MogoLatLng;
+import com.mogo.map.overlay.IMogoPolyline;
+import com.mogo.map.overlay.MogoPolylineOptions;
+import com.mogo.module.v2x.MoGoV2XServicePaths;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.manager.IMoGoV2XPolylineManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/17 10:00 PM
+ * desc : 当前车辆与道路事件的连接线
+ * version: 1.0
+ */
+@Route(path = MoGoV2XServicePaths.PATH_V2X_POLYLINE_MANAGER)
+public class MoGoV2XPolylineManager implements IMoGoV2XPolylineManager {
+ private static final String TAG = "MoGoV2XPolylineManager";
+ private static IMogoPolyline mMogoPolyline;
+
+ @Override
+ public void drawablePolyline(Context context, V2XRoadEventEntity roadEventEntity) {
+ try {
+ if (mMogoPolyline != null) {
+ mMogoPolyline.remove();
+ }
+ if ((V2XServiceManager.getMoGoV2XStatusManager().isRoadEventPOIShow()
+ || V2XServiceManager.getMoGoV2XStatusManager().isOtherSeekHelpPOIShow())
+ && V2XServiceManager.getV2XStatusManager().getTargetMoGoLatLng() != null) {
+ // 连接线参数
+ MogoPolylineOptions options = new MogoPolylineOptions();
+
+ // 渐变色
+ List colors = new ArrayList<>();
+
+ switch (roadEventEntity.getPoiType()) {
+ case V2XPoiTypeEnum.ALERT_TRAFFIC_LIGHT_SUGGEST:
+ case V2XPoiTypeEnum.ALERT_TRAFFIC_LIGHT_WARNING:
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP:
+ colors.add(0xFFFA8C34);
+ colors.add(0xFFBD6D36);
+ colors.add(0xFFFA8C34);
+ break;
+ default:
+ colors.add(0xFFF95959);
+ colors.add(0xFF942B48);
+ colors.add(0xFFCB253A);
+ break;
+ }
+
+ // 线条粗细,渐变,渐变色值
+ options.width(10).useGradient(true).colorValues(colors);
+
+ // 当前车辆位置
+ MogoLatLng carLocation = V2XServiceManager.getNavi().getCarLocation();
+ if (carLocation != null) {
+ options.add(carLocation);
+ } else {
+ options.add(V2XServiceManager.getV2XStatusManager().getLocation());
+ }
+ // 目标车辆位置
+ options.add(V2XServiceManager.getV2XStatusManager().getTargetMoGoLatLng());
+
+ // 绘制线的对象
+ mMogoPolyline = V2XServiceManager.getMogoOverlayManager().addPolyline(options);
+
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearLine() {
+ if (mMogoPolyline != null) {
+ mMogoPolyline.remove();
+ mMogoPolyline = null;
+ V2XServiceManager.getV2XStatusManager().setAlarmInfo(null);
+ }
+ }
+
+ @Override
+ public void init(Context context) {
+
+ }
+
+ /**
+ * @return 绘制连接线的对象
+ */
+ @Override
+ public IMogoPolyline getMogoPolyline() {
+ return mMogoPolyline;
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XStatusManager.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XStatusManager.java
new file mode 100644
index 0000000000..e45906f085
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/manager/impl/MoGoV2XStatusManager.java
@@ -0,0 +1,256 @@
+package com.mogo.module.v2x.manager.impl;
+
+import android.content.Context;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.mogo.module.v2x.MoGoV2XServicePaths;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.manager.IMoGoV2XStatusChangedListener;
+import com.mogo.module.v2x.manager.IMoGoV2XStatusManager;
+import com.mogo.module.v2x.manager.V2XStatusDescriptor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/17 7:34 PM
+ * desc :
+ * version: 1.0
+ */
+@Route(path = MoGoV2XServicePaths.PATH_V2X_STATUS_MANAGER)
+public class MoGoV2XStatusManager implements IMoGoV2XStatusManager {
+ private static final String TAG = "MoGoV2XStatusManager";
+
+ /**
+ * 状态记录
+ */
+ private static final Map mStatus = new ConcurrentHashMap<>();
+
+ /**
+ * 回调集合
+ */
+ private static final Map> mListeners = new ConcurrentHashMap<>();
+
+ /**
+ * 状态类型修改记录
+ */
+ private static final Map mModifier = new ConcurrentHashMap<>();
+
+ // 查询状态存储情况
+ private boolean get_bool_val(V2XStatusDescriptor descriptor) {
+ Boolean val = mStatus.get(descriptor);
+ return val == null ? false : val;
+ }
+
+ @Override
+ public boolean isRoadEventPOIShow() {
+ return get_bool_val(V2XStatusDescriptor.RoadEventPOI_UI);
+ }
+
+ @Override
+ public boolean isRoadEventButtonShow() {
+ return get_bool_val(V2XStatusDescriptor.RoadEventButton_UI);
+ }
+
+ @Override
+ public boolean isRoadEventWindowShow() {
+ return get_bool_val(V2XStatusDescriptor.RoadEventWindow_UI);
+ }
+
+ @Override
+ public boolean isRoadLiveCarWindowShow() {
+ return get_bool_val(V2XStatusDescriptor.RoadLiveCarWindow_UI);
+ }
+
+ @Override
+ public boolean isOtherSeekHelpWindowShow() {
+ return get_bool_val(V2XStatusDescriptor.OtherSeekHelpWindow_UI);
+ }
+
+ @Override
+ public boolean isMeSeekHelpButtonShow() {
+ return get_bool_val(V2XStatusDescriptor.MeSeekHelpButton_UI);
+ }
+
+ @Override
+ public boolean isOtherSeekHelpPOIShow() {
+ return get_bool_val(V2XStatusDescriptor.OtherSeekHelpPOI_UI);
+ }
+
+ @Override
+ public boolean isV2XAnimationShow() {
+ return get_bool_val(V2XStatusDescriptor.V2XAnimationWindow_UI);
+ }
+
+ @Override
+ public boolean isLeftLiveVideoShow() {
+ return get_bool_val(V2XStatusDescriptor.LiveCarWindow_UI);
+ }
+
+ @Override
+ public boolean isPushWindowShow() {
+ return get_bool_val(V2XStatusDescriptor.PushWindow_UI);
+ }
+
+ @Override
+ public boolean isPushPOIShow() {
+ return get_bool_val(V2XStatusDescriptor.PushWindowPOI_UI);
+ }
+
+ @Override
+ public boolean isFatigueDrivingWindowShow() {
+ return get_bool_val(V2XStatusDescriptor.FatigueDrivingWindow_UI);
+ }
+
+ @Override
+ public void setRoadEventPOIShow(String tag, boolean show) {
+ V2XServiceManager.getMoGoStatusManager().setV2XUIShow(V2XConst.MODULE_NAME, show);
+ mStatus.put(V2XStatusDescriptor.RoadEventPOI_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.RoadEventPOI_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.RoadEventPOI_UI);
+ }
+
+ @Override
+ public void setRoadEventButtonShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.RoadEventButton_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.RoadEventButton_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.RoadEventButton_UI);
+ }
+
+ @Override
+ public void setRoadEventWindowShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.RoadEventWindow_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.RoadEventWindow_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.RoadEventWindow_UI);
+ }
+
+ @Override
+ public void setRoadLiveCarWindowShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.RoadLiveCarWindow_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.RoadLiveCarWindow_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.RoadLiveCarWindow_UI);
+ }
+
+ @Override
+ public void setOtherSeekHelpWindowShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.OtherSeekHelpWindow_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.OtherSeekHelpWindow_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.OtherSeekHelpWindow_UI);
+ }
+
+ @Override
+ public void setMeSeekHelpButtonShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.MeSeekHelpButton_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.MeSeekHelpButton_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.MeSeekHelpButton_UI);
+ }
+
+ @Override
+ public void setOtherSeekHelpPOIShow(String tag, boolean show) {
+ V2XServiceManager.getMoGoStatusManager().setV2XUIShow(V2XConst.MODULE_NAME, show);
+ mStatus.put(V2XStatusDescriptor.OtherSeekHelpPOI_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.OtherSeekHelpPOI_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.OtherSeekHelpPOI_UI);
+ }
+
+ @Override
+ public void setV2XAnimationWindowShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.V2XAnimationWindow_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.V2XAnimationWindow_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.V2XAnimationWindow_UI);
+ }
+
+ @Override
+ public void setLiveCarWindowShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.LiveCarWindow_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.LiveCarWindow_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.LiveCarWindow_UI);
+ }
+
+
+ @Override
+ public void setPushWindowShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.PushWindow_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.PushWindow_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.PushWindow_UI);
+ }
+
+ @Override
+ public void setPushPOIShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.PushWindowPOI_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.PushWindowPOI_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.PushWindowPOI_UI);
+ }
+
+ @Override
+ public void setFatigueDrivingWindowShow(String tag, boolean show) {
+ mStatus.put(V2XStatusDescriptor.FatigueDrivingWindow_UI, show);
+ invokeStatusChangedListener(V2XStatusDescriptor.FatigueDrivingWindow_UI, show);
+ recordStatusModifier(tag, V2XStatusDescriptor.FatigueDrivingWindow_UI);
+ }
+
+ @Override
+ public void registerStatusChangedListener(String tag, V2XStatusDescriptor descriptor, IMoGoV2XStatusChangedListener listener) {
+ if (listener == null || descriptor == null) {
+ return;
+ }
+ if (!mListeners.containsKey(descriptor)) {
+ mListeners.put(descriptor, new ArrayList<>());
+ }
+ if (mListeners.get(descriptor) == null) {
+ mListeners.put(descriptor, new ArrayList<>());
+ }
+ List listeners = mListeners.get(descriptor);
+ if (listeners != null) {
+ listeners.add(listener);
+ }
+ }
+
+ @Override
+ public void unregisterStatusChangedListener(String tag, V2XStatusDescriptor descriptor, IMoGoV2XStatusChangedListener listener) {
+ List listeners = mListeners.get(descriptor);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
+
+ /**
+ * 调用所有存储的监听
+ *
+ * @param descriptor
+ * @param status
+ */
+ private void invokeStatusChangedListener(V2XStatusDescriptor descriptor, boolean status) {
+ if (mListeners.containsKey(descriptor)) {
+ List listeners = mListeners.get(descriptor);
+ if (listeners != null) {
+ for (IMoGoV2XStatusChangedListener listener : listeners) {
+ if (listener != null) {
+ listener.onStatusChanged(descriptor, status);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 记录状态
+ *
+ * @param tag
+ * @param descriptor
+ */
+ private void recordStatusModifier(String tag, V2XStatusDescriptor descriptor) {
+ mModifier.put(descriptor, tag);
+ }
+
+ @Override
+ public void init(Context context) {
+
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerAdapter.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerAdapter.java
new file mode 100644
index 0000000000..2e8cf7d8a1
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerAdapter.java
@@ -0,0 +1,147 @@
+package com.mogo.module.v2x.marker;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+
+import com.mogo.module.common.entity.MarkerShowEntity;
+import com.mogo.module.v2x.R;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+
+import java.util.ArrayList;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020-01-1015:55
+ * desc : V2X 地图气泡
+ * version: 1.0
+ */
+public class V2XMarkerAdapter {
+
+ /**
+ * 返回道路事件
+ */
+ public static Bitmap getV2XRoadEventMarkerView(Context context, V2XRoadEventEntity alarmInfo) {
+ return new V2XMarkerRoadEventView(context, alarmInfo).getView();
+ }
+
+ /**
+ * 返回道路事件
+ */
+ public static Bitmap getV2XRoadEventMarkerView(Context context, V2XRoadEventEntity alarmInfo, int imageRes) {
+ return new V2XMarkerRoadEventView(context, alarmInfo).setBackground(imageRes).getView();
+ }
+
+ /**
+ * 返回道路事件静态的图,因为D车机性能不行,会卡顿
+ */
+ public static Bitmap getV2XRoadEventViewPng(Context context, V2XRoadEventEntity alarmInfo) {
+ Bitmap bitmap;
+ switch (alarmInfo.getPoiType()) {
+ case V2XPoiTypeEnum.ALERT_TRAFFIC_LIGHT_SUGGEST:
+ case V2XPoiTypeEnum.ALERT_TRAFFIC_LIGHT_WARNING:
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP:
+ bitmap = getV2XRoadEventMarkerView(
+ context,
+ alarmInfo,
+ R.drawable.v_to_x_warning_circle_orange_00009);
+ break;
+ default:
+ bitmap = getV2XRoadEventMarkerView(
+ context,
+ alarmInfo,
+ R.drawable.v_to_x_warning_circle_red_00009);
+ break;
+ }
+ return bitmap;
+ }
+
+ /**
+ * 返回道路事件gif序列图集合
+ */
+ public static ArrayList getV2XRoadEventViewGif(Context context, V2XRoadEventEntity alarmInfo) {
+ ArrayList bitmapArrayList;
+ switch (alarmInfo.getPoiType()) {
+ case V2XPoiTypeEnum.ALERT_TRAFFIC_LIGHT_SUGGEST:
+ case V2XPoiTypeEnum.ALERT_TRAFFIC_LIGHT_WARNING:
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP:
+ bitmapArrayList = getV2XRoadEventOrangeMarkerView(context, alarmInfo);
+ break;
+ default:
+ bitmapArrayList = getV2XRoadEventRedMarkerView(context, alarmInfo);
+ break;
+ }
+ return bitmapArrayList;
+ }
+
+ /**
+ * 返回红色扩散效果的序列
+ */
+ public static ArrayList getV2XRoadEventRedMarkerView(Context context, V2XRoadEventEntity alarmInfo) {
+ ArrayList icons = new ArrayList<>();
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00000));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00001));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00002));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00003));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00004));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00005));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00006));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00007));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00008));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00009));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00010));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00011));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00012));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00013));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00015));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00017));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00018));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00020));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_red_00023));
+ return icons;
+ }
+
+ /**
+ * 返回橘色色扩散效果的序列
+ */
+ public static ArrayList getV2XRoadEventOrangeMarkerView(Context context, V2XRoadEventEntity alarmInfo) {
+ ArrayList icons = new ArrayList<>();
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00000));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00001));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00002));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00003));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00004));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00005));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00006));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00007));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00008));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00009));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00010));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00011));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00012));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00013));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00015));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00017));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00018));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00019));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00020));
+ icons.add(V2XMarkerAdapter.getV2XRoadEventMarkerView(context, alarmInfo, R.drawable.v_to_x_warning_circle_orange_00023));
+ return icons;
+ }
+
+ /**
+ * 获取模拟点
+ *
+ * @param context 上下文
+ * @return MarkerView
+ */
+ public static Bitmap getV2XCarMarkerView(Context context, MarkerShowEntity showEntity) {
+ if (showEntity.isChecked()) {
+ return new V2XMarkerCarInfoView(context, showEntity).getView();
+ } else {
+ return new V2XMarkerCarView(context, showEntity).getView();
+ }
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerCarInfoView.kt b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerCarInfoView.kt
new file mode 100644
index 0000000000..6ed65b7a18
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerCarInfoView.kt
@@ -0,0 +1,145 @@
+package com.mogo.module.v2x.marker
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.mogo.module.common.entity.MarkerOnlineCar
+import com.mogo.module.common.entity.MarkerShowEntity
+import com.mogo.module.service.utils.ViewUtils
+import com.mogo.module.v2x.R
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity
+import com.mogo.module.common.entity.V2XPoiTypeEnum
+import kotlinx.android.synthetic.main.view_marker_car.view.*
+import kotlinx.android.synthetic.main.view_marker_car_info.view.*
+import kotlinx.android.synthetic.main.view_marker_car_info.view.ivCar
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020-01-0619:55
+ * desc : 绘制在线车辆,特殊车辆
+ * version: 1.0
+ */
+class V2XMarkerCarInfoView(context: Context, showEntity: MarkerShowEntity) :
+ ConstraintLayout(context) {
+
+ init {
+ initView(context, showEntity)
+ }
+
+ private fun initView(context: Context, showEntity: MarkerShowEntity) {
+ val bindObj: Any = showEntity.bindObj
+ if (bindObj is MarkerOnlineCar) {
+ //1 老司机 2 安全驾驶 3 危险驾驶 4 可直播车辆
+ when (bindObj.userInfo.safeLabelType) {
+ // 可直播车机
+ 4 -> {
+ LayoutInflater.from(context)
+ .inflate(R.layout.view_marker_car, this)
+ ivMarkerTip.setImageResource(R.drawable.v_to_x_marker_car_live_vedio)
+ }
+ else -> {
+ LayoutInflater.from(context)
+ .inflate(R.layout.view_marker_car_info, this)
+ }
+ }
+ }
+ if (bindObj is V2XMarkerEntity) {
+ LayoutInflater.from(context)
+ .inflate(R.layout.view_marker_car, this)
+ }
+ updateUI(showEntity)
+ }
+
+ fun updateUI(showEntity: MarkerShowEntity) {
+ ivCar.rotation = showEntity.markerLocation.angle.toFloat()
+// ivCar.rotation = 90f
+
+ tvUserMarker.text = if (TextUtils.isEmpty(showEntity.textContent)) {
+ ""
+ } else {
+ showEntity.textContent
+ }
+
+ val bindObj: Any = showEntity.bindObj
+ if (bindObj is MarkerOnlineCar) {
+ tvMarkerContent.text =
+ if (bindObj.userInfo == null && TextUtils.isEmpty(bindObj.userInfo.userName)) {
+ "蘑菇车主"
+ } else {
+ bindObj.userInfo.userName
+ }
+
+ //车辆类型,0-普通车辆,1-警车,2-救护车,3-道路救援车辆',
+ when (bindObj.carInfo.vehicleType) {
+ // 普通车
+ 0 -> {
+ clMarkerContent.visibility = View.VISIBLE
+ ivReverseTriangle.visibility = View.VISIBLE
+ ivCar.setImageResource(R.drawable.icon_car_gray)
+ }
+ // 警车
+ 1 -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_police)
+ }
+ // 救护车
+ 2 -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_ambulance)
+ }
+ // 道路救援车辆
+ 3 -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.v_to_x_warning_car_red)
+ }
+ else -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_gray)
+ }
+ }
+ }
+ if (bindObj is V2XMarkerEntity) {
+ when (bindObj.targetId) {
+ 10001 -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_gray)
+ }
+ 10002 -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_ambulance)
+ }
+ 10003 -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_police)
+ }
+ //失控车
+ 10004 -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.v_to_x_warning_car_red)
+ }
+ // 故障车
+ V2XPoiTypeEnum.ALERT_CAR_TROUBLE_WARNING -> {
+ clMarkerContent.visibility = View.GONE
+ ivReverseTriangle.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.v_to_x_warning_car_red)
+ }
+ }
+ }
+ }
+
+ fun getView(): Bitmap {
+ return ViewUtils.fromView(this)
+ }
+}
\ No newline at end of file
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerCarView.kt b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerCarView.kt
new file mode 100644
index 0000000000..d283ff7692
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerCarView.kt
@@ -0,0 +1,115 @@
+package com.mogo.module.v2x.marker
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.view.LayoutInflater
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.mogo.module.common.entity.MarkerOnlineCar
+import com.mogo.module.common.entity.MarkerShowEntity
+import com.mogo.module.service.utils.ViewUtils
+import com.mogo.module.v2x.R
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity
+import com.mogo.module.common.entity.V2XPoiTypeEnum
+import kotlinx.android.synthetic.main.view_marker_car.view.*
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020-01-0619:55
+ * desc : 绘制在线车辆,特殊车辆
+ * version: 1.0
+ */
+class V2XMarkerCarView(context: Context, showEntity: MarkerShowEntity) :
+ ConstraintLayout(context) {
+
+ init {
+ initView(context, showEntity)
+ }
+
+ private fun initView(context: Context, showEntity: MarkerShowEntity) {
+ LayoutInflater.from(context)
+ .inflate(R.layout.view_marker_car, this)
+ updateUI(showEntity)
+ }
+
+ fun updateUI(showEntity: MarkerShowEntity) {
+ ivCar.rotation = showEntity.markerLocation.angle.toFloat()
+
+ val bindObj: Any = showEntity.bindObj
+ if (bindObj is MarkerOnlineCar) {
+ //1 老司机 2 安全驾驶 3 危险驾驶 4 可直播车辆
+ when (bindObj.userInfo.safeLabelType) {
+ 1 -> {
+ ivMarkerTip.setImageResource(R.drawable.v_to_x_marker_car_blue)
+ }
+ 2 -> {
+ ivMarkerTip.setImageResource(R.drawable.v_to_x_marker_car_green)
+ }
+ 3 -> {
+ ivMarkerTip.setImageResource(R.drawable.v_to_x_marker_car_red)
+ }
+ 4 -> {
+ ivMarkerTip.setImageResource(R.drawable.v_to_x_marker_car_live_vedio)
+ }
+ }
+
+ //车辆类型,0-普通车辆,1-警车,2-救护车,3-道路救援车辆',
+ when (bindObj.carInfo.vehicleType) {
+ // 普通车
+ 0 -> {
+ ivMarkerTip.visibility = View.VISIBLE
+ ivCar.setImageResource(R.drawable.icon_car_gray)
+ }
+ // 警车
+ 1 -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_police)
+ }
+ // 救护车
+ 2 -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_ambulance)
+ }
+ // 道路救援车辆
+ 3 -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.v_to_x_warning_car_red)
+ }
+ else -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_gray)
+ }
+ }
+ }
+ if (bindObj is V2XMarkerEntity) {
+ when (bindObj.targetId) {
+ 10001 -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_gray)
+ }
+ 10002 -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_ambulance)
+ }
+ 10003 -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.icon_car_police)
+ }
+ 10004 -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.v_to_x_warning_car_red)
+ }
+ // 故障车
+ V2XPoiTypeEnum.ALERT_CAR_TROUBLE_WARNING -> {
+ ivMarkerTip.visibility = View.GONE
+ ivCar.setImageResource(R.drawable.v_to_x_warning_car_red)
+ }
+ }
+ }
+ }
+
+ fun getView(): Bitmap {
+ return ViewUtils.fromView(this)
+ }
+}
\ No newline at end of file
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerRoadEventView.kt b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerRoadEventView.kt
new file mode 100644
index 0000000000..3059bcd6a7
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/marker/V2XMarkerRoadEventView.kt
@@ -0,0 +1,126 @@
+package com.mogo.module.v2x.marker
+
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.view.LayoutInflater
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.mogo.module.service.utils.ViewUtils
+import com.mogo.module.v2x.R
+import com.mogo.module.common.entity.V2XPoiTypeEnum
+import com.mogo.module.common.entity.V2XRoadEventEntity
+import kotlinx.android.synthetic.main.view_marker_event_car.view.*
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020-01-0619:55
+ * desc :
+ * 3、道路事件
+ * 2.18 演示 :驾驶模式中地图展示“拥堵“、“施工”;
+ * (ADAS模式下还包含 原有的拥堵、交通检查、封路事件)
+ * version: 1.0
+ */
+class V2XMarkerRoadEventView(context: Context, alarmInfo: V2XRoadEventEntity) :
+ ConstraintLayout(context) {
+ val TAG = "V2XMarkerRoadEventView"
+
+ init {
+ initView(context, alarmInfo)
+ }
+
+ fun initView(context: Context, alarmInfo: V2XRoadEventEntity) {
+ if (alarmInfo.poiType == V2XPoiTypeEnum.ALERT_FRONT_CAR ||
+ alarmInfo.poiType == V2XPoiTypeEnum.ALERT_CAR_TROUBLE_WARNING.toString()
+ ) {
+ LayoutInflater.from(context)
+ .inflate(R.layout.view_marker_event_car, this)
+ } else {
+ LayoutInflater.from(context)
+ .inflate(R.layout.view_marker_event_road, this)
+ }
+ updateIcon(alarmInfo)
+ }
+
+ /**
+ * @see V2XPoiTypeEnum
+ */
+ private fun updateIcon(alarmInfo: V2XRoadEventEntity) {
+ //Logger.d(MODULE_NAME, alarmInfo.toString())
+ // 道路施工、积水、路面结冰、浓雾、事故、拥堵
+ when (alarmInfo.poiType) {
+ //交通检查
+ V2XPoiTypeEnum.TRAFFIC_CHECK -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_2)
+ }
+ //封路
+ V2XPoiTypeEnum.ROAD_CLOSED -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_16)
+ }
+ //施工
+ V2XPoiTypeEnum.FOURS_ROAD_WORK -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_11)
+ }
+ //拥堵
+ V2XPoiTypeEnum.FOURS_BLOCK_UP -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_5)
+ }
+ //积水
+ V2XPoiTypeEnum.FOURS_PONDING -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_6)
+ }
+ //浓雾
+ V2XPoiTypeEnum.FOURS_FOG -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_9)
+ }
+ //结冰
+ V2XPoiTypeEnum.FOURS_ICE -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_8)
+ }
+ //事故
+ V2XPoiTypeEnum.FOURS_ACCIDENT -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_7)
+ }
+ //红绿灯数据
+ V2XPoiTypeEnum.ALERT_TRAFFIC_LIGHT_SUGGEST -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_3)
+ }
+ //红绿灯数据
+ V2XPoiTypeEnum.ALERT_TRAFFIC_LIGHT_WARNING -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_3)
+ }
+ //前方静止or慢速车辆报警
+ V2XPoiTypeEnum.ALERT_FRONT_CAR -> {
+ ivCar.setImageResource(R.drawable.v_to_x_warning_car_red)
+ }
+ // 故障车辆
+ V2XPoiTypeEnum.ALERT_CAR_TROUBLE_WARNING.toString() -> {
+ ivCar.setImageResource(R.drawable.icon_car_red)
+ ivCarTop.visibility = View.VISIBLE
+ }
+ // 取快递
+ V2XPoiTypeEnum.ALERT_TRAFFIC_EXPRESS -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_express)
+ ivBg.visibility = View.GONE
+ }
+ // 顺风车
+ V2XPoiTypeEnum.ALERT_TRAFFIC_TAXI -> {
+ ivCar.setImageResource(R.drawable.v_to_x_marker_taxi)
+ ivBg.visibility = View.GONE
+ }
+ }
+ }
+
+ /**
+ * 背景
+ */
+ fun setBackground(imageRes: Int): V2XMarkerRoadEventView {
+ ivBg.setImageResource(imageRes)
+ return this
+ }
+
+ fun getView(): Bitmap {
+ return ViewUtils.fromView(this)
+ }
+}
\ No newline at end of file
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/HttpConstant.kt b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/HttpConstant.kt
new file mode 100644
index 0000000000..dcc0b492d6
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/HttpConstant.kt
@@ -0,0 +1,23 @@
+package com.mogo.module.v2x.network
+
+import com.mogo.commons.debug.DebugConfig
+
+class HttpConstant {
+
+ companion object {
+ const val HOST_DEV = "http://dzt-test.zhidaohulian.com"
+ const val HOST_TEST = "http://dzt-test.zhidaohulian.com"
+ const val HOST_DEMO = "http://dzt-show.zhidaohulian.com"
+ const val HOST_PRODUCT = "https://dzt.zhidaohulian.com"
+
+ fun getNetHost(): String {
+ return when (DebugConfig.getNetMode()) {
+ DebugConfig.NET_MODE_DEV -> HOST_DEV
+ DebugConfig.NET_MODE_QA -> HOST_TEST
+ DebugConfig.NET_MODE_DEMO -> HOST_DEMO
+ else -> HOST_PRODUCT
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XApiService.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XApiService.java
new file mode 100644
index 0000000000..40e84f3b7d
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XApiService.java
@@ -0,0 +1,111 @@
+package com.mogo.module.v2x.network;
+
+import com.mogo.commons.data.BaseData;
+import com.mogo.module.common.entity.MarkerCardResult;
+import com.mogo.module.common.entity.MarkerResponse;
+import com.mogo.module.v2x.entity.net.V2XUserInfoRes;
+import com.mogo.module.v2x.entity.net.V2XDemoUserInfoRes;
+import com.mogo.module.v2x.entity.net.V2XLiveCarRes;
+import com.mogo.module.v2x.entity.net.V2XLivePushVoRes;
+import com.mogo.module.v2x.entity.net.V2XSeekHelpRes;
+import com.mogo.module.v2x.entity.net.V2XStrategyPushRes;
+
+import java.util.Map;
+
+import io.reactivex.Observable;
+import okhttp3.RequestBody;
+import retrofit2.http.Body;
+import retrofit2.http.FieldMap;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
+import retrofit2.http.Headers;
+import retrofit2.http.POST;
+
+/**
+ * @author congtaowang
+ * @since 2020-01-03
+ *
+ * 接口描述
+ */
+public interface V2XApiService {
+ /**
+ * 直播心跳
+ */
+ @FormUrlEncoded
+ @POST("/dataService/integratedServices/app/push/no/heartbeat/v1")
+ Observable refreshHeartBeat(@FieldMap Map liveBroadcast);
+
+ /**
+ * 刷新地图气泡点
+ */
+ @FormUrlEncoded
+ @POST("/yycp-launcherSnapshot/launcherSnapshot/querySnapshotAsync")
+ Observable querySnapshotAsync(@FieldMap Map parameters);
+
+ /**
+ * 直播点赞
+ */
+ @FormUrlEncoded
+ @POST("/yycp-geo-fence-carService/restrictedRules/car/liveBroadcastPraise")
+ Observable giveLikeLiveVideo(@FieldMap Map parameters);
+
+ /**
+ * 同过SN获取用户信息
+ */
+ @Headers({"Content-Type:application/json", "Accept:application/json"})
+ @POST("/yycp-realtimeLocations/realTimeLocationServer/queryUserInfoBySn")
+ Observable queryUserInfoBySn(@Body RequestBody jsonStr);
+
+ /**
+ * 车辆故障求助查询
+ *
+ * @param param
+ * @return
+ */
+ @FormUrlEncoded
+ @POST("/yycp-realtimeLocations/vehicleTypeManage/car/queryVehicleType/v1")
+ Observable queryHelpSignal(@FieldMap Map param);
+
+ /**
+ * 车辆故障求助查询
+ *
+ * @param param
+ * @return
+ */
+ @FormUrlEncoded
+ @POST("/yycp-realtimeLocations/vehicleTypeManage/car/updateVehicleType/v1")
+ Observable sendHelpSignal(@FieldMap Map param);
+
+ /**
+ * 根据经纬度查询附近可直播车机直播信息
+ */
+ @FormUrlEncoded
+ @POST("/yycp-realtimeLocations/realTimeLocationServer/car/queryNearbyVehicleLiveByLocation/v1")
+ Observable queryNearbyVehicleLiveByLocation(@FieldMap Map parameters);
+
+ /**
+ * 推流与停止推流接口
+ */
+ @FormUrlEncoded
+ @POST("/dataService/integratedServices/app/push/no/livePush/v1")
+ Observable livePush(@FieldMap Map parameters);
+
+ /**
+ * TODO 查询演示车用户信息
+ */
+ @GET("/yycp-launcherSnapshot/mock/getMockUserInfos")
+ Observable getMockUserInfos();
+
+ /**
+ * 根据名称获取策略详情
+ */
+ @GET("/yycp-strategyPush/push/strategy/item?name=TIRE_DRIVING")
+ Observable getStrategyPush();
+
+ /**
+ * 违章地段查询接口
+ */
+ @FormUrlEncoded
+ @POST("/yycp-launcherSnapshot/launcherSnapshot/queryIllegalPark")
+ Observable queryIllegalPark(@FieldMap Map parameters);
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XRefreshCallback.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XRefreshCallback.java
new file mode 100644
index 0000000000..f17d3d6672
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XRefreshCallback.java
@@ -0,0 +1,14 @@
+package com.mogo.module.v2x.network;
+
+/**
+ * @author congtaowang
+ * @since 2020-01-03
+ *
+ * 刷新回调
+ */
+public interface V2XRefreshCallback {
+
+ void onSuccess(T result);
+
+ void onFail(String msg);
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XRefreshModel.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XRefreshModel.java
new file mode 100644
index 0000000000..c3bf82a52e
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/network/V2XRefreshModel.java
@@ -0,0 +1,477 @@
+package com.mogo.module.v2x.network;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.mogo.commons.data.BaseData;
+import com.mogo.commons.network.ParamsProvider;
+import com.mogo.commons.network.SubscribeImpl;
+import com.mogo.commons.network.Utils;
+import com.mogo.map.MogoLatLng;
+import com.mogo.module.common.entity.MarkerResponse;
+import com.mogo.module.service.ServiceConst;
+import com.mogo.module.service.network.RefreshBody;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.entity.net.V2XDemoUserInfoRes;
+import com.mogo.module.v2x.entity.net.V2XLiveCarBroadcastReq;
+import com.mogo.module.v2x.entity.net.V2XLiveCarRes;
+import com.mogo.module.v2x.entity.net.V2XLivePushVoRes;
+import com.mogo.module.v2x.entity.net.V2XSeekHelpRes;
+import com.mogo.module.v2x.entity.net.V2XStrategyPushRes;
+import com.mogo.module.v2x.entity.net.V2XUserInfoRes;
+import com.mogo.service.MogoServicePaths;
+import com.mogo.service.network.IMogoNetwork;
+import com.mogo.utils.logger.Logger;
+import com.mogo.utils.network.RequestOptions;
+import com.zhidao.utils.common.GsonUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.RequestBody;
+
+
+/**
+ * 数据接口API
+ *
+ * @author donghongyu
+ * @since 2020-01-03
+ */
+public class V2XRefreshModel {
+
+ private Context mContext;
+ private V2XApiService mV2XApiService;
+ private static V2XRefreshModel mV2XRefreshModel;
+
+ private V2XRefreshModel() {
+ }
+
+ public synchronized static V2XRefreshModel getInstance(Context context) {
+ if (mV2XRefreshModel == null) {
+ synchronized (V2XRefreshModel.class) {
+ if (mV2XRefreshModel == null) {
+ mV2XRefreshModel = new V2XRefreshModel();
+ mV2XRefreshModel.init(context);
+ }
+ }
+ }
+ return mV2XRefreshModel;
+ }
+
+ private void init(Context context) {
+ this.mContext = context;
+ IMogoNetwork network = (IMogoNetwork) ARouter.getInstance().build(MogoServicePaths.PATH_SERVICES_NETWORK).navigation(context);
+ this.mV2XApiService = network.create(V2XApiService.class, HttpConstant.Companion.getNetHost());
+ }
+
+ /**
+ * 刷新地图点数据
+ */
+ public void querySnapshotAsync(MogoLatLng latLng, int radius, int limit) {
+ if (mV2XApiService != null) {
+ final Map query = new ParamsProvider.Builder(mContext).build();
+ final RefreshBody refreshBody = new RefreshBody();
+ refreshBody.limit = limit;
+ refreshBody.location = new RefreshBody.LatLon(latLng.lat, latLng.lon);
+ refreshBody.radius = radius;
+ refreshBody.dataType.add(ServiceConst.CARD_TYPE_ROAD_CONDITION);
+ refreshBody.viewPush = true;
+ query.put("data", GsonUtil.jsonFromObject(refreshBody));
+ mV2XApiService.querySnapshotAsync(query)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(BaseData o) {
+ super.onSuccess(o);
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ }
+ });
+ }
+ }
+
+ /**
+ * 触发刷新直播心跳
+ *
+ * @param sn 目标车机SN
+ * @param videoChannel 直播的频道
+ * @param callback 回调
+ */
+ public void refreshHeartBeat(String sn, String videoChannel, final V2XRefreshCallback callback) {
+ if (mV2XApiService != null) {
+ Map liveBroadcast = new HashMap<>();
+ liveBroadcast.put("sn", sn);
+ liveBroadcast.put("data", GsonUtil.getGson().toJson(new V2XLiveCarBroadcastReq(sn, videoChannel)));
+
+ mV2XApiService.refreshHeartBeat(liveBroadcast)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(BaseData o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+ }
+ }
+
+
+ /**
+ * 直播点赞👍
+ *
+ * @param callback 回调
+ */
+ public void giveLikeLiveVideo(final V2XRefreshCallback callback, String snStr) {
+ if (mV2XApiService != null) {
+ Logger.d(V2XConst.MODULE_NAME, "点赞车机:" + snStr);
+ final Map query = new ParamsProvider.Builder(mContext).build();
+ query.put("data", "{\"sn\":" + snStr + "}");
+ mV2XApiService.giveLikeLiveVideo(query)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(BaseData o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 获取用户信息
+ *
+ * @param sn 目标车机SN
+ */
+ public void queryUserInfoBySn(String sn, final V2XRefreshCallback callback) {
+ if (mV2XApiService != null) {
+ RequestBody body = RequestBody.create(
+ okhttp3.MediaType.parse("application/json; charset=utf-8"),
+ "{\"sn\":\"" + sn + "\"}");
+
+ mV2XApiService.queryUserInfoBySn(body)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(V2XUserInfoRes o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 根据经纬度查询附近可直播车机直播信息
+ *
+ * @param callback 回调
+ */
+ public void queryNearbyVehicleLiveByLocation(
+ V2XRefreshCallback callback, double lon, double lat) {
+ if (mV2XApiService != null) {
+ final Map query = new ParamsProvider.Builder(mContext).build();
+
+ StringBuffer dataStr = new StringBuffer();
+ dataStr.append("{");
+ dataStr.append("\"lon\":");
+ dataStr.append(lon);
+ dataStr.append(",");
+ dataStr.append("\"lat\":");
+ dataStr.append(lat);
+ dataStr.append(",");
+ dataStr.append("\"radius\":");
+ dataStr.append(0.5);
+ dataStr.append(",");
+ dataStr.append("\"size\":");
+ dataStr.append(5);
+ dataStr.append("}");
+
+ query.put("data", dataStr.toString());
+ mV2XApiService.queryNearbyVehicleLiveByLocation(query)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(V2XLiveCarRes o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 推流与停止推流接口
+ *
+ * @param callback 回调
+ */
+ public void livePush(V2XRefreshCallback callback, String sn, int playType) {
+ if (mV2XApiService != null) {
+ final Map query = new ParamsProvider.Builder(mContext).build();
+ query.put("data", "{" +
+ "\"eventId\": \"yycp\"," +
+ "\"sn\": \"" + sn + "\"," +
+ "\"type\": \"" + playType + "\"," +
+ "\"videoChannel\": \"C_1\"" +
+ "}");
+ mV2XApiService.livePush(query)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(V2XLivePushVoRes o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 取消求助
+ *
+ * @param callback
+ */
+ public void cancelHelpSignal(V2XRefreshCallback callback) {
+ if (mV2XApiService != null) {
+ final Map map = new ParamsProvider.Builder(mContext).build();
+ String json = new StringBuilder()
+ .append("{")
+ .append("\"sn\":").append(Utils.getSn())
+ .append(",")
+ .append("\"vehicleType\":")
+ .append(0)
+ .append("}").toString();
+ map.put("data", json);
+ mV2XApiService.sendHelpSignal(map).subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(BaseData o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+
+ }
+ }
+
+ /**
+ * 查询当前车辆故障求助状态
+ *
+ * @param callback
+ */
+ public void getHelpSignal(V2XRefreshCallback callback) {
+ if (mV2XApiService != null) {
+ final Map map = new ParamsProvider.Builder(mContext).build();
+ map.put("sn", Utils.getSn());
+ mV2XApiService.queryHelpSignal(map).subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(V2XSeekHelpRes o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+
+ }
+ }
+
+ /**
+ * TODO 演示需求
+ *
+ * @param callback
+ */
+ public void getMockUserInfos(V2XRefreshCallback callback) {
+ if (mV2XApiService != null) {
+ mV2XApiService.getMockUserInfos().subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(V2XDemoUserInfoRes o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 根据名称获取策略详情
+ */
+ public void getStrategyPush(V2XRefreshCallback callback) {
+ if (mV2XApiService != null) {
+ mV2XApiService.getStrategyPush().subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(V2XStrategyPushRes o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 违章地段查询接口
+ */
+ public void queryIllegalPark(V2XRefreshCallback callback, double lon, double lat) {
+ if (mV2XApiService != null) {
+ final Map map = new ParamsProvider.Builder(mContext).build();
+ String json = "{\"location\":{\"lat\":" + lat + ",\"lon\":" + lon + "},\"radius\":50}";
+// String json = "{\"location\":{\"lat\":39.968139,\"lon\":116.380476},\"radius\":1000}";
+ map.put("data", json);
+ mV2XApiService.queryIllegalPark(map).subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new SubscribeImpl(RequestOptions.create(mContext)) {
+ @Override
+ public void onSuccess(MarkerResponse o) {
+ super.onSuccess(o);
+ if (callback != null) {
+ callback.onSuccess(o);
+ }
+ }
+
+ @Override
+ public void onError(String message, int code) {
+ super.onError(message, code);
+ if (callback != null) {
+ if (TextUtils.isEmpty(message)) {
+ message = "网络错误,请稍后重试";
+ }
+ callback.onFail(message);
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/receiver/SceneBroadcastReceiver.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/receiver/SceneBroadcastReceiver.java
new file mode 100644
index 0000000000..4966e2dfba
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/receiver/SceneBroadcastReceiver.java
@@ -0,0 +1,32 @@
+package com.mogo.module.v2x.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.v2x.scenario.impl.V2XScenarioManager;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+/**
+ * 场景广播接收,道路预警、后台下发、违章停车、故障求助、疲劳驾驶
+ *
+ * @author donghongyu
+ */
+public class SceneBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String TAG = V2XConst.MODULE_NAME + "_SceneBroadcastReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ V2XMessageEntity v2XMessageEntity = (V2XMessageEntity) intent.getSerializableExtra("V2XMessageEntity");
+ Logger.d(TAG, "v2XMessageEntity:" + GsonUtil.jsonFromObject(v2XMessageEntity));
+ V2XScenarioManager.getInstance().handlerMessage(v2XMessageEntity);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/IV2XScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/IV2XScenario.java
new file mode 100644
index 0000000000..89e4de77fc
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/IV2XScenario.java
@@ -0,0 +1,71 @@
+package com.mogo.module.v2x.scenario;
+
+import com.mogo.commons.voice.IMogoVoiceCmdCallBack;
+import com.mogo.module.common.entity.V2XMessageEntity;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 4:13 PM
+ * desc : V2X安全驾驶场景接口
+ * version: 1.0
+ */
+public interface IV2XScenario {
+
+ /**
+ * 展示场景
+ */
+ void show();
+
+ /**
+ * 关闭场景
+ */
+ void close();
+
+ /**
+ * 展示Window
+ */
+ void showWindow();
+
+ /**
+ * 关闭Window
+ */
+ void closeWindow();
+
+ /**
+ * 展示按钮
+ */
+ void showButton();
+
+ /**
+ * 关闭按钮
+ */
+ void closeButton();
+
+ /**
+ * 绘制POI
+ */
+ void drawPOI();
+
+ /**
+ * 清除POI
+ */
+ void clearPOI();
+
+ /**
+ * 是否是相同的场景,如果是说明重复的场景,需要根据场景进行不同的处理
+ *
+ * @param v2XMessageEntity 要比较的场景
+ * @return true-相同的场景,false-不同场景
+ */
+ boolean isSameScenario(V2XMessageEntity v2XMessageEntity);
+
+ /**
+ * 调用小智语音播放TTS
+ *
+ * @param msg 消息
+ * @param callBack 播放回调
+ */
+ void speakTTSVoice(String msg, IMogoVoiceCmdCallBack callBack);
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/IV2XScenarioManager.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/IV2XScenarioManager.java
new file mode 100644
index 0000000000..2a061a4fca
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/IV2XScenarioManager.java
@@ -0,0 +1,14 @@
+package com.mogo.module.v2x.scenario;
+
+import com.mogo.module.common.entity.V2XMessageEntity;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 3:47 PM
+ * desc : V2X安全驾驶场景管理
+ * version: 1.0
+ */
+public interface IV2XScenarioManager {
+ void handlerMessage(V2XMessageEntity v2XMessageEntity);
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/impl/AbsV2XScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/impl/AbsV2XScenario.java
new file mode 100644
index 0000000000..f6c3fe4523
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/impl/AbsV2XScenario.java
@@ -0,0 +1,89 @@
+package com.mogo.module.v2x.scenario.impl;
+
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.commons.voice.AIAssist;
+import com.mogo.commons.voice.IMogoVoiceCmdCallBack;
+import com.mogo.commons.voice.VoicePreemptType;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.v2x.scenario.IV2XScenario;
+import com.mogo.module.v2x.scenario.view.IV2XButton;
+import com.mogo.module.v2x.scenario.view.IV2XMarker;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.manager.IMoGoV2XStatusManager;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.utils.logger.Logger;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:01 PM
+ * desc :
+ * version: 1.0
+ */
+public abstract class AbsV2XScenario implements IV2XScenario {
+ protected String TAG = "AbsV2XScenario";
+ protected IV2XWindow mV2XWindow;
+ protected IV2XButton mV2XButton;
+ protected IV2XMarker mV2XMarker;
+ protected IMoGoV2XStatusManager mV2XStatusManager;
+ protected V2XMessageEntity mV2XMessageEntity;
+
+ protected AbsV2XScenario() {
+ this.mV2XStatusManager = V2XServiceManager.getMoGoV2XStatusManager();
+ }
+
+ public abstract void init(@Nullable V2XMessageEntity v2XMessageEntity);
+
+ @Override
+ public void close() {
+ closeButton();
+ closeWindow();
+ clearPOI();
+ }
+
+ @Override
+ public void speakTTSVoice(@Nullable String msg, IMogoVoiceCmdCallBack callBack) {
+ if (!TextUtils.isEmpty(msg)) {
+ Logger.w(V2XConst.MODULE_NAME, "调用TTS播放语音:" + msg);
+ AIAssist.getInstance(V2XUtils.getApp()).speakTTSVoice(msg, VoicePreemptType.PREEMPT_TYPE_IMMEADIATELY, callBack);
+ }
+ }
+
+ public IV2XWindow getV2XWindow() {
+ return mV2XWindow;
+ }
+
+ public void setV2XWindow(@Nullable IV2XWindow mV2XWindow) {
+ this.mV2XWindow = mV2XWindow;
+ }
+
+ public void setV2XButton(@Nullable IV2XButton mV2XButton) {
+ this.mV2XButton = mV2XButton;
+ }
+
+ public void setV2XMarker(@Nullable IV2XMarker mV2XMarker) {
+ this.mV2XMarker = mV2XMarker;
+ }
+
+ public void setV2XMessageEntity(@Nullable V2XMessageEntity mV2XMessageEntity) {
+ this.mV2XMessageEntity = mV2XMessageEntity;
+ }
+
+ public V2XMessageEntity getV2XMessageEntity() {
+ return mV2XMessageEntity;
+ }
+
+ @Override
+ public boolean isSameScenario(@Nullable V2XMessageEntity v2XMessageEntity) {
+ if (mV2XMessageEntity == null) {
+ return false;
+ }
+ return mV2XMessageEntity.equals(v2XMessageEntity);
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/impl/V2XScenarioManager.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/impl/V2XScenarioManager.java
new file mode 100644
index 0000000000..495fc47b96
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/impl/V2XScenarioManager.java
@@ -0,0 +1,111 @@
+package com.mogo.module.v2x.scenario.impl;
+
+import android.content.Intent;
+
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.scenario.IV2XScenarioManager;
+import com.mogo.module.v2x.scenario.scene.animation.V2XAnimationScenario;
+import com.mogo.module.v2x.scenario.scene.fatigue.V2XFatigueDrivingScenario;
+import com.mogo.module.v2x.scenario.scene.help.V2XCarForHelpScenario;
+import com.mogo.module.v2x.scenario.scene.livecar.V2XPushLiveCarScenario;
+import com.mogo.module.v2x.scenario.scene.park.V2XIllegalParkScenario;
+import com.mogo.module.v2x.scenario.scene.push.V2XPushEventScenario;
+import com.mogo.module.v2x.scenario.scene.road.V2XRoadEventScenario;
+import com.mogo.module.v2x.scenario.scene.seek.V2XSeekHelpScenario;
+import com.mogo.module.v2x.utils.ToastUtils;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+import java.util.HashMap;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 4:22 PM
+ * desc : 场景管理的分发
+ * version: 1.0
+ */
+public class V2XScenarioManager implements IV2XScenarioManager {
+ private static V2XScenarioManager mV2XScenarioManager;
+ private AbsV2XScenario mV2XScenario;
+ private HashMap mV2XScenarioSet = new HashMap<>();
+
+ private V2XScenarioManager() {
+ }
+
+ public static V2XScenarioManager getInstance() {
+ if (mV2XScenarioManager == null) {
+ synchronized (V2XScenarioManager.class) {
+ if (mV2XScenarioManager == null) {
+ mV2XScenarioManager = new V2XScenarioManager();
+ }
+ }
+ }
+ return mV2XScenarioManager;
+ }
+
+ @Override
+ public void handlerMessage(V2XMessageEntity v2XMessageEntity) {
+ Logger.w(MODULE_NAME, "处理V2X场景:" + GsonUtil.jsonFromObject(v2XMessageEntity));
+ synchronized (V2XScenarioManager.class) {
+ // 展示
+ V2XUtils.runOnUiThread(() -> {
+ // 提取之前存储的场景
+ if (v2XMessageEntity != null) {
+ // 广播给应用内部其它模块
+ Intent intent = new Intent(V2XConst.BROADCAST_SCENE_ACTION);
+ intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
+ LocalBroadcastManager.getInstance(V2XUtils.getApp()).sendBroadcast(intent);
+
+ mV2XScenario = mV2XScenarioSet.get(v2XMessageEntity.getType());
+
+ // 如果没有拿到之前的,根据类型分发
+ if (mV2XScenario == null) {
+ switch (v2XMessageEntity.getType()) {
+ case V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING:
+ mV2XScenario = V2XRoadEventScenario.getInstance();
+ break;
+ case V2XMessageEntity.V2XTypeEnum.ALERT_SEEK_WARNING:
+ mV2XScenario = V2XSeekHelpScenario.getInstance();
+ break;
+ case V2XMessageEntity.V2XTypeEnum.ALERT_FATIGUE_DRIVING:
+ mV2XScenario = V2XFatigueDrivingScenario.getInstance();
+ break;
+ case V2XMessageEntity.V2XTypeEnum.ALERT_PUSH_WINDOW_WARNING:
+ mV2XScenario = V2XPushEventScenario.getInstance();
+ break;
+ case V2XMessageEntity.V2XTypeEnum.ALERT_PUSH_LIVE_CAR_WARNING:
+ mV2XScenario = V2XPushLiveCarScenario.getInstance();
+ break;
+ case V2XMessageEntity.V2XTypeEnum.ALERT_ANIMATION_WARNING:
+ mV2XScenario = V2XAnimationScenario.getInstance();
+ break;
+ case V2XMessageEntity.V2XTypeEnum.ALERT_CAR_FOR_HELP:
+ mV2XScenario = V2XCarForHelpScenario.getInstance();
+ break;
+ case V2XMessageEntity.V2XTypeEnum.ALERT_ILLEGAL_PARK_WARNING:
+ mV2XScenario = V2XIllegalParkScenario.getInstance();
+ break;
+ default:
+ ToastUtils.showLong("当前V2X消息类型未定义。");
+ return;
+ }
+ }
+
+
+ // 展示最新的消息
+ if (mV2XScenario != null) {
+ mV2XScenario.init(v2XMessageEntity);
+ mV2XScenarioSet.put(v2XMessageEntity.getType(), mV2XScenario);
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/animation/V2XAnimationScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/animation/V2XAnimationScenario.java
new file mode 100644
index 0000000000..bc55ad50a9
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/animation/V2XAnimationScenario.java
@@ -0,0 +1,121 @@
+package com.mogo.module.v2x.scenario.scene.animation;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.utils.ADASUtils;
+import com.mogo.utils.logger.Logger;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:37 PM
+ * desc : 场景动画的场景展示,目前这里仅在演示DEMO中使用
+ * version: 1.0
+ */
+public class V2XAnimationScenario extends AbsV2XScenario {
+
+ private static V2XAnimationScenario mV2XAnimationScenario;
+
+ private V2XAnimationScenario() {
+ }
+
+
+ public static V2XAnimationScenario getInstance() {
+ if (mV2XAnimationScenario == null) {
+ synchronized (V2XAnimationScenario.class) {
+ if (mV2XAnimationScenario == null) {
+ mV2XAnimationScenario = new V2XAnimationScenario();
+ mV2XAnimationScenario.setV2XWindow(new V2XAnimationWindow());
+ }
+ }
+ }
+ return mV2XAnimationScenario;
+ }
+
+ @Override
+ public void init(@Nullable V2XMessageEntity v2XMessageEntity) {
+ if (!isSameScenario(v2XMessageEntity)
+ && V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ boolean isV2XAnimationShow = V2XServiceManager.getMoGoV2XStatusManager().isV2XAnimationShow();
+ if (isV2XAnimationShow) {
+ close();
+ }
+ setV2XMessageEntity(v2XMessageEntity);
+ show();
+ } else {
+ setV2XMessageEntity(v2XMessageEntity);
+ Logger.w(V2XConst.MODULE_NAME, "要处理的场景已经存在,丢弃这次初始化");
+ }
+ }
+
+ @Override
+ public void show() {
+ showWindow();
+ }
+
+ @Override
+ public void showWindow() {
+ if (mV2XWindow != null) {
+ mV2XWindow.setWindowStatusListener(new V2XWindowStatusListener() {
+ @Override
+ public void onViewShow() {
+ ADASUtils.broadcastToADAS(V2XServiceManager.getContext(), mV2XMessageEntity.getContent());
+ }
+
+ @Override
+ public void onViewClose() {
+ closeWindow();
+ clearPOI();
+ }
+ });
+ mV2XWindow.show(mV2XMessageEntity.getContent());
+ V2XServiceManager
+ .getIMogoWindowManager()
+ .addView(mV2XWindow.getView(), 0, 0, false);
+ V2XServiceManager.getMoGoV2XStatusManager().setV2XAnimationWindowShow(TAG, true);
+ }
+ }
+
+ @Override
+ public void closeWindow() {
+ if (mV2XWindow != null) {
+ mV2XWindow.close();
+ }
+ V2XServiceManager.getMoGoV2XStatusManager().setV2XAnimationWindowShow(TAG, false);
+ }
+
+ @Override
+ public void showButton() {
+ if (mV2XButton != null) {
+ mV2XButton.show();
+ }
+ }
+
+ @Override
+ public void closeButton() {
+ if (mV2XButton != null) {
+ mV2XButton.close();
+ }
+ }
+
+ @Override
+ public void drawPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.drawPOI(mV2XMessageEntity.getContent());
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ setV2XMessageEntity(null);
+ if (mV2XMarker != null) {
+ mV2XMarker.clearPOI();
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/animation/V2XAnimationWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/animation/V2XAnimationWindow.java
new file mode 100644
index 0000000000..0ece153745
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/animation/V2XAnimationWindow.java
@@ -0,0 +1,130 @@
+package com.mogo.module.v2x.scenario.scene.animation;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.view.TextureVideoView;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/24 11:34 AM
+ * desc : TODO 前瞻演示功能场景动画
+ * version: 1.0
+ */
+public class V2XAnimationWindow extends ConstraintLayout implements IV2XWindow {
+ // 弹窗状态监听
+ private V2XWindowStatusListener mV2XWindowStatusListener;
+ private TextureVideoView vvCarAnimation;
+
+ public V2XAnimationWindow() {
+ this(V2XServiceManager.getContext(), null);
+ }
+
+ public V2XAnimationWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XAnimationWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XAnimationWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ public void initView(Context context) {
+ Logger.w(MODULE_NAME, "初始化场景动画View。。。。。");
+ LayoutInflater.from(context).inflate(R.layout.window_animation, this);
+ vvCarAnimation = findViewById(R.id.vvCarAnimation);
+ }
+
+ /**
+ * 展示道路事件详情Windows
+ */
+ @Override
+ public void show(V2XPushMessageEntity entity) {
+ Uri videoURI = null;
+ switch (entity.getSceneId()) {
+ // 前车紧急制动告警
+ case "100005":
+ videoURI = Uri.parse("android.resource://" + getContext().getPackageName() + "/raw/" + R.raw.video_emergency_braking);
+ break;
+ // 十字路口碰撞预警
+ case "100006":
+ videoURI = Uri.parse("android.resource://" + getContext().getPackageName() + "/raw/" + R.raw.video_left_right_car);
+ break;
+ // 岔路口碰撞预警
+ case "100007":
+ videoURI = Uri.parse("android.resource://" + getContext().getPackageName() + "/raw/" + R.raw.video_cut_in_line);
+ break;
+ // 禁行车道预警
+ case "100008":
+ videoURI = Uri.parse("android.resource://" + getContext().getPackageName() + "/raw/" + R.raw.video_current_row_closed);
+ break;
+ // 应急车辆优先通行
+ case "100012":
+ videoURI = Uri.parse("android.resource://" + getContext().getPackageName() + "/raw/" + R.raw.video_emergency_lane);
+ break;
+ // 闯红灯预警
+ case "100013":
+ videoURI = Uri.parse("android.resource://" + getContext().getPackageName() + "/raw/" + R.raw.video_traffic_light_speed_cut);
+ break;
+ }
+
+ if (videoURI != null) {
+ vvCarAnimation.setVideoURI(videoURI);
+ vvCarAnimation.setOnPreparedListener(mediaPlayer -> {
+ Logger.w(MODULE_NAME, "场景动画准备。。。。。");
+ });
+ vvCarAnimation.setOnCompletionListener(mediaPlayer -> {
+ Logger.w(MODULE_NAME, "动画播放结束...");
+ if (mV2XWindowStatusListener != null) {
+ mV2XWindowStatusListener.onViewClose();
+ }
+ });
+ vvCarAnimation.start();
+ Logger.w(MODULE_NAME, "开始播放动画。。。。。");
+ }
+ }
+
+ @Override
+ public void close() {
+ //移除窗体
+ V2XServiceManager
+ .getIMogoWindowManager()
+ .removeView(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (vvCarAnimation != null) {
+ vvCarAnimation.stopPlayback();
+ }
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void setWindowStatusListener(V2XWindowStatusListener listener) {
+ this.mV2XWindowStatusListener = listener;
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/fatigue/V2XFatigueDrivingScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/fatigue/V2XFatigueDrivingScenario.java
new file mode 100644
index 0000000000..f529d85065
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/fatigue/V2XFatigueDrivingScenario.java
@@ -0,0 +1,157 @@
+package com.mogo.module.v2x.scenario.scene.fatigue;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.map.MogoLatLng;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.service.windowview.IMogoTopViewStatusListener;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:37 PM
+ * desc : 疲劳驾驶场景
+ * version: 1.0
+ */
+public class V2XFatigueDrivingScenario extends AbsV2XScenario implements IMogoTopViewStatusListener {
+
+ private static V2XFatigueDrivingScenario mV2XTiredScenario;
+ // 语音控制导航
+ private V2XVoiceCallbackListener mNaviCb = (command, intent) -> startNavi();
+
+ private V2XFatigueDrivingScenario() {
+ }
+
+ public static V2XFatigueDrivingScenario getInstance() {
+ if (mV2XTiredScenario == null) {
+ synchronized (V2XFatigueDrivingScenario.class) {
+ if (mV2XTiredScenario == null) {
+ mV2XTiredScenario = new V2XFatigueDrivingScenario();
+ mV2XTiredScenario.setV2XWindow(new V2XFatigueDrivingWindow());
+ }
+ }
+ }
+ return mV2XTiredScenario;
+ }
+
+ @Override
+ public void init(@Nullable V2XMessageEntity v2XMessageEntity) {
+ if (!isSameScenario(v2XMessageEntity)) {
+ setV2XMessageEntity(v2XMessageEntity);
+ if (v2XMessageEntity != null) {
+ // 注册语音交互
+ V2XVoiceManager.INSTANCE
+ .registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_NAVI, mNaviCb)
+ .registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_NAVI_UN_WAKEUP, mNaviCb);
+ if (v2XMessageEntity.isShowState()
+ && V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ show();
+ }
+ }
+ } else {
+ setV2XMessageEntity(v2XMessageEntity);
+ Logger.w(V2XConst.MODULE_NAME, "要处理的场景已经存在,丢弃这次初始化");
+ }
+ }
+
+ @Override
+ public void show() {
+ showWindow();
+ }
+
+ @Override
+ public void showWindow() {
+ if (mV2XWindow != null) {
+ ViewGroup.LayoutParams layoutParams =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ (int) V2XUtils.getApp().getResources().getDimension(R.dimen.module_v2x_fatigue_driving_window_height_ground));
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .addView(mV2XWindow.getView(), layoutParams, this);
+ mV2XWindow.show(mV2XMessageEntity.getContent());
+ V2XServiceManager.getMoGoV2XStatusManager().setFatigueDrivingWindowShow(TAG, true);
+ }
+ }
+
+ @Override
+ public void closeWindow() {
+ if (mV2XWindow != null) {
+ mV2XWindow.close();
+ }
+ }
+
+ @Override
+ public void showButton() {
+ }
+
+ @Override
+ public void closeButton() {
+ }
+
+ @Override
+ public void drawPOI() {
+ }
+
+ @Override
+ public void clearPOI() {
+ }
+
+ @Override
+ public void onViewAdded(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 动画结束");
+ }
+
+ @Override
+ public void onViewRemoved(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 动画结束");
+ }
+
+ @Override
+ public void beforeViewAddAnim(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 开始");
+
+ }
+
+ @Override
+ public void beforeViewRemoveAnim(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 开始");
+ // 重置场景提示的消息
+ setV2XMessageEntity(null);
+ V2XServiceManager.getMoGoV2XStatusManager().setFatigueDrivingWindowShow(TAG, false);
+ }
+
+
+ /**
+ * 导航规划路线
+ */
+ private void startNavi() {
+ if (mV2XMessageEntity.getContent() != null) {
+ // 反注册语音交互
+ V2XVoiceManager.INSTANCE
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_NAVI)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_NAVI_UN_WAKEUP);
+ MogoLatLng endPoint = new MogoLatLng(mV2XMessageEntity.getContent().getLat(), mV2XMessageEntity.getContent().getLon());
+ V2XServiceManager.getNavi().naviTo(endPoint);
+ close();
+ }
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/fatigue/V2XFatigueDrivingWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/fatigue/V2XFatigueDrivingWindow.java
new file mode 100644
index 0000000000..9ce5a57e2b
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/fatigue/V2XFatigueDrivingWindow.java
@@ -0,0 +1,195 @@
+package com.mogo.module.v2x.scenario.scene.fatigue;
+
+import android.content.Context;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.mogo.map.MogoLatLng;
+import com.mogo.map.navi.IMogoNaviListener;
+import com.mogo.map.navi.MogoNaviInfo;
+import com.mogo.map.navi.MogoTraffic;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.utils.SpanUtils;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/24 11:34 AM
+ * desc : 疲劳驾驶场景Window
+ * version: 1.0
+ */
+public class V2XFatigueDrivingWindow extends RelativeLayout
+ implements IV2XWindow, IMogoNaviListener {
+
+ private TextView mTvAddress, mTvAddressDistance;
+ private ImageView mIvToNav;
+
+ // 直播30秒自动关闭
+ private static Handler handlerV2XEvent = new Handler();
+ private static Runnable runnableV2XEvent;
+
+ // 推荐的停车场
+ private V2XPushMessageEntity mV2XPushMessageEntity;
+
+ public V2XFatigueDrivingWindow() {
+ this(V2XServiceManager.getContext(), null);
+ }
+
+ public V2XFatigueDrivingWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XFatigueDrivingWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XFatigueDrivingWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ public void initView(Context context) {
+ Logger.w(MODULE_NAME, "V2X-初始化疲劳驾驶");
+ LayoutInflater.from(context).inflate(R.layout.window_fatigue_driving, this);
+ mTvAddress = findViewById(R.id.tvAddress);
+ mTvAddressDistance = findViewById(R.id.tvAddressDistance);
+ mIvToNav = findViewById(R.id.ivToNav);
+ mIvToNav.setOnClickListener(v -> startNavi());
+ }
+
+ /**
+ * 导航规划路线
+ */
+ private void startNavi() {
+ if (mV2XPushMessageEntity != null) {
+ MogoLatLng endPoint = new MogoLatLng(mV2XPushMessageEntity.getLat(), mV2XPushMessageEntity.getLon());
+ V2XServiceManager.getNavi().naviTo(endPoint);
+ close();
+ }
+ }
+
+ /**
+ * 展示道路事件详情Windows
+ */
+ @Override
+ public void show(V2XPushMessageEntity entity) {
+ mV2XPushMessageEntity = entity;
+ if (entity != null) {
+ if (!TextUtils.isEmpty(entity.getAddress())) {
+ mTvAddress.setText(entity.getAddress());
+ } else {
+ mTvAddress.setText("");
+ }
+ SpanUtils.with(mTvAddressDistance)
+ .append("" + (int) entity.getDistance())
+ .setFontSize(82)
+ .append("M")
+ .setFontSize(30)
+ .create();
+ countDownV2XEvent(entity);
+ }
+ }
+
+ @Override
+ public void close() {
+ // 反注册语音交互
+ V2XVoiceManager.INSTANCE
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_NAVI)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_NAVI_UN_WAKEUP);
+ // 停止倒计时
+ if (handlerV2XEvent != null && runnableV2XEvent != null) {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ runnableV2XEvent = null;
+ }
+ //移除窗体
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .removeView(this);
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void setWindowStatusListener(V2XWindowStatusListener listener) {
+ }
+
+ /**
+ * 窗体倒计时
+ */
+ public void countDownV2XEvent(V2XPushMessageEntity v2XPushMessageEntity) {
+ // 倒计时
+ if (runnableV2XEvent == null) {
+ runnableV2XEvent = () -> {
+ Logger.d(MODULE_NAME, "V2X=== Window 30秒倒计时结束。。。");
+ // 移出Window详细信息
+ close();
+ };
+ } else {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ }
+ int expireTime = v2XPushMessageEntity.getExpireTime();
+ Logger.d(MODULE_NAME, "V2X=== Window 展示开始倒计时:" + expireTime);
+ handlerV2XEvent.postDelayed(runnableV2XEvent, expireTime);
+ }
+
+
+ @Override
+ public void onCalculateSuccess() {
+ V2XServiceManager.getNavi().startNavi(true);
+ }
+
+ @Override
+ public void onInitNaviFailure() {
+
+ }
+
+ @Override
+ public void onInitNaviSuccess() {
+
+ }
+
+ @Override
+ public void onNaviInfoUpdate(MogoNaviInfo naviinfo) {
+
+ }
+
+ @Override
+ public void onStartNavi() {
+
+ }
+
+ @Override
+ public void onStopNavi() {
+
+ }
+
+ @Override
+ public void onoCalculateFailed() {
+
+ }
+
+ @Override
+ public void onUpdateTraffic(MogoTraffic traffic) {
+
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/help/V2XCarForHelpScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/help/V2XCarForHelpScenario.java
new file mode 100644
index 0000000000..3e0cda95a1
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/help/V2XCarForHelpScenario.java
@@ -0,0 +1,232 @@
+package com.mogo.module.v2x.scenario.scene.help;
+
+import android.content.Intent;
+import android.os.CountDownTimer;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.scenario.scene.seek.V2XSeekHelpButton;
+import com.mogo.module.v2x.scenario.scene.seek.V2XSeekHelpDialog;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.service.entrance.ButtonIndex;
+import com.mogo.service.statusmanager.IMogoStatusChangedListener;
+import com.mogo.service.statusmanager.StatusDescriptor;
+import com.mogo.utils.logger.Logger;
+import com.mogo.utils.storage.SharedPrefsMgr;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.scenario.scene
+ * @ClassName: V2XCarForHelpScenario
+ * @Description: 自车求助场景
+ * @Author: fenghl
+ * @CreateDate: 2020/5/25 11:44
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/5/25 11:44
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public class V2XCarForHelpScenario extends AbsV2XScenario implements IMogoStatusChangedListener {
+ private static final String TAG = MODULE_NAME + "_" + V2XCarForHelpScenario.class.getSimpleName();
+ private volatile static V2XCarForHelpScenario mV2XCarForHelpScenario;
+ private CountDownTimer mMySeekHelpCountDown;
+ private V2XSeekHelpDialog v2xFaultHelpDialog;
+
+ private V2XCarForHelpScenario() {
+ setV2XButton(new V2XSeekHelpButton());
+ V2XServiceManager.getMoGoStatusManager().registerStatusChangedListener(TAG, StatusDescriptor.SEEK_HELPING, this);
+ }
+
+ public static V2XCarForHelpScenario getInstance() {
+ if (mV2XCarForHelpScenario == null) {
+ synchronized (V2XCarForHelpScenario.class) {
+ if (mV2XCarForHelpScenario == null) {
+ mV2XCarForHelpScenario = new V2XCarForHelpScenario();
+ }
+ }
+ }
+ return mV2XCarForHelpScenario;
+ }
+
+ @Override
+ public void init(@Nullable V2XMessageEntity v2XMessageEntity) {
+ // 设置Button的显示
+ if (v2XMessageEntity == null) {
+ return;
+ }
+ mV2XMessageEntity = v2XMessageEntity;
+ Boolean isShow = mV2XMessageEntity.getContent();
+ if (isShow) {
+ showButton();
+ mySeekHelpCountDownTimerCancel();
+ mySeekHelpCountDownTimerStart();
+ if (cancelCb == null) {
+ cancelCb = (String command, Intent intent) -> {
+ TextView tv = V2XServiceManager.getMogoEntranceButtonController().getButton(ButtonIndex.BUTTON2);
+ if (tv != null) {
+ showDialog();
+ }
+ };
+ }
+ unregisterSeekHelpButtonCmd();
+ V2XVoiceManager.INSTANCE.registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CANCEL_HELP, cancelCb);
+ V2XVoiceManager.INSTANCE.registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CANCEL_FOR_HELP_UN_WAKEUP, cancelCb);
+ } else {
+ closeButton();
+ }
+ }
+
+ @Override
+ public void show() {
+ showButton();
+ }
+
+ @Override
+ public void showWindow() {
+ //无window
+ }
+
+ @Override
+ public void closeWindow() {
+ //无window
+ }
+
+ private V2XVoiceCallbackListener cancelCb = null;
+
+ @Override
+ public void showButton() {
+ Logger.d(TAG, "打开自车求助按钮!");
+ // 设置Button的显示
+ V2XUtils.runOnUiThread(() -> {
+ if (mV2XButton != null) {
+ mV2XButton.setOnActionListener(this::showDialog);
+ mV2XButton.show();
+ }
+ });
+ }
+
+ @Override
+ public void closeButton() {
+ if (V2XServiceManager.getMoGoStatusManager().isSeekHelping()) {
+ Logger.d(TAG, "关闭自车求助按钮!");
+ V2XServiceManager.getMoGoStatusManager().setSeekHelping(TAG, false);
+ if (mV2XButton != null) {
+ mV2XButton.close();
+ SharedPrefsMgr.getInstance(V2XUtils.getApp()).putLong(V2XConst.SEEK_HELP_TIME, 0);
+ }
+
+ mySeekHelpCountDownTimerCancel();
+ }
+ if (V2XServiceManager.getMoGoV2XStatusManager().isOtherSeekHelpPOIShow()) {
+ // 移除线
+ V2XServiceManager.getMoGoV2XPolylineManager().clearLine();
+ // 移除事件POI
+ V2XServiceManager.getMoGoV2XMarkerManager().clearSpecialCarPOI();
+ // 绘制上次的数据
+ V2XServiceManager.getMoGoV2XMarkerManager().drawableLastAllPOI();
+ }
+
+
+ }
+
+
+ @Override
+ public void drawPOI() {
+ //无Poi
+ }
+
+ @Override
+ public void clearPOI() {
+ //无Poi
+ }
+
+
+ private void mySeekHelpCountDownTimerStart() {
+ long seekHelpTimeStart = SharedPrefsMgr.getInstance(V2XServiceManager.getContext()).getLong(V2XConst.SEEK_HELP_TIME, 0);
+ if (seekHelpTimeStart == 0) {
+ return;
+ }
+ long curTime = System.currentTimeMillis();
+ long timeOut = 10 * 60_000;
+ //超时时间
+ if (curTime - seekHelpTimeStart < timeOut) {
+ long min30 = 30 * 60 * 1000;
+ //long min30 = 10_000;
+ long countDown = seekHelpTimeStart + min30 - curTime;
+ //从自车故障时间起 30分钟后弹框提醒
+ if (mMySeekHelpCountDown == null) {
+ mMySeekHelpCountDown = new CountDownTimer(countDown, countDown) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+
+ }
+
+ @Override
+ public void onFinish() {
+ showDialog();
+ }
+ };
+ mMySeekHelpCountDown.start();
+ }
+ }
+ }
+
+ private void mySeekHelpCountDownTimerCancel() {
+ if (mMySeekHelpCountDown != null) {
+ mMySeekHelpCountDown.cancel();
+ mMySeekHelpCountDown = null;
+ }
+ }
+
+
+ public void showDialog() {
+ if (v2xFaultHelpDialog == null) {
+ v2xFaultHelpDialog = new V2XSeekHelpDialog(V2XServiceManager.getContext());
+ }
+ v2xFaultHelpDialog.setOnClickListener(new V2XSeekHelpDialog.OnClickListener() {
+ @Override
+ public void onClickLeft() {
+ //放弃求助
+ /* if (V2XServiceManager.getMoGoStatusManager().isSeekHelping()) {
+ V2XServiceManager.getMoGoStatusManager().setSeekHelping(TAG, false);
+ }*/
+ closeButton();
+ v2xFaultHelpDialog.dismiss();
+ }
+
+ @Override
+ public void onClickRight() {
+ //继续求助
+ v2xFaultHelpDialog.dismiss();
+
+ }
+ });
+ v2xFaultHelpDialog.show();
+
+ }
+
+ private void unregisterSeekHelpButtonCmd() {
+ V2XVoiceManager.INSTANCE.unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CANCEL_HELP);
+ V2XVoiceManager.INSTANCE.unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CANCEL_FOR_HELP_UN_WAKEUP);
+ }
+
+ @Override
+ public void onStatusChanged(StatusDescriptor descriptor, boolean isTrue) {
+ if (descriptor == StatusDescriptor.SEEK_HELPING) {
+ V2XMessageEntity entity = new V2XMessageEntity<>();
+ entity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_CAR_FOR_HELP);
+ entity.setContent(isTrue);
+ init(entity);
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XPushLiveCarScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XPushLiveCarScenario.java
new file mode 100644
index 0000000000..a11dffc26d
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XPushLiveCarScenario.java
@@ -0,0 +1,151 @@
+package com.mogo.module.v2x.scenario.scene.livecar;
+
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.utils.ADASUtils;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.service.windowview.IMogoTopViewStatusListener;
+import com.mogo.utils.TipToast;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:37 PM
+ * desc : TODO 演示使用的推送单车机直播场景
+ * version: 1.0
+ */
+public class V2XPushLiveCarScenario extends AbsV2XScenario implements IMogoTopViewStatusListener {
+
+ private static V2XPushLiveCarScenario mV2XLiveCarScenario;
+
+ private V2XPushLiveCarScenario() {
+ }
+
+ public static V2XPushLiveCarScenario getInstance() {
+ if (mV2XLiveCarScenario == null) {
+ synchronized (V2XPushLiveCarScenario.class) {
+ if (mV2XLiveCarScenario == null) {
+ mV2XLiveCarScenario = new V2XPushLiveCarScenario();
+ mV2XLiveCarScenario.setV2XWindow(new V2XPushLiveCarWindow());
+ }
+ }
+ }
+ return mV2XLiveCarScenario;
+ }
+
+ @Override
+ public void init(@Nullable V2XMessageEntity v2XMessageEntity) {
+ if (!isSameScenario(v2XMessageEntity)
+ && V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ boolean isWindowShow = V2XServiceManager.getMoGoV2XStatusManager().isLeftLiveVideoShow();
+ if (isWindowShow) {
+ close();
+ }
+ setV2XMessageEntity(v2XMessageEntity);
+ if (v2XMessageEntity != null &&
+ (v2XMessageEntity.getContent().getVideoSn() != null ||
+ !TextUtils.isEmpty(v2XMessageEntity.getContent().getVideoUrl()))) {
+ show();
+ } else {
+ TipToast.shortTip("附近没有可直播车机");
+ Logger.e(V2XConst.MODULE_NAME, "直播地址为null");
+ }
+ } else {
+ setV2XMessageEntity(v2XMessageEntity);
+ Logger.w(V2XConst.MODULE_NAME, "要处理的场景已经存在,丢弃这次初始化");
+ }
+ }
+
+ @Override
+ public void show() {
+ showWindow();
+ }
+
+ @Override
+ public void showWindow() {
+ if (mV2XWindow != null) {
+ ViewGroup.LayoutParams layoutParams =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ (int) V2XUtils.getApp().getResources().getDimension(R.dimen.module_v2x_event_window_height));
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .addView(mV2XWindow.getView(), layoutParams, this);
+ mV2XWindow.show(mV2XMessageEntity.getContent());
+ V2XServiceManager.getMoGoV2XStatusManager().setLiveCarWindowShow(TAG, true);
+ }
+ }
+
+ @Override
+ public void closeWindow() {
+ if (mV2XWindow != null) {
+ mV2XWindow.close();
+ }
+ V2XServiceManager.getMoGoV2XStatusManager().setLiveCarWindowShow(TAG, false);
+ }
+
+ @Override
+ public void showButton() {
+ if (mV2XButton != null) {
+ mV2XButton.show();
+ }
+ }
+
+ @Override
+ public void closeButton() {
+ if (mV2XButton != null) {
+ mV2XButton.close();
+ }
+ }
+
+ @Override
+ public void drawPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.drawPOI(mV2XMessageEntity.getContent());
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.clearPOI();
+ }
+ }
+
+ @Override
+ public void onViewAdded(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 动画结束");
+ ADASUtils.broadcastToADAS(V2XServiceManager.getContext(), mV2XMessageEntity.getContent());
+ }
+
+ @Override
+ public void onViewRemoved(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 动画结束");
+ }
+
+ @Override
+ public void beforeViewAddAnim(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 开始");
+ }
+
+ @Override
+ public void beforeViewRemoveAnim(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 开始");
+ // 重置场景提示的消息
+ setV2XMessageEntity(null);
+ V2XServiceManager.getMoGoV2XStatusManager().setLiveCarWindowShow(TAG, false);
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XPushLiveCarWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XPushLiveCarWindow.java
new file mode 100644
index 0000000000..05edb72666
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XPushLiveCarWindow.java
@@ -0,0 +1,136 @@
+package com.mogo.module.v2x.scenario.scene.livecar;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import com.mogo.module.common.entity.MarkerCarInfo;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.utils.MarkerUtils;
+import com.mogo.module.v2x.view.V2XLiveGSYVideoView;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/24 11:34 AM
+ * desc : TODO 演示使用的推送单车机直播场景
+ * version: 1.0
+ */
+public class V2XPushLiveCarWindow extends RelativeLayout implements IV2XWindow {
+
+ private Context mContext;
+ private V2XLiveGSYVideoView mV2XLiveGSYVideoView;
+ // 弹窗状态监听
+ private V2XWindowStatusListener mV2XWindowStatusListener;
+
+ // 直播30秒自动关闭
+ private static Handler handlerV2XEvent = new Handler();
+ private static Runnable runnableV2XEvent;
+
+ public V2XPushLiveCarWindow() {
+ this(V2XServiceManager.getContext(), null);
+ }
+
+ public V2XPushLiveCarWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XPushLiveCarWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XPushLiveCarWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mContext = context;
+ initView(context);
+ }
+
+ public void initView(Context context) {
+ Logger.w(MODULE_NAME, "初始化直播小窗口View。。。。。");
+ LayoutInflater.from(context).inflate(R.layout.window_push_live_video, this);
+ // 详情列表
+ mV2XLiveGSYVideoView = findViewById(R.id.videoPlayer);
+ }
+
+ /**
+ * 展示道路事件详情Windows
+ */
+ @Override
+ public void show(V2XPushMessageEntity entity) {
+ if (entity != null) {
+ Logger.w(MODULE_NAME, "更新直播信息。。。。。" + entity);
+
+ // 启动播放
+ MarkerCarInfo.CarLiveInfo carLiveInfo = new MarkerCarInfo.CarLiveInfo();
+ carLiveInfo.setVideoChannel(entity.getVideoChannel());
+ carLiveInfo.setVideoSn(entity.getVideoSn());
+ carLiveInfo.setVideoUrl(entity.getVideoUrl());
+ mV2XLiveGSYVideoView.startLive(carLiveInfo);
+ countDownV2XEvent(entity);
+
+ if (mV2XWindowStatusListener != null) {
+ mV2XWindowStatusListener.onViewShow();
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ // 修改地图比例
+ V2XServiceManager.getMoGoStatusManager().setUserInteractionStatus(MODULE_NAME, true, false);
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ MarkerUtils.resetMapZoom(16);
+ // 如果窗体处于展示的时候
+ if (mV2XWindowStatusListener != null) {
+ mV2XWindowStatusListener.onViewClose();
+ }
+ // 停止倒计时
+ if (handlerV2XEvent != null && runnableV2XEvent != null) {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ runnableV2XEvent = null;
+ }
+ //移除窗体
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .removeView(this);
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void setWindowStatusListener(V2XWindowStatusListener listener) {
+ this.mV2XWindowStatusListener = listener;
+ }
+
+ /**
+ * 窗体倒计时
+ */
+ public void countDownV2XEvent(V2XPushMessageEntity v2XPushMessageEntity) {
+ // 倒计时
+ if (runnableV2XEvent == null) {
+ runnableV2XEvent = () -> {
+ Logger.d(MODULE_NAME, "V2X=== Window 30秒倒计时结束。。。");
+ // 移出Window详细信息
+ close();
+ };
+ } else {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ }
+ int expireTime = v2XPushMessageEntity.getExpireTime();
+ Logger.d(MODULE_NAME, "V2X=== Window 展示开始倒计时:" + expireTime);
+ handlerV2XEvent.postDelayed(runnableV2XEvent, expireTime);
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XRoadLiveCarScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XRoadLiveCarScenario.java
new file mode 100644
index 0000000000..ec0086f5bb
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XRoadLiveCarScenario.java
@@ -0,0 +1,153 @@
+package com.mogo.module.v2x.scenario.scene.livecar;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XEventShowEntity;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.scenario.scene.road.V2XRoadEventScenario;
+import com.mogo.module.v2x.scenario.scene.road.V2XRoadEventWindow;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.service.windowview.IMogoTopViewStatusListener;
+import com.mogo.utils.logger.Logger;
+
+import java.util.List;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/29 11:16 AM
+ * desc : 道路预警中的直播场景
+ * version: 1.0
+ */
+public class V2XRoadLiveCarScenario extends AbsV2XScenario> implements IMogoTopViewStatusListener {
+ private static final String TAG = "V2XRoadEventScenario";
+ private static V2XRoadLiveCarScenario mV2XRoadLiveCarScenario;
+
+ private V2XRoadLiveCarScenario() {
+ }
+
+ public static V2XRoadLiveCarScenario getInstance() {
+ if (mV2XRoadLiveCarScenario == null) {
+ synchronized (V2XRoadLiveCarScenario.class) {
+ if (mV2XRoadLiveCarScenario == null) {
+ mV2XRoadLiveCarScenario = new V2XRoadLiveCarScenario();
+ }
+ }
+ }
+ return mV2XRoadLiveCarScenario;
+ }
+
+ @Override
+ public void init(V2XMessageEntity> v2XMessageEntity) {
+ try {
+ List v2XRoadEventEntity = v2XMessageEntity.getContent();
+ if (v2XRoadEventEntity != null) {
+ if (v2XMessageEntity.isShowState()) {
+ if (!isSameScenario(v2XMessageEntity)
+ && V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ mV2XRoadLiveCarScenario.setV2XWindow(new V2XRoadLiveCarWindow());
+ setV2XMessageEntity(v2XMessageEntity);
+ show();
+ } else {
+ setV2XMessageEntity(v2XMessageEntity);
+ Logger.w(V2XConst.MODULE_NAME, "要处理的场景已经存在,丢弃这次初始化");
+ }
+ } else {
+ close();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void show() {
+ if (mV2XMessageEntity != null && mV2XMessageEntity.getContent() != null) {
+ showWindow();
+ }
+ }
+
+ @Override
+ public void showWindow() {
+ if (mV2XWindow != null) {
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .addSubView(mV2XWindow.getView(), this);
+ mV2XWindow.show(mV2XMessageEntity.getContent());
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadLiveCarWindowShow(TAG, true);
+ }
+ }
+
+ @Override
+ public void closeWindow() {
+ if (mV2XWindow != null) {
+ mV2XWindow.close();
+ }
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadLiveCarWindowShow(TAG, false);
+ }
+
+ @Override
+ public void showButton() {
+ if (mV2XButton != null) {
+ mV2XButton.show();
+ }
+ }
+
+ @Override
+ public void closeButton() {
+ if (mV2XButton != null) {
+ mV2XButton.close();
+ }
+ }
+
+ @Override
+ public void drawPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.drawPOI(mV2XMessageEntity.getContent());
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.clearPOI();
+ }
+ }
+
+ @Override
+ public void onViewAdded(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 动画结束");
+ }
+
+ @Override
+ public void onViewRemoved(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 动画结束");
+ // 警报状态,true-警报中,false-警报结束
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadLiveCarWindowShow(TAG, false);
+ }
+
+ @Override
+ public void beforeViewAddAnim(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 开始");
+ }
+
+ @Override
+ public void beforeViewRemoveAnim(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 开始");
+ // 打开直播后,窗口倒计时暂停关闭,直播被关闭后继续倒计时
+ V2XRoadEventWindow window = (V2XRoadEventWindow) V2XRoadEventScenario.getInstance().getV2XWindow();
+ window.startCountDown();
+ // 重置场景提示的消息
+ setV2XMessageEntity(null);
+ // 关闭场景提示
+ clearPOI();
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XRoadLiveCarWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XRoadLiveCarWindow.java
new file mode 100644
index 0000000000..9d9437b586
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/livecar/V2XRoadLiveCarWindow.java
@@ -0,0 +1,236 @@
+package com.mogo.module.v2x.scenario.scene.livecar;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.PagerSnapHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.adapter.V2XRoadEventAdapter;
+import com.mogo.module.common.entity.V2XEventShowEntity;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.module.v2x.voice.V2XVoicePagingListener;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/24 11:34 AM
+ * desc : 道路预警中的直播场景
+ * version: 1.0
+ */
+public class V2XRoadLiveCarWindow extends RelativeLayout
+ implements IV2XWindow> {
+ private String TAG = "V2XRoadEventDetailWindow";
+ private TextView mTvEventStubClose;
+ // 展示列表
+ private RecyclerView mRecyclerView;
+ // 列表数据适配器
+ private V2XRoadEventAdapter mV2XRoadEventAdapter;
+ // 列表展示
+ private List mItemList = new ArrayList<>();
+ // 当前展示的位置
+ private int mCurPosition;
+ // 处理道路事件,30秒倒计时
+ private Handler handlerV2XEvent = new Handler();
+ private Runnable runnableV2XEvent;
+ private int countDownTime = 30;
+ // 关闭弹窗回调
+ private V2XVoiceCallbackListener v2XVoiceCallbackCloseWindowListener = (command, intent) -> close();
+ // 翻页回调监听
+ private V2XVoicePagingListener v2XVoicePagingCallbackListener = new V2XVoicePagingListener() {
+
+ @Override
+ public void onNextCallback() {
+ if (mRecyclerView != null && mCurPosition >= 0 && mCurPosition < mV2XRoadEventAdapter.getItemCount() - 1) {
+ mRecyclerView.scrollToPosition(mCurPosition + 1);
+ }
+ }
+
+ @Override
+ public void onPrevCallback() {
+ if (mRecyclerView != null && mCurPosition > 0 && mCurPosition <= mV2XRoadEventAdapter.getItemCount() - 1) {
+ mRecyclerView.scrollToPosition(mCurPosition - 1);
+ }
+ }
+ };
+
+ public V2XRoadLiveCarWindow() {
+ this(V2XServiceManager.getContext(), null);
+ }
+
+ public V2XRoadLiveCarWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XRoadLiveCarWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XRoadLiveCarWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ /**
+ * 初始化视图
+ */
+ private void initView(Context context) {
+ Logger.w(MODULE_NAME, "V2X===初始化道路直播视图");
+ // 填充布局
+ LayoutInflater.from(context).inflate(R.layout.window_road_live_car_detail, this);
+
+ mTvEventStubClose = findViewById(R.id.tvEventStubClose);
+ mRecyclerView = findViewById(R.id.rvRoadEventList);
+ mTvEventStubClose.setOnClickListener(v -> {
+ close();
+ });
+
+ mV2XRoadEventAdapter = new V2XRoadEventAdapter(mItemList);
+ mRecyclerView.setAdapter(mV2XRoadEventAdapter);
+ // 设置切换样式
+ new PagerSnapHelper().attachToRecyclerView(mRecyclerView);
+ // 配置列表朝向
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
+ mRecyclerView.setLayoutManager(layoutManager);
+ mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (recyclerView.getChildCount() > 0) {
+ if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+ //获得当前显示在第一个item的位置
+ mCurPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
+ // 用户处于交互的时候延后隐藏时间
+ countDownV2XEvent(30000);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * 展示道路事件详情Windows
+ */
+ @Override
+ public void show(List entity) {
+ Logger.d(MODULE_NAME, "V2X===道路事件直播:展示 Window :" + GsonUtil.jsonFromObject(entity));
+ // 注册语音交互
+ V2XVoiceManager.INSTANCE
+ .registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CLOSE_WINDOW,
+ v2XVoiceCallbackCloseWindowListener)
+ .registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CLOSE_WINDOW_UN_WAKEUP,
+ v2XVoiceCallbackCloseWindowListener)
+ .registerUnWakeVoice(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CLOSE_LIVE_UN_WAKEUP,
+ v2XVoiceCallbackCloseWindowListener)
+ .registerPagingCallback(v2XVoicePagingCallbackListener);
+ // 更新头部的窗体视图
+ updateTopWindowInfo(entity);
+ // 倒计时
+ countDownV2XEvent(30000);
+ }
+
+ /**
+ * 更新头部的窗体视图
+ */
+ private void updateTopWindowInfo(List v2XRoadEventEntity) {
+ // 清空数据
+ mItemList.clear();
+ mItemList.addAll(v2XRoadEventEntity);
+ // 刷新列表
+ mV2XRoadEventAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * 关闭详情展示框
+ */
+ @Override
+ public void close() {
+ Logger.d(MODULE_NAME, "V2X===关闭Window");
+
+ // 注册语音交互
+ V2XVoiceManager.INSTANCE.unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CLOSE_WINDOW);
+ V2XVoiceManager.INSTANCE.unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CLOSE_WINDOW_UN_WAKEUP);
+ V2XVoiceManager.INSTANCE.unRegisterPagingCallback();
+ V2XVoiceManager.INSTANCE
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CALL_CHATTING)
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_ZAN)
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_REPORT)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_CHAT_MORE_UN_WAKEUP)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_ZAN_UN_WAKEUP)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_FEEDBACK_TRUE)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_FEEDBACK_ERROR);
+
+ // 停止倒计时
+ if (handlerV2XEvent != null && runnableV2XEvent != null) {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ runnableV2XEvent = null;
+ }
+
+ if (V2XServiceManager
+ .getMogoTopViewManager().isViewAdded(this)) {
+ //移除窗体
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .removeSubView(this);
+ }
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void setWindowStatusListener(V2XWindowStatusListener listener) {
+ }
+
+ /**
+ * 窗体倒计时
+ */
+ public void countDownV2XEvent(int expireTime) {
+ // 倒计时
+ if (runnableV2XEvent == null) {
+ runnableV2XEvent = () -> {
+ Logger.d(MODULE_NAME, "V2X=== Window 30秒倒计时结束。。。");
+ // 移出Window详细信息
+ mTvEventStubClose.setText((countDownTime--) + "s");
+ handlerV2XEvent.postDelayed(runnableV2XEvent, 1000);
+ if (countDownTime <= 0) {
+ close();
+ }
+ };
+ } else {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ }
+ Logger.d(MODULE_NAME, "V2X=== Window 展示开始倒计时:" + expireTime);
+ handlerV2XEvent.postDelayed(runnableV2XEvent, 1000);
+ }
+}
+
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkMarker.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkMarker.java
new file mode 100644
index 0000000000..2f30437784
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkMarker.java
@@ -0,0 +1,123 @@
+package com.mogo.module.v2x.scenario.scene.park;
+
+import android.os.Handler;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+
+import com.mogo.map.marker.IMogoMarker;
+import com.mogo.map.marker.anim.OnMarkerAnimationListener;
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.common.entity.MarkerShowEntity;
+import com.mogo.module.common.map.MapCenterPointStrategy;
+import com.mogo.module.common.map.Scene;
+import com.mogo.module.service.ServiceConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.scenario.view.IV2XMarker;
+import com.mogo.module.v2x.utils.MarkerUtils;
+import com.mogo.utils.WorkThreadHandler;
+import com.mogo.utils.logger.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/6/19 17:37 PM
+ * desc : 违章停车场景POI绘制
+ * version: 1.0
+ */
+public class V2XIllegalParkMarker implements IV2XMarker> {
+
+ public ArrayList mIllegalParkMarkerList;
+
+ // 处理道路事件,30秒倒计时
+ private Handler handlerV2XEvent = new Handler();
+ private Runnable runnableV2XEvent;
+ private int mExpireTime = 30000;
+
+ @Override
+ public void drawPOI(List entityList) {
+ try {
+ mIllegalParkMarkerList = new ArrayList<>();
+ // 清除道路事件
+ V2XServiceManager
+ .getMoGoV2XMarkerManager().clearALLPOI();
+ if (entityList != null) {
+ for (int i = 0; i < entityList.size(); i++) {
+ MarkerExploreWay markerExploreWay = entityList.get(i);
+ MarkerShowEntity markerShowEntity = new MarkerShowEntity();
+ markerShowEntity.setBindObj(markerExploreWay);
+ markerShowEntity.setChecked(false);
+ markerShowEntity.setTextContent(markerExploreWay.getAddr());
+ markerShowEntity.setMarkerLocation(markerExploreWay.getLocation());
+ markerShowEntity.setMarkerType(ServiceConst.CARD_TYPE_NOVELTY);
+
+ WorkThreadHandler.getInstance().postDelayed(() -> {
+ IMogoMarker mogoMarker = V2XServiceManager.getIMogoMarkerService().drawMarker(markerShowEntity);
+ // 点击监听,天际弹窗展示详情
+ if (mogoMarker != null) {
+ mogoMarker.startScaleAnimation(0, 1.2f, 0, 1.2f, 300, new AccelerateInterpolator(), new OnMarkerAnimationListener() {
+ @Override
+ public void onAnimStart() {
+ Logger.d(MODULE_NAME, " onAnimStart ---1----> ");
+ }
+
+ @Override
+ public void onAnimEnd() {
+ if (mogoMarker.isDestroyed()) {
+ return;
+ }
+ mogoMarker.startScaleAnimation(1.2f, 1, 1.2f, 1, 100, new LinearInterpolator(), null);
+ }
+ });
+ mogoMarker.setOwner(MODULE_NAME);
+ mogoMarker.setObject(markerShowEntity);
+ mIllegalParkMarkerList.add(mogoMarker);
+ }
+ }, i * 100L);
+ }
+ }
+
+ countDownV2XEvent();
+ } catch (Exception e) {
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ MarkerUtils.resetMapZoom(16);
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ // 移动回原来的中心点
+ MapCenterPointStrategy.setMapCenterPointByScene(V2XServiceManager.getMapUIController(), Scene.AIMLESS);
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ MarkerUtils.resetMapZoom(16);
+ // 移除违章停车点
+ V2XServiceManager.getMarkerManager().removeMarkers(ServiceConst.CARD_TYPE_NOVELTY);
+ // 移除线
+ V2XServiceManager.getMoGoV2XPolylineManager().clearLine();
+ // 移除事件POI
+ V2XServiceManager.getMoGoV2XMarkerManager().clearAlarmPOI();
+ }
+
+ /**
+ * 倒计时
+ */
+ public void countDownV2XEvent() {
+ // 倒计时
+ if (runnableV2XEvent == null) {
+ runnableV2XEvent = () -> {
+ Logger.d(MODULE_NAME, "V2X===违章停车 30秒倒计时结束。。。");
+ // 移出Window详细信息
+ clearPOI();
+ };
+ } else {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ }
+ Logger.d(MODULE_NAME, "V2X===违章停车 POI 展示开始倒计时:" + mExpireTime);
+ handlerV2XEvent.postDelayed(runnableV2XEvent, mExpireTime);
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkScenario.java
new file mode 100644
index 0000000000..9bd5603dbe
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkScenario.java
@@ -0,0 +1,106 @@
+package com.mogo.module.v2x.scenario.scene.park;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.V2XStatusManager;
+import com.mogo.module.v2x.alarm.V2XAlarmServer;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.utils.TrackUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:37 PM
+ * desc : 违章停车
+ * version: 1.0
+ */
+public class V2XIllegalParkScenario extends AbsV2XScenario> {
+ private static final String TAG = "V2XILLegalParkScenario";
+
+ private static V2XIllegalParkScenario mV2XIllegalParkScenario;
+
+ public static V2XIllegalParkScenario getInstance() {
+ if (mV2XIllegalParkScenario == null) {
+ synchronized (V2XIllegalParkScenario.class) {
+ if (mV2XIllegalParkScenario == null) {
+ mV2XIllegalParkScenario = new V2XIllegalParkScenario();
+ mV2XIllegalParkScenario.setV2XWindow(new V2XIllegalParkWindow());
+ mV2XIllegalParkScenario.setV2XMarker(new V2XIllegalParkMarker());
+ }
+ }
+ }
+ return mV2XIllegalParkScenario;
+ }
+
+ @Override
+ public void init(@Nullable V2XMessageEntity> v2XMessageEntity) {
+ setV2XMessageEntity(v2XMessageEntity);
+ show();
+ }
+
+
+ @Override
+ public void show() {
+ if (V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ drawPOI();
+ }
+ showWindow();
+ }
+
+ @Override
+ public void showWindow() {
+ MarkerExploreWay markerExploreWay = null;
+ if (mV2XMessageEntity.getContent() != null) {
+ markerExploreWay = V2XAlarmServer
+ .getIllegalParkAlarmEvent(
+ (ArrayList) mV2XMessageEntity.getContent(),
+ V2XStatusManager.getInstance().getLocation());
+ }
+ if (markerExploreWay != null) {
+ //弹框
+ // 设置要展开的违章停车事件
+ //((V2XIllegalParkMarker) mV2XMarker).setOpenPoiId(markerExploreWay.getInfoId());
+ if (V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ speakTTSVoice(markerExploreWay.getAddr() + "可能被罚违章停车,您可以说,有用或没用来帮助其它车友。", null);
+ ((V2XIllegalParkWindow) mV2XWindow).show(markerExploreWay, true);
+ TrackUtils.trackV2xRoadShow(markerExploreWay.getInfoId(), V2XPoiTypeEnum.ALERT_ILLEGAL_PARK, "1");
+ } else {
+ speakTTSVoice(markerExploreWay.getAddr() + "可能被罚违章停车", null);
+ TrackUtils.trackV2xRoadShow(markerExploreWay.getInfoId(), V2XPoiTypeEnum.ALERT_ILLEGAL_PARK, "2");
+ }
+ }
+ }
+
+ @Override
+ public void closeWindow() {
+ V2XServiceManager.getMogoTopViewManager().removeView(mV2XWindow.getView());
+ }
+
+ @Override
+ public void showButton() {
+ }
+
+ @Override
+ public void closeButton() {
+ }
+
+ @Override
+ public void drawPOI() {
+ if (mV2XMessageEntity != null) {
+ mV2XMarker.drawPOI(mV2XMessageEntity.getContent());
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ }
+
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkWindow.java
new file mode 100644
index 0000000000..03e4b3a0c1
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/park/V2XIllegalParkWindow.java
@@ -0,0 +1,209 @@
+package com.mogo.module.v2x.scenario.scene.park;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.utils.RoadConditionUtils;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.module.v2x.view.HeartLikeView;
+import com.mogo.module.v2x.view.HeartUnLikeView;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.utils.logger.Logger;
+
+/**
+ * 违章停车
+ * 弹窗视图
+ */
+public class V2XIllegalParkWindow extends RelativeLayout implements IV2XWindow {
+ private static final String TAG = "V2XLegalParkWindow";
+
+ private TextView mAddressTv;
+ private TextView mIllegalNumTv;
+ private HeartLikeView mIlIllegalParkingLike;
+ private HeartUnLikeView mIIllegalParkingUnLike;
+
+ // 处理道路事件,20秒倒计时
+ private Handler handlerV2XEvent = new Handler();
+ private Runnable runnableV2XEvent;
+ private long mExpireTime = 20000;
+ private boolean mIsAutoClose = true;
+
+ private MarkerExploreWay mExploreWay;
+
+ // 反馈按钮语音操控
+ private V2XVoiceCallbackListener v2XVoiceCallbackYouYongListener = (command, intent) -> roadReportTrue();
+ private V2XVoiceCallbackListener v2XVoiceCallbackMeiYongListener = (command, intent) -> roadReportErr();
+
+ public V2XIllegalParkWindow() {
+ this(V2XServiceManager.getContext(), null);
+ }
+
+ public V2XIllegalParkWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XIllegalParkWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XIllegalParkWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ /**
+ * 初始化视图
+ */
+ private void initView(Context context) {
+ LayoutInflater.from(context).inflate(R.layout.window_illegal_parking, this);
+
+ mAddressTv = findViewById(R.id.tvAddress);
+ mIllegalNumTv = findViewById(R.id.tvIllegalNum);
+ mIlIllegalParkingLike = findViewById(R.id.llIllegalParkingLike);
+ mIIllegalParkingUnLike = findViewById(R.id.llIllegalParkingUnLike);
+ }
+
+ public void show(MarkerExploreWay entity, boolean isAutoClose) {
+ mIsAutoClose = isAutoClose;
+ show(entity);
+ }
+
+ /**
+ * 展示道路事件详情Windows
+ */
+ @Override
+ public void show(MarkerExploreWay entity) {
+ mExploreWay = entity;
+ //Logger.d(V2XConst.MODULE_NAME, "V2X===违章停车:展示 Window=\n" + entity);
+ mAddressTv.setText(entity.getAddr());
+ try {
+ mIllegalNumTv.setText("违章人数:" + (int) entity.getItems().get(0).getIllegalCount());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ mIlIllegalParkingLike.setOnClickCallListener(v -> {
+ Logger.d(V2XConst.MODULE_NAME, "反馈有用");
+ roadReportTrue();
+ });
+
+ mIIllegalParkingUnLike.setOnClickCallListener(v -> {
+ Logger.d(V2XConst.MODULE_NAME, "反馈无用");
+ roadReportErr();
+ });
+
+ // 倒计时
+ if (mIsAutoClose) {
+ countDownV2XEvent();
+ }
+
+ ViewGroup.LayoutParams layoutParams =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ (int) V2XUtils.getApp()
+ .getResources()
+ .getDimension(R.dimen.module_v2x_fatigue_driving_window_height_ground));
+
+ // 添加弹窗
+ V2XServiceManager.getMogoTopViewManager().addView(this, layoutParams);
+
+ // 注册语音交互
+ V2XVoiceManager.INSTANCE
+ .registerUnWakeVoice(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_ILLEGAL_PARKMARKER_FEEDBACK_YOUYONG_UN_WAKEUP,
+ v2XVoiceCallbackYouYongListener)
+ .registerUnWakeVoice(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_ILLEGAL_PARKMARKER_FEEDBACK_MEIYONG_UN_WAKEUP,
+ v2XVoiceCallbackMeiYongListener);
+
+ }
+
+ /**
+ * 关闭详情展示框
+ */
+ @Override
+ public void close() {
+ mIsAutoClose = false;
+
+ V2XVoiceManager.INSTANCE
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_ILLEGAL_PARKMARKER_FEEDBACK_YOUYONG_UN_WAKEUP)
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_ILLEGAL_PARKMARKER_FEEDBACK_MEIYONG_UN_WAKEUP);
+
+ // 停止倒计时
+ if (handlerV2XEvent != null && runnableV2XEvent != null) {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ runnableV2XEvent = null;
+ }
+
+ //移除窗体
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .removeView(this);
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void setWindowStatusListener(V2XWindowStatusListener listener) {
+ }
+
+ /**
+ * 窗体倒计时
+ */
+ public void countDownV2XEvent() {
+ // 倒计时
+ if (runnableV2XEvent == null) {
+ runnableV2XEvent = () -> {
+ Logger.d(V2XConst.MODULE_NAME, "V2X===20秒倒计时结束。。。");
+ // 移出Window详细信息
+ close();
+ };
+ } else {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ }
+ Logger.d(V2XConst.MODULE_NAME, "V2X===违章停车 Window 展示开始倒计时:" + mExpireTime);
+ handlerV2XEvent.postDelayed(runnableV2XEvent, mExpireTime);
+ }
+
+
+ /**
+ * 反馈路况正确
+ */
+ private void roadReportTrue() {
+ if (mExploreWay != null) {
+ RoadConditionUtils.sendDataErrorReceiverInfo(
+ mExploreWay.getPoiType(),
+ mExploreWay.getInfoId(),
+ "2");
+ }
+ close();
+ }
+
+ /**
+ * 反馈路况错误
+ */
+ private void roadReportErr() {
+ if (mExploreWay != null) {
+ RoadConditionUtils.sendDataErrorReceiverInfo(
+ mExploreWay.getPoiType(),
+ mExploreWay.getInfoId(),
+ "1");
+ }
+ close();
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventMarker.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventMarker.java
new file mode 100644
index 0000000000..10e6fa9ac1
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventMarker.java
@@ -0,0 +1,84 @@
+package com.mogo.module.v2x.scenario.scene.push;
+
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.common.entity.MarkerExploreWayItem;
+import com.mogo.module.common.entity.MarkerLocation;
+import com.mogo.module.common.map.MapCenterPointStrategy;
+import com.mogo.module.common.map.Scene;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.listener.V2XMarkerClickListener;
+import com.mogo.module.v2x.scenario.view.IV2XMarker;
+import com.mogo.module.v2x.utils.MarkerUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:37 PM
+ * desc : 推送场景
+ * version: 1.0
+ */
+public class V2XPushEventMarker implements IV2XMarker {
+ @Override
+ public void drawPOI(V2XPushMessageEntity entity) {
+ try {
+ // 清除道路事件
+ V2XServiceManager
+ .getMoGoV2XMarkerManager().clearALLPOI();
+
+ // 位置信息
+ MarkerLocation markerLocation = new MarkerLocation();
+ markerLocation.setLon(entity.getLon());
+ markerLocation.setLat(entity.getLat());
+
+ // 进行数据转换,用于Marker展示
+ V2XRoadEventEntity v2XRoadEventEntity = new V2XRoadEventEntity();
+ v2XRoadEventEntity.setLocation(markerLocation);
+ // 探路目前只有上报拥堵
+ v2XRoadEventEntity.setPoiType(V2XPoiTypeEnum.ALERT_TRAFFIC_EXPRESS);
+
+ MarkerExploreWay markerNoveltyInfo = new MarkerExploreWay();
+
+ List items = new ArrayList<>();
+ MarkerExploreWayItem exploreWayItem = new MarkerExploreWayItem();
+ exploreWayItem.setThumbnail(entity.getMsgImgUrl());
+ items.add(exploreWayItem);
+ markerNoveltyInfo.setPoiType(V2XPoiTypeEnum.ALERT_TRAFFIC_EXPRESS);
+ markerNoveltyInfo.setItems(items);
+ markerNoveltyInfo.setUploadType("1");
+
+ v2XRoadEventEntity.setNoveltyInfo(markerNoveltyInfo);
+ v2XRoadEventEntity.setExpireTime(20000);
+
+ V2XServiceManager
+ .getMoGoV2XMarkerManager()
+ .drawableAlarmPOI(V2XServiceManager.getContext(),
+ v2XRoadEventEntity,
+ V2XMarkerClickListener.getInstance());
+
+ } catch (Exception e) {
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ MarkerUtils.resetMapZoom(16);
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ // 移动回原来的中心点
+ MapCenterPointStrategy.setMapCenterPointByScene(V2XServiceManager.getMapUIController(), Scene.AIMLESS);
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ MarkerUtils.resetMapZoom(16);
+ // 移除线
+ V2XServiceManager.getMoGoV2XPolylineManager().clearLine();
+ // 移除事件POI
+ V2XServiceManager.getMoGoV2XMarkerManager().clearAlarmPOI();
+ // 绘制上次的数据
+ V2XServiceManager.getMoGoV2XMarkerManager().drawableLastAllPOI();
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventScenario.java
new file mode 100644
index 0000000000..85240f8983
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventScenario.java
@@ -0,0 +1,151 @@
+package com.mogo.module.v2x.scenario.scene.push;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.service.windowview.IMogoTopViewStatusListener;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:37 PM
+ * desc : 推送场景
+ * version: 1.0
+ */
+public class V2XPushEventScenario extends AbsV2XScenario implements IMogoTopViewStatusListener {
+
+ private static V2XPushEventScenario mV2XPushEventScenario;
+
+ private V2XPushEventScenario() {
+ }
+
+ public static V2XPushEventScenario getInstance() {
+ if (mV2XPushEventScenario == null) {
+ synchronized (V2XPushEventScenario.class) {
+ if (mV2XPushEventScenario == null) {
+ mV2XPushEventScenario = new V2XPushEventScenario();
+ mV2XPushEventScenario.setV2XMarker(new V2XPushEventMarker());
+ mV2XPushEventScenario.setV2XWindow(new V2XPushEventWindow());
+ }
+ }
+ }
+ return mV2XPushEventScenario;
+ }
+
+
+ @Override
+ public void init(@Nullable V2XMessageEntity v2XMessageEntity) {
+ Logger.w(MODULE_NAME, "处理推送场景:" + GsonUtil.jsonFromObject(v2XMessageEntity));
+
+ if (!isSameScenario(v2XMessageEntity)
+ && V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ setV2XMessageEntity(v2XMessageEntity);
+ show();
+ } else {
+ setV2XMessageEntity(v2XMessageEntity);
+ Logger.w(V2XConst.MODULE_NAME, "要处理的场景已经存在,丢弃这次初始化");
+ }
+ }
+
+ @Override
+ public void show() {
+ if (mV2XMessageEntity != null && mV2XMessageEntity.getContent() != null) {
+ speakTTSVoice(mV2XMessageEntity.getContent().getTts(), null);
+ drawPOI();
+ showWindow();
+ }
+ }
+
+ @Override
+ public void showWindow() {
+ if (mV2XWindow != null) {
+ ViewGroup.LayoutParams layoutParams =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ (int) V2XUtils.getApp().getResources().getDimension(R.dimen.module_v2x_event_window_height_ground));
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .addView(mV2XWindow.getView(), layoutParams,this);
+ mV2XWindow.show(mV2XMessageEntity.getContent());
+ V2XServiceManager.getMoGoV2XStatusManager().setPushWindowShow(TAG, true);
+ }
+ }
+
+ @Override
+ public void closeWindow() {
+ V2XServiceManager.getMoGoV2XStatusManager().setPushWindowShow(TAG, false);
+ if (mV2XWindow != null) {
+ mV2XWindow.close();
+ }
+ }
+
+ @Override
+ public void showButton() {
+ if (mV2XButton != null) {
+ mV2XButton.show();
+ }
+ }
+
+ @Override
+ public void closeButton() {
+ if (mV2XButton != null) {
+ mV2XButton.close();
+ }
+ }
+
+ @Override
+ public void drawPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.drawPOI(mV2XMessageEntity.getContent());
+ V2XServiceManager.getMoGoV2XStatusManager().setPushPOIShow(TAG, true);
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.clearPOI();
+ }
+ V2XServiceManager.getMoGoV2XStatusManager().setPushPOIShow(TAG, false);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
+ @Override
+ public void onViewAdded(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 动画结束");
+ }
+
+ @Override
+ public void onViewRemoved(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 动画结束");
+ V2XServiceManager.getMoGoV2XStatusManager().setPushWindowShow(TAG, false);
+ }
+
+ @Override
+ public void beforeViewAddAnim(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 开始");
+ }
+
+ @Override
+ public void beforeViewRemoveAnim(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 开始");
+ // 重置场景提示的消息
+ setV2XMessageEntity(null);
+ clearPOI();
+ }
+ ////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventWindow.java
new file mode 100644
index 0000000000..352c225212
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/push/V2XPushEventWindow.java
@@ -0,0 +1,202 @@
+package com.mogo.module.v2x.scenario.scene.push;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.PagerSnapHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.adapter.V2XPushEventAdapter;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.utils.logger.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/14 2:37 PM
+ * desc :
+ * TODO 目前睡前瞻推送使用的消息都在这里展示
+ * version: 1.0
+ */
+public class V2XPushEventWindow extends RelativeLayout implements IV2XWindow {
+ private String TAG = "V2XPushEventDetailWindow";
+ // 展示列表
+ private RecyclerView mRecyclerView;
+ // 列表数据适配器
+ private V2XPushEventAdapter mV2XRoadEventAdapter;
+ // 列表展示
+ private List mItemList = new ArrayList<>();
+ // 处理道路事件,30秒倒计时
+ private Handler handlerV2XEvent = new Handler();
+ private Runnable runnableV2XEvent;
+ private int mExpireTime = 30000;
+ // 关闭弹窗回调
+ private V2XVoiceCallbackListener v2XVoiceCallbackCloseWindowListener = (String command, Intent intent) -> {
+ close();
+ };
+
+ public V2XPushEventWindow() {
+ this(V2XServiceManager.getContext(), null);
+ }
+
+ public V2XPushEventWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XPushEventWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XPushEventWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ /**
+ * 初始化视图
+ */
+ private void initView(Context context) {
+ Logger.w(MODULE_NAME, "V2X===初始化推送的消息小窗口View。。。。。");
+ // 填充布局
+ LayoutInflater.from(context).inflate(R.layout.window_push_event_detail, this);
+ // 详情列表
+ mRecyclerView = findViewById(R.id.rvRoadEventList);
+ mV2XRoadEventAdapter = new V2XPushEventAdapter(mItemList);
+ mRecyclerView.setAdapter(mV2XRoadEventAdapter);
+ // 设置切换样式
+ new PagerSnapHelper().attachToRecyclerView(mRecyclerView);
+ // 配置列表朝向
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
+ mRecyclerView.setLayoutManager(layoutManager);
+ mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (recyclerView.getChildCount() > 0) {
+ if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+ // 用户处于交互的时候延后隐藏时间
+ countDownV2XEvent();
+ }
+ }
+ }
+ });
+
+ }
+
+ /**
+ * 更新头部的窗体视图
+ */
+ private void updateTopWindowInfo(V2XPushMessageEntity v2XRoadEventEntity) {
+ // 清空数据
+ mItemList.clear();
+ if (v2XRoadEventEntity != null) {
+ //Logger.d(MODULE_NAME, "V2X===推送消息:" + v2XRoadEventEntity);
+ mItemList.add(v2XRoadEventEntity);
+ }
+ // 刷新列表
+ mV2XRoadEventAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * 展示道路事件详情Windows
+ */
+ @Override
+ public void show(V2XPushMessageEntity entity) {
+ Logger.d(MODULE_NAME, "V2X===推送消息:展示 Window=\n" + entity);
+ // 更新头部的窗体视图
+ updateTopWindowInfo(entity);
+ // 倒计时
+ mExpireTime = entity.getExpireTime();
+ countDownV2XEvent();
+ // 注册语音交互
+ V2XVoiceManager.INSTANCE
+ .registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CLOSE_WINDOW, v2XVoiceCallbackCloseWindowListener)
+ .registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CLOSE_WINDOW_UN_WAKEUP, v2XVoiceCallbackCloseWindowListener);
+ }
+
+ /**
+ * 关闭详情展示框
+ */
+ @Override
+ public void close() {
+ Logger.d(MODULE_NAME, "V2X===关闭Window");
+ // 注册语音交互
+ V2XVoiceManager.INSTANCE
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CLOSE_WINDOW)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_CLOSE_WINDOW_UN_WAKEUP)
+ .unRegisterPagingCallback()
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CALL_CHATTING)
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_ZAN)
+ .unRegisterWakeCmd(
+ V2XVoiceConstants.COMMAND_ZHIDAO_V2X_REPORT)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_CHAT_MORE_UN_WAKEUP)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_ZAN_UN_WAKEUP)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_FEEDBACK_TRUE)
+ .unRegisterUnWakeVoice(
+ V2XVoiceConstants.COMMAND_V2X_TO_FEEDBACK_ERROR);
+ // 停止倒计时
+ if (handlerV2XEvent != null && runnableV2XEvent != null) {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ runnableV2XEvent = null;
+ }
+
+ //移除窗体
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .removeView(this);
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void setWindowStatusListener(V2XWindowStatusListener listener) {
+ }
+
+ /**
+ * 窗体倒计时
+ */
+ public void countDownV2XEvent() {
+ // 倒计时
+ if (runnableV2XEvent == null) {
+ runnableV2XEvent = () -> {
+ Logger.d(MODULE_NAME, "V2X===30秒倒计时结束。。。");
+ // 移出Window详细信息
+ close();
+ };
+ } else {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ }
+ Logger.d(MODULE_NAME, "V2X===推送消息 Window 展示开始倒计时:" + mExpireTime);
+ handlerV2XEvent.postDelayed(runnableV2XEvent, mExpireTime);
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventButton.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventButton.java
new file mode 100644
index 0000000000..7f195de836
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventButton.java
@@ -0,0 +1,83 @@
+package com.mogo.module.v2x.scenario.scene.road;
+
+import android.content.Intent;
+import android.view.View;
+import android.widget.TextView;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.scenario.view.IV2XButton;
+import com.mogo.module.v2x.scenario.view.IV2XButtonListener;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.service.entrance.ButtonIndex;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.scenario.view
+ * @ClassName: V2XRoadEventButton
+ * @Description: 道路实况按钮
+ * @Author: fenghl
+ * @CreateDate: 2020/5/18 10:46
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/5/18 10:46
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public class V2XRoadEventButton implements IV2XButton {
+ private TextView tv;
+ private IV2XButtonListener mListener;
+ private V2XVoiceCallbackListener mVoiceListener = (String command, Intent intent) -> {
+ if (mListener != null) {
+ mListener.onAction();
+ }
+ close();
+ };
+
+ @Override
+ public void setOnActionListener(IV2XButtonListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void show() {
+ // 注册语音交互
+ registerVoice();
+ tv = V2XServiceManager.getMogoEntranceButtonController().getButton(ButtonIndex.BUTTON1);
+ tv.setText("查看\n详情");
+ tv.setBackgroundResource(R.drawable.bg_v2x_event_live_show);
+ tv.setVisibility(View.VISIBLE);
+ tv.setOnClickListener(v -> {
+ if (mListener != null) {
+ mListener.onAction();
+ }
+ close();
+ });
+ }
+
+ @Override
+ public void close() {
+ if (tv != null) {
+ tv.setVisibility(View.GONE);
+ // 反注册语音交互
+ mListener = null;
+ }
+ unRegisterVoice();
+ }
+
+
+ @Override
+ public void registerVoice() {
+ V2XVoiceManager.INSTANCE
+ .registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_COMMON_CONFIRM, mVoiceListener)
+ .registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CONFIRM_UN_WAKEUP, mVoiceListener);
+ }
+
+ @Override
+ public void unRegisterVoice() {
+ V2XVoiceManager.INSTANCE
+ .unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_COMMON_CONFIRM)
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CONFIRM_UN_WAKEUP);
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventMarker.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventMarker.java
new file mode 100644
index 0000000000..b3da065410
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventMarker.java
@@ -0,0 +1,53 @@
+package com.mogo.module.v2x.scenario.scene.road;
+
+import com.mogo.module.common.map.MapCenterPointStrategy;
+import com.mogo.module.common.map.Scene;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.scenario.view.IV2XMarker;
+import com.mogo.module.v2x.utils.MarkerUtils;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.scenario.view
+ * @ClassName: V2XRoadEventMarker
+ * @Description: java类作用描述
+ * @Author: fenghl
+ * @CreateDate: 2020/5/18 10:48
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/5/18 10:48
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public class V2XRoadEventMarker implements IV2XMarker {
+ @Override
+ public void drawPOI(V2XRoadEventEntity entity) {
+ try {
+ // 清除道路事件
+ V2XServiceManager
+ .getMoGoV2XMarkerManager().clearALLPOI();
+ if (entity != null) {
+ V2XServiceManager.getMoGoV2XMarkerManager()
+ .drawableAlarmPOI(V2XServiceManager.getContext(), entity, null);
+ }
+ } catch (Exception e) {
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ MarkerUtils.resetMapZoom(16);
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ // 移动回原来的中心点
+ MapCenterPointStrategy.setMapCenterPointByScene(V2XServiceManager.getMapUIController(), Scene.AIMLESS);
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ MarkerUtils.resetMapZoom(16);
+ // 移除线
+ V2XServiceManager.getMoGoV2XPolylineManager().clearLine();
+ // 移除事件POI
+ V2XServiceManager.getMoGoV2XMarkerManager().clearAlarmPOI();
+ // 绘制上次的数据
+ V2XServiceManager.getMoGoV2XMarkerManager().drawableLastAllPOI();
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventScenario.java
new file mode 100644
index 0000000000..e69073c022
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventScenario.java
@@ -0,0 +1,212 @@
+package com.mogo.module.v2x.scenario.scene.road;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.scenario.scene.livecar.V2XRoadLiveCarScenario;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.utils.ADASUtils;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.service.windowview.IMogoTopViewStatusListener;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:37 PM
+ * desc : 道路预警场景
+ * version: 1.0
+ */
+public class V2XRoadEventScenario extends AbsV2XScenario implements IMogoTopViewStatusListener {
+ private static final String TAG = "V2XRoadEventScenario";
+ private static V2XRoadEventScenario mV2XRoadEventScenario;
+
+ private V2XRoadEventScenario() {
+ }
+
+ public static V2XRoadEventScenario getInstance() {
+ if (mV2XRoadEventScenario == null) {
+ synchronized (V2XRoadEventScenario.class) {
+ if (mV2XRoadEventScenario == null) {
+ mV2XRoadEventScenario = new V2XRoadEventScenario();
+ mV2XRoadEventScenario.setV2XButton(new V2XRoadEventButton());
+ mV2XRoadEventScenario.setV2XMarker(new V2XRoadEventMarker());
+ mV2XRoadEventScenario.setV2XWindow(new V2XRoadEventWindow());
+ }
+ }
+ }
+ return mV2XRoadEventScenario;
+ }
+
+ @Override
+ public void init(V2XMessageEntity v2XMessageEntity) {
+ try {
+ Logger.w(V2XConst.MODULE_NAME, "v2XMessageEntity:" + GsonUtil.jsonFromObject(v2XMessageEntity));
+
+ V2XRoadEventEntity v2XRoadEventEntity = v2XMessageEntity.getContent();
+ if (v2XRoadEventEntity != null) {
+ if (v2XMessageEntity.isShowState()) {
+ if (!isSameScenario(v2XMessageEntity)) {
+ // 更新要提醒的数据
+ setV2XMessageEntity(v2XMessageEntity);
+ show();
+ } else {
+ // 更新要提醒的数据
+ setV2XMessageEntity(v2XMessageEntity);
+ Logger.w(V2XConst.MODULE_NAME, "要处理的场景已经存在,丢弃这次初始化");
+ }
+ } else {
+ close();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void show() {
+ if (mV2XMessageEntity != null && mV2XMessageEntity.getContent() != null) {
+ // 如果道路事件小于50米的时候需要用户反馈
+ if (mV2XMessageEntity.getContent().getDistance() < 50) {
+ // 设置TTS
+ mV2XMessageEntity.getContent().getTtsWithFeedback();
+ } else {
+ // 设置TTS
+ mV2XMessageEntity.getContent().getTts(false);
+ }
+ // 广播给ADAS
+ ADASUtils.broadcastToADAS(
+ V2XServiceManager.getContext(),
+ mV2XMessageEntity.getContent());
+ if (V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ drawPOI();
+ showWindow();
+ }
+ }
+ }
+
+ @Override
+ public void showWindow() {
+ if (mV2XWindow != null) {
+ // 关闭直播弹窗
+ try {
+ IV2XWindow v2XWindow = V2XRoadLiveCarScenario.getInstance().getV2XWindow();
+ if (v2XWindow != null) {
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .removeSubView(v2XWindow.getView());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ // 关闭之前的事件弹窗
+ boolean isShowEventWindow = V2XServiceManager.getMoGoV2XStatusManager().isRoadEventWindowShow();
+ if (isShowEventWindow) {
+ ((V2XRoadEventWindow) mV2XWindow).updateTopWindowInfo(mV2XMessageEntity.getContent());
+ } else {
+ ViewGroup.LayoutParams layoutParams =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ (int) V2XUtils.getApp().getResources().getDimension(R.dimen.module_v2x_event_window_height));
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .addView(mV2XWindow.getView(), layoutParams, this);
+ mV2XWindow.show(mV2XMessageEntity.getContent());
+ }
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadEventWindowShow(TAG, true);
+ }
+
+ // 地图主动推送/触发消息 埋点
+ String poiType = mV2XMessageEntity.getContent().getPoiType();
+ String lat = String.valueOf(mV2XMessageEntity.getContent().getLocation().getLat());
+ String lon = String.valueOf(mV2XMessageEntity.getContent().getLocation().getLon());
+ String infoId = mV2XMessageEntity.getContent().getNoveltyInfo().getInfoId();
+ String style = V2XServiceManager.getMoGoStatusManager().isMainPageOnResume() ? "1" : "2";
+ Map properties = new HashMap<>();
+ properties.put("dbid", infoId);
+ properties.put("type", poiType);
+ properties.put("lng", lon);
+ properties.put("lat", lat);
+ properties.put("style", style);
+ V2XServiceManager.getMogoAnalytics().track(V2XConst.V2X_ROAD_SHOW, properties);
+ }
+
+ @Override
+ public void closeWindow() {
+ if (mV2XWindow != null) {
+ mV2XWindow.close();
+ }
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadEventWindowShow(TAG, false);
+ }
+
+ @Override
+ public void showButton() {
+ if (mV2XButton != null) {
+ mV2XButton.setOnActionListener(this::showWindow);
+ mV2XButton.show();
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadEventButtonShow(TAG, true);
+ }
+ }
+
+ @Override
+ public void closeButton() {
+ if (mV2XButton != null) {
+ mV2XButton.close();
+ }
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadEventButtonShow(TAG, false);
+ }
+
+ @Override
+ public void drawPOI() {
+ if (mV2XMarker != null) {
+ // 重置告警信息
+ V2XServiceManager.getV2XStatusManager().setAlarmInfo(mV2XMessageEntity.getContent());
+ mV2XMarker.drawPOI(mV2XMessageEntity.getContent());
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.clearPOI();
+ }
+ }
+
+ @Override
+ public void onViewAdded(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 动画结束");
+ }
+
+ @Override
+ public void onViewRemoved(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 动画结束");
+ V2XServiceManager.getMoGoV2XStatusManager().setRoadEventWindowShow(TAG, false);
+ }
+
+ @Override
+ public void beforeViewAddAnim(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 开始");
+ }
+
+ @Override
+ public void beforeViewRemoveAnim(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 开始");
+ // 重置场景提示的消息
+ setV2XMessageEntity(null);
+ clearPOI();
+ closeButton();
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventWindow.java
new file mode 100644
index 0000000000..8723cb1dd9
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadEventWindow.java
@@ -0,0 +1,285 @@
+package com.mogo.module.v2x.scenario.scene.road;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.PagerSnapHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.adapter.V2XRoadEventAdapter;
+import com.mogo.module.common.entity.V2XEventShowEntity;
+import com.mogo.module.v2x.entity.net.V2XLiveCarRes;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.common.entity.V2XWindowTypeEnum;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.network.V2XRefreshCallback;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.module.v2x.voice.V2XVoicePagingListener;
+import com.mogo.utils.logger.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/14 2:37 PM
+ * desc : 显示事件的列表
+ * version: 1.0
+ */
+public class V2XRoadEventWindow extends RelativeLayout
+ implements V2XRefreshCallback, IV2XWindow {
+ private String TAG = "V2XRoadEventDetailWindow";
+ // 展示列表
+ private RecyclerView mRecyclerView;
+ // 列表数据适配器
+ private V2XRoadEventAdapter mV2XRoadEventAdapter;
+ // 列表展示
+ private List mItemList = new ArrayList<>();
+ // 当前展示的位置
+ private int mCurPosition;
+ // 处理道路事件,30秒倒计时
+ private Handler handlerV2XEvent = new Handler();
+ private Runnable runnableV2XEvent;
+ private int mExpireTime = 20000;
+ // 关闭弹窗回调
+ private V2XVoiceCallbackListener v2XVoiceCallbackCloseWindowListener = (command, intent) -> close();
+ // 翻页回调监听
+ private V2XVoicePagingListener v2XVoicePagingCallbackListener = new V2XVoicePagingListener() {
+
+ @Override
+ public void onNextCallback() {
+ if (mRecyclerView != null && mCurPosition >= 0 && mCurPosition < mV2XRoadEventAdapter.getItemCount() - 1) {
+ mRecyclerView.scrollToPosition(mCurPosition + 1);
+ }
+ }
+
+ @Override
+ public void onPrevCallback() {
+ if (mRecyclerView != null && mCurPosition > 0 && mCurPosition <= mV2XRoadEventAdapter.getItemCount() - 1) {
+ mRecyclerView.scrollToPosition(mCurPosition - 1);
+ }
+ }
+ };
+
+ public V2XRoadEventWindow() {
+ this(V2XServiceManager.getContext(), null);
+ }
+
+ public V2XRoadEventWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XRoadEventWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XRoadEventWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ /**
+ * 初始化视图
+ */
+ private void initView(Context context) {
+ Logger.w(MODULE_NAME, "V2X===初始化道路事件小窗口View。。。。。");
+ // 填充布局
+ LayoutInflater.from(context).inflate(R.layout.window_road_event_detail, this);
+ // 详情列表
+ mRecyclerView = findViewById(R.id.rvRoadEventList);
+ mV2XRoadEventAdapter = new V2XRoadEventAdapter(mItemList);
+ mRecyclerView.setAdapter(mV2XRoadEventAdapter);
+ // 设置切换样式
+ new PagerSnapHelper().attachToRecyclerView(mRecyclerView);
+ // 配置列表朝向
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
+ mRecyclerView.setLayoutManager(layoutManager);
+ mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (recyclerView.getChildCount() > 0) {
+ if (newState == RecyclerView.SCROLL_STATE_IDLE) {
+ //获得当前显示在第一个item的位置
+ mCurPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
+ // 用户处于交互的时候延后隐藏时间
+ startCountDown();
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * 展示道路事件详情Windows
+ */
+ @Override
+ public void show(V2XRoadEventEntity entity) {
+ //Logger.d(MODULE_NAME, "V2X===道路事件详情:展示 Window:" + entity);
+ // 注册语音交互
+ V2XVoiceManager.INSTANCE
+ .registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CLOSE_WINDOW, v2XVoiceCallbackCloseWindowListener)
+ .registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CLOSE_WINDOW_UN_WAKEUP, v2XVoiceCallbackCloseWindowListener)
+ .registerPagingCallback(v2XVoicePagingCallbackListener);
+ // 更新头部的窗体视图
+ updateTopWindowInfo(entity);
+
+ // 倒计时
+ mExpireTime = entity.getExpireTime();
+ startCountDown();
+ }
+
+ /**
+ * 更新头部的窗体视图
+ */
+ public void updateTopWindowInfo(V2XRoadEventEntity v2XRoadEventEntity) {
+ Logger.d(MODULE_NAME, "V2X===道路事件详情:更新 Window:" + v2XRoadEventEntity);
+
+ // 清空数据
+ mItemList.clear();
+ if (v2XRoadEventEntity != null) {
+ // 道路事件行驶到了50米附近,弹出事件纠错框给用户
+ //Logger.d(MODULE_NAME, "V2X===道路事件:" + v2XRoadEventEntity);
+ // 进行类型分发
+ switch (v2XRoadEventEntity.getPoiType()) {
+ case V2XPoiTypeEnum.TRAFFIC_CHECK: // 交通检查
+ case V2XPoiTypeEnum.ROAD_CLOSED://封路
+ case V2XPoiTypeEnum.FOURS_ROAD_WORK://施工
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP://拥堵
+ case V2XPoiTypeEnum.FOURS_PONDING://积水
+ case V2XPoiTypeEnum.FOURS_FOG://浓雾
+ case V2XPoiTypeEnum.FOURS_ICE://结冰
+ case V2XPoiTypeEnum.FOURS_ACCIDENT://事故
+ // 展示道路事件本身详情
+ if (mItemList.isEmpty()) {
+ V2XEventShowEntity v2XEventShowEntity = new V2XEventShowEntity();
+ v2XEventShowEntity.setViewType(V2XWindowTypeEnum.ROAD_EVENT_WINDOW);
+ v2XEventShowEntity.setV2XRoadEventEntity(v2XRoadEventEntity);
+ mItemList.add(v2XEventShowEntity);
+ }
+ // 获取道路事件周边的直播车机
+ V2XServiceManager
+ .getV2XRefreshModel()
+ .queryNearbyVehicleLiveByLocation(
+ this,
+ v2XRoadEventEntity.getLocation().getLon(),
+ v2XRoadEventEntity.getLocation().getLat());
+ break;
+ }
+ }
+ // 刷新列表
+ mV2XRoadEventAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onSuccess(V2XLiveCarRes result) {
+ // TODO 这里是测试数据
+ //result = TestOnLineCarUtils.queryNearbyVehicleLiveByLocation();
+ //Logger.d(MODULE_NAME, "V2X===事件周边的直播车机:" + result);
+ try {
+ if (!mItemList.isEmpty() &&
+ result != null &&
+ result.getResult() != null &&
+ result.getResult().getInfo() != null &&
+ result.getResult().getInfo().size() > 0) {
+ mItemList.get(0).setV2XLiveCarList(result.getResult().getInfo());
+ }
+ // 刷新列表
+ mV2XRoadEventAdapter.notifyDataSetChanged();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onFail(String msg) {
+
+ }
+
+
+ /**
+ * 关闭详情展示框
+ */
+ @Override
+ public void close() {
+ Logger.d(MODULE_NAME, "V2X===关闭 Window");
+ // 注册语音交互
+ V2XVoiceManager.INSTANCE.unRegisterPagingCallback();
+ V2XVoiceManager.INSTANCE
+ .unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CLOSE_WINDOW)
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CLOSE_WINDOW_UN_WAKEUP)
+ .unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CALL_CHATTING)
+ .unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_ZAN)
+ .unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_REPORT)
+ .unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_LIVE_ROAD)
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_OPEN_LIVE_UN_WAKEUP)
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CHAT_MORE_UN_WAKEUP)
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_ZAN_UN_WAKEUP)
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_FEEDBACK_TRUE)
+ .unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_FEEDBACK_ERROR);
+
+ // 停止倒计时
+ if (handlerV2XEvent != null && runnableV2XEvent != null) {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ runnableV2XEvent = null;
+ }
+
+ //移除窗体
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .removeView(this);
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void setWindowStatusListener(V2XWindowStatusListener listener) {
+ }
+
+ /**
+ * 窗体倒计时
+ */
+ public void startCountDown() {
+ // 倒计时
+ if (runnableV2XEvent == null) {
+ runnableV2XEvent = () -> {
+ Logger.d(MODULE_NAME, "V2X=== Window 30秒倒计时结束。。。");
+ // 移出Window详细信息
+ close();
+ };
+ } else {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ }
+ Logger.d(MODULE_NAME, "V2X=== Window 展示开始倒计时:" + mExpireTime);
+ handlerV2XEvent.postDelayed(runnableV2XEvent, mExpireTime);
+ }
+
+ /**
+ * 停止倒计时
+ */
+ public void stopCountDown() {
+ // 停止倒计时
+ if (handlerV2XEvent != null && runnableV2XEvent != null) {
+ handlerV2XEvent.removeCallbacks(runnableV2XEvent);
+ runnableV2XEvent = null;
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpButton.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpButton.java
new file mode 100644
index 0000000000..84d9191d17
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpButton.java
@@ -0,0 +1,93 @@
+package com.mogo.module.v2x.scenario.scene.seek;
+
+import android.content.Intent;
+import android.view.View;
+import android.widget.TextView;
+
+import com.mogo.commons.data.BaseData;
+import com.mogo.commons.voice.AIAssist;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.network.V2XRefreshCallback;
+import com.mogo.module.v2x.scenario.view.IV2XButton;
+import com.mogo.module.v2x.scenario.view.IV2XButtonListener;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.service.entrance.ButtonIndex;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.scenario.view
+ * @ClassName: V2XSeekHelpButton
+ * @Description: 自车求助按钮
+ * @Author: fenghl
+ * @CreateDate: 2020/5/18 10:46
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/5/18 10:46
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public class V2XSeekHelpButton implements IV2XButton {
+ private TextView tv;
+ private IV2XButtonListener mListener;
+ private V2XVoiceCallbackListener cancelCb = (String command, Intent intent) -> {
+ doAction();
+ };
+
+ private void doAction() {
+ if (mListener != null) {
+ mListener.onAction();
+ }
+ }
+
+ @Override
+ public void setOnActionListener(IV2XButtonListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void show() {
+ registerVoice();
+ tv = V2XServiceManager.getMogoEntranceButtonController().getButton(ButtonIndex.BUTTON2);
+ tv.setText("取消\n求助");
+ tv.setVisibility(View.VISIBLE);
+ tv.setBackgroundResource(R.drawable.bg_v2x_event_live_show);
+ tv.setOnClickListener(v -> {
+ //调用取消求助接口
+ // TODO: 2020/5/18 回调,显示对话框
+ doAction();
+ });
+ }
+
+ @Override
+ public void close() {
+ if (tv != null) {
+ AIAssist.getInstance(V2XUtils.getApp()).speakTTSVoice("将为您取消", null);
+ tv.setVisibility(View.GONE);
+ V2XServiceManager
+ .getV2XRefreshModel().cancelHelpSignal(new V2XRefreshCallback() {
+ @Override
+ public void onSuccess(BaseData result) {
+ tv.setVisibility(View.GONE);
+ }
+ @Override
+ public void onFail(String msg) {
+ tv.setVisibility(View.VISIBLE);
+ }
+ });
+ mListener = null;
+ }
+ unRegisterVoice();
+ }
+ @Override
+ public void registerVoice() {
+ V2XVoiceManager.INSTANCE.registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CANCEL_HELP, cancelCb);
+ }
+
+ @Override
+ public void unRegisterVoice() {
+ V2XVoiceManager.INSTANCE.unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CANCEL_HELP);
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpDialog.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpDialog.java
new file mode 100644
index 0000000000..d1ae3f2d22
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpDialog.java
@@ -0,0 +1,176 @@
+package com.mogo.module.v2x.scenario.scene.seek;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.mogo.commons.voice.AIAssist;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.utils.WindowUtils;
+import com.mogo.utils.logger.Logger;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.window
+ * @ClassName: V2XDialog
+ * @Description: java类作用描述
+ * @Author: fenghl
+ * @CreateDate: 2020/4/22 14:27
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/4/22 14:27
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public class V2XSeekHelpDialog extends ConstraintLayout implements View.OnClickListener {
+ public static final String TAG = "V2XDialog";
+ private TextView tvContent;
+ private TextView tvLeft;
+ private TextView tvRight;
+ private Context mContext;
+
+ public V2XSeekHelpDialog(Context context) {
+ this(context, null);
+ }
+
+ public V2XSeekHelpDialog(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XSeekHelpDialog(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ init(context);
+ }
+
+ private void init(Context context) {
+ mContext = context;
+ LayoutInflater.from(context).inflate(R.layout.dialog_v2x_common, this);
+ tvContent = findViewById(R.id.tvDialogContent);
+ tvLeft = findViewById(R.id.tvDialogLeft);
+ tvRight = findViewById(R.id.tvDialogRight);
+ tvContent.setText("确定解除求助状态?");
+ tvLeft.setText("确定");
+ tvRight.setText("取消");
+ tvLeft.setOnClickListener(this);
+ tvRight.setOnClickListener(this);
+
+ }
+
+ private OnClickListener mListener;
+
+ public void setOnClickListener(OnClickListener listener) {
+ mListener = listener;
+ }
+
+ public interface OnClickListener {
+ void onClickLeft();
+
+ void onClickRight();
+ }
+
+ boolean isShown;
+ WindowManager windowManager;
+
+ public void show() {
+ Logger.d(TAG, "使用windowManager实现");
+ if (!isShown) {
+ windowManager = (WindowManager) mContext.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
+ WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ } else {
+ layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+ }
+ layoutParams.format = PixelFormat.TRANSLUCENT;
+ layoutParams.gravity = Gravity.START | Gravity.TOP;
+// mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ // FLAG_LAYOUT_IN_SCREEN:将window放置在整个屏幕之内,无视其他的装饰(比如状态栏); FLAG_NOT_TOUCH_MODAL:不阻塞事件传递到后面的窗口
+ layoutParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ layoutParams.width = WindowUtils.getScreenWidth(mContext);
+ layoutParams.height = WindowUtils.getScreenHeight(mContext);
+ //后面变暗区域透明...
+ layoutParams.dimAmount = 0;
+ layoutParams.x = 0;
+ layoutParams.y = 0;
+ windowManager.addView(this, layoutParams);
+ isShown = true;
+ AIAssist.getInstance(V2XUtils.getApp()).speakTTSVoice("确定要解除求助状态吗");
+ unRegisterVoiceCmd();
+ registerVoiceCmd();
+ }
+ }
+
+ private V2XVoiceCallbackListener voiceRightCb = (String command, Intent intent) -> {
+ //继续求助
+ handleRight();
+ };
+ private V2XVoiceCallbackListener voiceLeftCb = (String command, Intent intent) -> {
+
+ //放弃求助
+ handleLeft();
+ };
+
+ public void dismiss() {
+ if (isShown && windowManager != null) {
+ windowManager.removeViewImmediate(this);
+ windowManager = null;
+ isShown = false;
+ }
+ }
+
+
+ //放弃求助
+ private void handleLeft() {
+ if (mListener != null) {
+ mListener.onClickLeft();
+ }
+
+ unRegisterVoiceCmd();
+ }
+
+ //继续求助
+ private void handleRight() {
+ if (mListener != null) {
+ mListener.onClickRight();
+ }
+ unRegisterVoiceCmd();
+
+ }
+
+ private void registerVoiceCmd() {
+ V2XVoiceManager.INSTANCE.registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_COMMON_CONFIRM, voiceLeftCb);
+ V2XVoiceManager.INSTANCE.registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_COMMON_CANCEL, voiceRightCb);
+ V2XVoiceManager.INSTANCE.registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CONFIRM_UN_WAKEUP, voiceLeftCb);
+ V2XVoiceManager.INSTANCE.registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CANCEL_UN_WAKEUP, voiceRightCb);
+ }
+
+ private void unRegisterVoiceCmd() {
+ V2XVoiceManager.INSTANCE.unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_COMMON_CONFIRM);
+ V2XVoiceManager.INSTANCE.unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_COMMON_CANCEL);
+ V2XVoiceManager.INSTANCE.unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CONFIRM_UN_WAKEUP);
+ V2XVoiceManager.INSTANCE.unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CANCEL_UN_WAKEUP);
+ }
+
+ @Override
+ public void onClick(View v) {
+ int vId = v.getId();
+ if (vId == R.id.tvDialogLeft) {
+ handleLeft();
+ } else if (vId == R.id.tvDialogRight) {
+ handleRight();
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpMarker.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpMarker.java
new file mode 100644
index 0000000000..7ccbf06cfa
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpMarker.java
@@ -0,0 +1,54 @@
+package com.mogo.module.v2x.scenario.scene.seek;
+
+import android.content.Context;
+
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.v2x.listener.V2XMarkerClickListener;
+import com.mogo.module.v2x.scenario.view.IV2XMarker;
+import com.mogo.module.v2x.utils.MarkerUtils;
+
+import java.util.List;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.scenario.view
+ * @ClassName: V2XSeekHelpMarker
+ * @Description: java类作用描述
+ * @Author: fenghl
+ * @CreateDate: 2020/5/18 10:48
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/5/18 10:48
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public class V2XSeekHelpMarker implements IV2XMarker> {
+
+ @Override
+ public void drawPOI(List entity) {
+ // 移除上一次的数据
+ Context context = V2XServiceManager.getContext();
+ for (V2XMarkerEntity coordinate : entity) {
+ //故障车机
+ if (coordinate.getTargetId() == V2XPoiTypeEnum.ALERT_CAR_TROUBLE_WARNING) {
+ //绘制
+ V2XServiceManager
+ .getMoGoV2XMarkerManager()
+ .drawableSpecialCarPOI(context, coordinate, V2XMarkerClickListener.getInstance());
+ }
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ MarkerUtils.resetMapZoom(16);
+ // 移除线
+ V2XServiceManager.getMoGoV2XPolylineManager().clearLine();
+ // 移除事件POI
+ V2XServiceManager.getMoGoV2XMarkerManager().clearSpecialCarPOI();
+ // 绘制上次的数据
+ V2XServiceManager.getMoGoV2XMarkerManager().drawableLastAllPOI();
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpScenario.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpScenario.java
new file mode 100644
index 0000000000..a014a47eac
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpScenario.java
@@ -0,0 +1,217 @@
+package com.mogo.module.v2x.scenario.scene.seek;
+
+import android.os.CountDownTimer;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.commons.voice.AIAssist;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.scenario.impl.AbsV2XScenario;
+import com.mogo.module.v2x.utils.ADASUtils;
+import com.mogo.module.v2x.utils.V2XUtils;
+import com.mogo.service.windowview.IMogoTopViewStatusListener;
+import com.mogo.utils.logger.Logger;
+
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:37 PM
+ * desc : 故障求助场景
+ * version: 1.0
+ */
+public class V2XSeekHelpScenario extends AbsV2XScenario> implements IMogoTopViewStatusListener {
+ private volatile static V2XSeekHelpScenario mV2XSeekHelpScenario;
+
+ private V2XSeekHelpScenario() {
+ }
+
+ public static V2XSeekHelpScenario getInstance() {
+ if (mV2XSeekHelpScenario == null) {
+ synchronized (V2XSeekHelpScenario.class) {
+ if (mV2XSeekHelpScenario == null) {
+ mV2XSeekHelpScenario = new V2XSeekHelpScenario();
+ }
+ }
+ }
+ return mV2XSeekHelpScenario;
+ }
+
+ private List mMarkerEntity;
+
+ @Override
+ public void init(@Nullable V2XMessageEntity> v2XMessageEntity) {
+ mV2XMessageEntity = v2XMessageEntity;
+ if (V2XServiceManager.getMoGoStatusManager().isMainPageLaunched()) {
+ if (mV2XMessageEntity != null) {
+ mMarkerEntity = mV2XMessageEntity.getContent();
+ }
+ if (!isShown) {
+ setV2XWindow(new V2XSeekHelpWindow());
+ setV2XMarker(new V2XSeekHelpMarker());
+ } else {
+ countDownFaultHelpWindowCancel();
+ closeWindow();
+ clearPOI();
+ }
+ show();
+ }
+ AIAssist.getInstance(V2XUtils.getApp()).speakTTSVoice("发现其他车主的求助信息");
+ // 广播给ADAS和Launcher卡片
+ V2XRoadEventEntity eventEntity = new V2XRoadEventEntity();
+ eventEntity.setPoiType(V2XPoiTypeEnum.ALERT_CAR_TROUBLE_WARNING + "");
+ eventEntity.setExpireTime(30000);
+ eventEntity.setAlarmContent("其他车主求助");
+ ADASUtils.broadcastToADAS(V2XServiceManager.getContext(), eventEntity);
+ }
+
+ @Override
+ public void show() {
+ showWindow();
+ drawPOI();
+ }
+
+ @Override
+ public void close() {
+ countDownFaultHelpWindowCancel();
+ super.close();
+ mV2XWindow = null;
+ mV2XMarker = null;
+ mV2XButton = null;
+ }
+
+ private volatile boolean isShown = false;
+ private ArrayBlockingQueue> blockingQueue = new ArrayBlockingQueue<>(5);
+ private CountDownTimer faultHelpTimer;
+
+ private void countDownFaultHelpWindowCancel() {
+ if (faultHelpTimer != null) {
+ faultHelpTimer.cancel();
+ faultHelpTimer = null;
+ }
+ blockingQueue.clear();
+ isShown = false;
+ }
+
+ private void countDownFaultHelpWindowStart() {
+ if (faultHelpTimer == null) {
+ faultHelpTimer = new CountDownTimer(20_000, 20_000) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ // Logger.d(TAG, "故障求助倒计时: " + millisUntilFinished);
+ }
+
+ @Override
+ public void onFinish() {
+ Logger.d(TAG, "故障求助倒计时: onFinish");
+ isShown = false;
+ List result = blockingQueue.poll();
+ closeWindow();
+ clearPOI();
+ if (result != null) {
+ mMarkerEntity = result;
+ show();
+ }
+ }
+
+ };
+ }
+ faultHelpTimer.start();
+ }
+
+ @Override
+ public void showWindow() {
+ V2XServiceManager.getMoGoV2XStatusManager().setOtherSeekHelpWindowShow(TAG, true);
+ if (mV2XWindow != null && mMarkerEntity != null) {
+ if (!isShown) {
+ isShown = true;
+ countDownFaultHelpWindowStart();
+ View view = mV2XWindow.getView();
+ Logger.d(TAG, "添加window= " + view);
+ ViewGroup.LayoutParams layoutParams =
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ (int) V2XUtils.getApp().getResources().getDimension(R.dimen.module_v2x_fatigue_driving_window_height_ground));
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .addView(mV2XWindow.getView(), layoutParams,this);
+ mV2XWindow.show(mMarkerEntity);
+ } else {
+ blockingQueue.offer(mMarkerEntity);
+ }
+ }
+
+ }
+
+ @Override
+ public void closeWindow() {
+ if (V2XServiceManager.getMoGoV2XStatusManager().isOtherSeekHelpWindowShow()) {
+ Logger.d(TAG, "关闭求助车辆的Window!");
+ V2XServiceManager.getMoGoV2XStatusManager().setOtherSeekHelpWindowShow(TAG, false);
+ if (mV2XWindow != null) {
+ View view = mV2XWindow.getView();
+ Logger.d(TAG, "移除window= " + view);
+ V2XServiceManager
+ .getMogoTopViewManager()
+ .removeView(view);
+ mV2XWindow.close();
+ }
+ }
+ }
+
+ @Override
+ public void showButton() {
+
+ }
+
+ @Override
+ public void closeButton() {
+
+ }
+
+ @Override
+ public void drawPOI() {
+ if (mV2XMarker != null && mMarkerEntity != null) {
+ mV2XMarker.drawPOI(mMarkerEntity);
+ }
+ }
+
+ @Override
+ public void clearPOI() {
+ if (mV2XMarker != null) {
+ mV2XMarker.clearPOI();
+ }
+ }
+
+ @Override
+ public void onViewAdded(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 动画结束");
+ }
+
+ @Override
+ public void onViewRemoved(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 动画结束");
+ V2XServiceManager.getMoGoV2XStatusManager().setOtherSeekHelpWindowShow(TAG, false);
+ }
+
+ @Override
+ public void beforeViewAddAnim(View view) {
+ Logger.d(MODULE_NAME, "展示 Window 开始");
+ }
+
+ @Override
+ public void beforeViewRemoveAnim(View view) {
+ Logger.d(MODULE_NAME, "关闭 Window 开始");
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpWindow.java
new file mode 100644
index 0000000000..55cbc39080
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/seek/V2XSeekHelpWindow.java
@@ -0,0 +1,276 @@
+package com.mogo.module.v2x.scenario.scene.seek;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.PagerSnapHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.mogo.map.MogoLatLng;
+import com.mogo.map.navi.IMogoNaviListener;
+import com.mogo.map.navi.MogoNaviInfo;
+import com.mogo.map.navi.MogoTraffic;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.adapter.V2XSeekHelpAdapter;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity;
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+import com.mogo.module.v2x.scenario.view.IV2XWindow;
+import com.mogo.module.v2x.voice.V2XVoiceCallbackListener;
+import com.mogo.module.v2x.voice.V2XVoiceConstants;
+import com.mogo.module.v2x.voice.V2XVoiceManager;
+import com.mogo.service.module.IMogoRegisterCenter;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.carchattingprovider.CallChattingProviderConstant;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.window
+ * @ClassName: V2XFaultHelpWindow
+ * @Description: 故障求助window
+ * @Author: fenghl
+ * @CreateDate: 2020/4/13 15:24
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/4/13 15:24
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public class V2XSeekHelpWindow extends ConstraintLayout implements V2XSeekHelpAdapter.OnViewClickListener, IMogoNaviListener, IV2XWindow> {
+ private static final String TAG = V2XSeekHelpWindow.class.getSimpleName();
+ private RecyclerView mRecyclerView;
+ private int mCurPosition;
+ private V2XSeekHelpAdapter mAdapter;
+ IMogoRegisterCenter mMogoRegisterCenter = null;
+ private V2XMarkerEntity mMarkerEntity;
+ // 弹窗状态监听
+ private V2XWindowStatusListener mV2XWindowStatusListener;
+
+ public V2XSeekHelpWindow() {
+ this(V2XServiceManager.getContext(), null);
+ }
+ public V2XSeekHelpWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XSeekHelpWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XSeekHelpWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ private void init(Context context) {
+ Logger.w(MODULE_NAME, "初始化道路故障求助窗口View。。。。。");
+ LayoutInflater.from(context).inflate(R.layout.window_fault_help, this);
+ initRecyclerView(context);
+ initVoice();
+ mMogoRegisterCenter = V2XServiceManager.getMogoRegisterCenter();
+ }
+
+ private void initRecyclerView(Context context) {
+ mRecyclerView = findViewById(R.id.rvFaultHelp);
+ LinearLayoutManager layoutManager = new LinearLayoutManager(context, RecyclerView.VERTICAL, false);
+ mRecyclerView.setLayoutManager(layoutManager);
+ mAdapter = new V2XSeekHelpAdapter(context);
+ mAdapter.setOnViewClickListener(this);
+ mRecyclerView.setAdapter(mAdapter);
+
+ mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (recyclerView.getChildCount() > 0 && newState == RecyclerView.SCROLL_STATE_IDLE) {
+ mCurPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
+ Logger.d(TAG, "v2x 故障求助 当前显示位置===" + mCurPosition);
+ }
+ }
+ });
+ new PagerSnapHelper().attachToRecyclerView(mRecyclerView);
+ }
+
+ private V2XVoiceCallbackListener mChatCb = new V2XVoiceCallbackListener() {
+ @Override
+ public void onCallback(String command, Intent intent) {
+ if (mMarkerEntity != null) {
+ callChatting(mMarkerEntity);
+ }
+ }
+ };
+ private V2XVoiceCallbackListener mNaviCb = new V2XVoiceCallbackListener() {
+ @Override
+ public void onCallback(String command, Intent intent) {
+ if (mMarkerEntity != null) {
+ startNavi(mMarkerEntity.getLat(), mMarkerEntity.getLon());
+ }
+ }
+ };
+
+ /**
+ * 注册语音
+ */
+ private void initVoice() {
+ registerVoiceCmd();
+ }
+
+ private void registerVoiceCmd() {
+ V2XVoiceManager.INSTANCE.registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CALL_CHATTING, mChatCb);
+ V2XVoiceManager.INSTANCE.registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CHAT_MORE_UN_WAKEUP, mChatCb);
+ V2XVoiceManager.INSTANCE.registerWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_NAVI, mNaviCb);
+ V2XVoiceManager.INSTANCE.registerUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_NAVI_UN_WAKEUP, mNaviCb);
+ }
+
+ private void unregisterVoiceCmd() {
+ V2XVoiceManager.INSTANCE.unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_CALL_CHATTING);
+ V2XVoiceManager.INSTANCE.unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_CHAT_MORE_UN_WAKEUP);
+ V2XVoiceManager.INSTANCE.unRegisterWakeCmd(V2XVoiceConstants.COMMAND_ZHIDAO_V2X_NAVI);
+ V2XVoiceManager.INSTANCE.unRegisterUnWakeVoice(V2XVoiceConstants.COMMAND_V2X_TO_NAVI_UN_WAKEUP);
+ }
+
+
+ private void callChatting(V2XMarkerEntity entity) {
+ try {
+ Map params = new HashMap<>(16);
+ params.put(CallChattingProviderConstant.CCPROVIDER_SN, entity.getSn());
+ V2XMarkerEntity.UserInfoBean infoBean = entity.getUserInfo();
+ if (infoBean != null) {
+ params.put(CallChattingProviderConstant.CCPROVIDER_USER_IMG, infoBean.getHeadImgUrl());
+
+ params.put(CallChattingProviderConstant.CCPROVIDER_USER_AGE, infoBean.getAge() + "");
+ params.put(CallChattingProviderConstant.CCPROVIDER_USER_SEX, infoBean.getSex() + "");
+ params.put(CallChattingProviderConstant.CCPROVIDER_NICK_NAME, infoBean.getDisplayName());
+ }
+// params.put(CallChattingProviderConstant.CCPROVIDER_CAR_TYPE, );
+
+ //params.put(CallChattingProviderConstant.CCPROVIDER_ADDRESS, location.getAddress());
+ params.put(CallChattingProviderConstant.CCPROVIDER_LAT, entity.getLat() + "");
+ params.put(CallChattingProviderConstant.CCPROVIDER_LON, entity.getLon() + "");
+
+
+ Logger.d(TAG, "调用车聊聊传入参数==$params===" + params);
+ V2XServiceManager.getCarsChattingProvider().call(params);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onViewChatClick(V2XMarkerEntity entity) {
+ Log.d(TAG, "点击:调用车聊聊通话!");
+ callChatting(entity);
+ }
+
+ @Override
+ public void onViewNaviClick(double lat, double lng) {
+ startNavi(lat, lng);
+ }
+
+ private void startNavi(double lat, double lng) {
+ Log.d(TAG, "去导航!");
+ MogoLatLng endPoint = new MogoLatLng(lat, lng);
+ V2XServiceManager.getNavi().naviTo(endPoint);
+ synchronized (V2XSeekHelpWindow.class) {
+ mMogoRegisterCenter.unregisterMogoNaviListener(MODULE_NAME);
+ mMogoRegisterCenter.registerMogoNaviListener(MODULE_NAME, this);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ unregisterVoiceCmd();
+ mAdapter.clearData();
+ mAdapter.setOnViewClickListener(null);
+ synchronized (V2XSeekHelpWindow.class) {
+ mMogoRegisterCenter.unregisterMogoNaviListener(MODULE_NAME);
+ }
+ mMarkerEntity = null;
+
+ }
+
+ @Override
+ public void onInitNaviFailure() {
+
+ }
+
+ @Override
+ public void onInitNaviSuccess() {
+
+ }
+
+ @Override
+ public void onNaviInfoUpdate(MogoNaviInfo naviinfo) {
+
+ }
+
+ @Override
+ public void onStartNavi() {
+
+ }
+
+ @Override
+ public void onStopNavi() {
+
+ }
+
+ @Override
+ public void onCalculateSuccess() {
+ V2XServiceManager.getNavi().startNavi(true);
+ }
+
+ @Override
+ public void onoCalculateFailed() {
+
+ }
+
+ @Override
+ public void onUpdateTraffic(MogoTraffic traffic) {
+
+ }
+
+
+ @Override
+ public void show(List entityList) {
+ // TODO: 2020/4/13 根据需求仅展示一个,默认显示第一个
+ if (entityList != null && !entityList.isEmpty()) {
+ V2XMarkerEntity entity = entityList.get(0);
+ mAdapter.addData(entityList.get(0));
+ mAdapter.notifyDataSetChanged();
+ mMarkerEntity = entity;
+ }
+ //mAdapter.addDataList(entityList);
+ }
+
+ @Override
+ public void close() {
+ /* Logger.d(TAG,"移除window= "+this);
+ V2XServiceManager
+ .getIMogoWindowManager()
+ .removeView(this);*/
+ }
+
+ @Override
+ public View getView() {
+ return this;
+ }
+
+ @Override
+ public void setWindowStatusListener(V2XWindowStatusListener listener) {
+ mV2XWindowStatusListener = listener;
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/test/V2XTestConsoleWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/test/V2XTestConsoleWindow.java
new file mode 100644
index 0000000000..db742c9792
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/test/V2XTestConsoleWindow.java
@@ -0,0 +1,130 @@
+package com.mogo.module.v2x.scenario.scene.test;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes;
+import com.mogo.module.v2x.utils.TestOnLineCarUtils;
+
+import java.util.List;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/24 11:34 AM
+ * desc : 用来测试的控制台 Window
+ * version: 1.0
+ */
+public class V2XTestConsoleWindow extends ConstraintLayout {
+
+ private Button mBtnTriggerRoadEvent;
+ private Button mBtnTriggerPushEvent;
+ private Button mBtnTriggerPushLiveCarEvent;
+ private Button mBtnTriggerAnimationEvent;
+ private Button mBtnTriggerFatigueDrivingEvent;
+ private Button mBtnTriggerSeekHelpEvent;
+ private Button mBtnTriggerParkEvent;
+
+ public V2XTestConsoleWindow(Context context) {
+ this(context, null);
+ }
+
+ public V2XTestConsoleWindow(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public V2XTestConsoleWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ private void initView(Context context) {
+ LayoutInflater.from(context).inflate(R.layout.window_test_console, this);
+
+ mBtnTriggerRoadEvent = findViewById(R.id.btnTriggerRoadEvent);
+ mBtnTriggerPushEvent = findViewById(R.id.btnTriggerPushEvent);
+ mBtnTriggerPushLiveCarEvent = findViewById(R.id.btnTriggerPushLiveCarEvent);
+ mBtnTriggerAnimationEvent = findViewById(R.id.btnTriggerAnimationEvent);
+ mBtnTriggerFatigueDrivingEvent = findViewById(R.id.btnTriggerFatigueDrivingEvent);
+ mBtnTriggerSeekHelpEvent = findViewById(R.id.btnTriggerSeekHelpEvent);
+ mBtnTriggerParkEvent = findViewById(R.id.btnTriggerParkEvent);
+
+ mBtnTriggerRoadEvent.setOnClickListener(v -> {
+ V2XMessageEntity v2XMessageEntity =
+ TestOnLineCarUtils.getV2XScenarioRoadEventData();
+
+ Intent intent = new Intent(V2XConst.BROADCAST_SCENE_HANDLER_ACTION);
+ intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
+ });
+
+ mBtnTriggerPushEvent.setOnClickListener(v -> {
+ V2XMessageEntity v2XMessageEntity =
+ TestOnLineCarUtils.getV2XScenarioPushEventData();
+
+ Intent intent = new Intent(V2XConst.BROADCAST_SCENE_HANDLER_ACTION);
+ intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
+ });
+
+ mBtnTriggerPushLiveCarEvent.setOnClickListener(v -> {
+ V2XMessageEntity v2XMessageEntity =
+ TestOnLineCarUtils.getV2XScenarioPushEventData();
+
+ Intent intent = new Intent(V2XConst.BROADCAST_SCENE_HANDLER_ACTION);
+ intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
+ });
+
+ mBtnTriggerAnimationEvent.setOnClickListener(v -> {
+ V2XMessageEntity v2XMessageEntity =
+ TestOnLineCarUtils.getV2XScenarioAnimationEventData();
+
+ Intent intent = new Intent(V2XConst.BROADCAST_SCENE_HANDLER_ACTION);
+ intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
+ });
+
+ mBtnTriggerFatigueDrivingEvent.setOnClickListener(v -> {
+ V2XMessageEntity v2XMessageEntity =
+ TestOnLineCarUtils.getV2XScenarioFatigueDrivingData();
+
+ Intent intent = new Intent(V2XConst.BROADCAST_SCENE_HANDLER_ACTION);
+ intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
+ });
+
+ mBtnTriggerSeekHelpEvent.setOnClickListener(v -> {
+ V2XMessageEntity> v2XMessageEntity =
+ TestOnLineCarUtils.getV2XScenarioSeekHelpData();
+
+ Intent intent = new Intent(V2XConst.BROADCAST_SCENE_HANDLER_ACTION);
+ intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
+ });
+
+
+ mBtnTriggerParkEvent.setOnClickListener(v -> {
+ V2XMessageEntity> v2XMessageEntity =
+ TestOnLineCarUtils.getV2XIllegalParkData();
+
+ Intent intent = new Intent(V2XConst.BROADCAST_SCENE_HANDLER_ACTION);
+ intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
+ });
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XButton.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XButton.java
new file mode 100644
index 0000000000..9ff7d04cc0
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XButton.java
@@ -0,0 +1,38 @@
+package com.mogo.module.v2x.scenario.view;
+
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 5:02 PM
+ * desc :
+ * version: 1.0
+ */
+public interface IV2XButton {
+ /**
+ * 设置监听器,处理button点击和语音唤醒
+ * @param listener
+ */
+ void setOnActionListener(IV2XButtonListener listener);
+
+ /**
+ * 按钮显示
+ */
+ void show();
+
+ /**
+ * 按钮关闭
+ */
+ void close();
+
+ /**
+ * 语音注册
+ */
+ void registerVoice();
+
+ /**
+ * 语音反注册
+ */
+ void unRegisterVoice();
+
+}
\ No newline at end of file
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XButtonListener.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XButtonListener.java
new file mode 100644
index 0000000000..6d9876fc99
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XButtonListener.java
@@ -0,0 +1,22 @@
+package com.mogo.module.v2x.scenario.view;
+
+import android.view.View;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.scenario.view
+ * @ClassName: IV2XButtonListener
+ * @Description: java类作用描述
+ * @Author: fenghl
+ * @CreateDate: 2020/5/18 15:58
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/5/18 15:58
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public interface IV2XButtonListener {
+ /**
+ * 响应点击或语音触发事件
+ */
+ void onAction();
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XMarker.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XMarker.java
new file mode 100644
index 0000000000..eb46fdef6a
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XMarker.java
@@ -0,0 +1,16 @@
+package com.mogo.module.v2x.scenario.view;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 4:13 PM
+ * desc : V2X安全驾驶场景接口
+ * version: 1.0
+ */
+public interface IV2XMarker {
+
+ void drawPOI(T entity);
+
+ void clearPOI();
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XWindow.java
new file mode 100644
index 0000000000..55159f0338
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/view/IV2XWindow.java
@@ -0,0 +1,44 @@
+package com.mogo.module.v2x.scenario.view;
+
+import android.view.View;
+
+import com.mogo.module.v2x.listener.V2XWindowStatusListener;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.statusmanager
+ * @ClassName: IV2XWindow
+ * @Description: java类作用描述
+ * @Author: fenghl
+ * @CreateDate: 2020/5/15 11:24
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/5/15 11:24
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public interface IV2XWindow {
+
+ /**
+ * 展示1/2窗口
+ */
+ void show(T entity);
+
+ /**
+ * 关闭1/2窗口
+ */
+ void close();
+
+ /**
+ * 返回窗体
+ *
+ * @return 当前窗体
+ */
+ View getView();
+
+ /**
+ * 设置Window状态监听
+ *
+ * @param listener 监听器
+ */
+ void setWindowStatusListener(V2XWindowStatusListener listener);
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ADASUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ADASUtils.java
new file mode 100644
index 0000000000..26e1e56bd4
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ADASUtils.java
@@ -0,0 +1,139 @@
+package com.mogo.module.v2x.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
+
+import com.mogo.commons.voice.AIAssist;
+import com.mogo.commons.voice.IMogoVoiceCmdCallBack;
+import com.mogo.commons.voice.VoicePreemptType;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.entity.net.V2XAlarmEventRes;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.utils.AppUtils;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/3 6:54 PM
+ * desc :
+ * version: 1.0
+ */
+public class ADASUtils {
+ /**
+ * 广播给ADAS进行展示
+ *
+ * @param v2XRoadEventEntity 广播信息
+ */
+ public static void broadcastToADAS(Context context, V2XRoadEventEntity v2XRoadEventEntity) {
+ try {
+ V2XPushMessageEntity v2XAlarmMessage = new V2XPushMessageEntity();
+ v2XAlarmMessage.setTts(v2XRoadEventEntity.getTts());
+ v2XAlarmMessage.setAlarmContent(v2XRoadEventEntity.getAlarmContent());
+ v2XAlarmMessage.setExpireTime(v2XRoadEventEntity.getExpireTime());
+ v2XAlarmMessage.setSceneId(v2XRoadEventEntity.getPoiType());
+
+ broadcastToADAS(context, v2XAlarmMessage);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 广播给ADAS进行展示
+ *
+ * @param alarmInfo 广播信息
+ */
+ public static void broadcastToADAS(Context context, V2XAlarmEventRes.AlarmInfo alarmInfo) {
+ try {
+ V2XPushMessageEntity v2XAlarmMessage = new V2XPushMessageEntity();
+ v2XAlarmMessage.setTts(alarmInfo.getTts());
+ v2XAlarmMessage.setAlarmContent(alarmInfo.getAlarmContent());
+ v2XAlarmMessage.setExpireTime(alarmInfo.getExpireTime());
+ v2XAlarmMessage.setSceneId(alarmInfo.getPoiType());
+
+ broadcastToADAS(context, v2XAlarmMessage);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 广播给ADAS进行展示
+ *
+ * @param alarmMessage 广播信息
+ */
+ public static void broadcastToADAS(Context context, V2XPushMessageEntity alarmMessage) {
+ try {
+ Intent intent = new Intent("com.mogo.launcher.v2x");
+ intent.putExtra("v2x_warning_type", alarmMessage.getSceneId());
+ intent.putExtra("v2x_warining_timeout", alarmMessage.getExpireTime());
+ intent.putExtra("v2x_warning_tts", alarmMessage.getTts());
+ intent.putExtra("v2x_warning_info", alarmMessage.getAlarmContent());
+ context.sendBroadcast(intent);
+
+ // 这里是兼容 1+16G 版本没有ADAS的时候自身进行TTS
+ if (!AppUtils.isAppInstalled(context, "com.zhidao.autopilot")) {
+ speakTTSVoice(alarmMessage.getTts(), null);
+ }
+
+ Logger.i(MODULE_NAME, "向ADAS分发服务器下发的事件," + alarmMessage.toString());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 广播给ADAS进行展示
+ *
+ * @param alarmInfo 广播信息
+ */
+ public static void broadcastToADAS_TTS(Context context, V2XAlarmEventRes.AlarmInfo alarmInfo) {
+ try {
+ V2XPushMessageEntity v2XAlarmMessage = new V2XPushMessageEntity();
+ v2XAlarmMessage.setTts(alarmInfo.getTts());
+ v2XAlarmMessage.setAlarmContent(alarmInfo.getAlarmContent());
+ v2XAlarmMessage.setExpireTime(alarmInfo.getExpireTime());
+ v2XAlarmMessage.setSceneId(alarmInfo.getPoiType());
+
+ broadcastToADAS_TTS(context, v2XAlarmMessage);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 广播给ADAS进行TTS播报
+ *
+ * @param alarmMessage 广播信息
+ */
+ public static void broadcastToADAS_TTS(Context context, V2XPushMessageEntity alarmMessage) {
+ try {
+ Intent intent = new Intent("com.mogo.launcher.v2x.tts");
+ intent.putExtra("v2x_warning_tts", alarmMessage.getTts());
+ context.sendBroadcast(intent);
+
+ // 这里是兼容 1+16G 版本没有ADAS的时候自身进行TTS
+ if (!AppUtils.isAppInstalled(context, "com.zhidao.autopilot")) {
+ speakTTSVoice(alarmMessage.getTts(), null);
+ }
+
+ Logger.i(MODULE_NAME, "向ADAS分发服务器下发的事件," + alarmMessage.toString());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void speakTTSVoice(@Nullable String msg, IMogoVoiceCmdCallBack callBack) {
+ if (!TextUtils.isEmpty(msg)) {
+ Logger.w(V2XConst.MODULE_NAME, "调用TTS播放语音:" + msg);
+ AIAssist.getInstance(V2XUtils.getApp()).speakTTSVoice(msg, VoicePreemptType.PREEMPT_TYPE_IMMEADIATELY, callBack);
+ }
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ChartingUtil.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ChartingUtil.java
new file mode 100644
index 0000000000..c507a39220
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ChartingUtil.java
@@ -0,0 +1,101 @@
+package com.mogo.module.v2x.utils;
+
+import com.mogo.module.common.entity.MarkerLocation;
+import com.mogo.module.common.entity.MarkerUserInfo;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.carchattingprovider.CallChattingProviderConstant;
+import com.zhidao.carchattingprovider.ICallChatResponse;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/15 4:26 PM
+ * desc : 调用车聊聊的工具类
+ * version: 1.0
+ */
+public class ChartingUtil {
+ public interface ChartStatusListener {
+ void canCall(boolean b);
+ }
+
+ /**
+ * 调用车聊聊拨打电话
+ *
+ * @param userInfo 用户数据
+ * @param location 位置信息
+ */
+ public static void callChatting(MarkerUserInfo userInfo, MarkerLocation location) {
+ try {
+ Map params = new HashMap<>();
+ params.put(CallChattingProviderConstant.CCPROVIDER_SN, userInfo.getSn());
+ params.put(CallChattingProviderConstant.CCPROVIDER_USER_IMG, userInfo.getUserHead());
+ params.put(CallChattingProviderConstant.CCPROVIDER_USER_AGE, userInfo.getAgeNumber() + "");
+ params.put(CallChattingProviderConstant.CCPROVIDER_NICK_NAME, userInfo.getUserName());
+ params.put(CallChattingProviderConstant.CCPROVIDER_USER_SEX, userInfo.getGender() + "");
+
+ if (location != null) {
+ params.put(CallChattingProviderConstant.CCPROVIDER_ADDRESS, location.getAddress());
+ params.put(CallChattingProviderConstant.CCPROVIDER_LAT, location.getLat() + "");
+ params.put(CallChattingProviderConstant.CCPROVIDER_LON, location.getLon() + "");
+ }
+
+ Logger.d(MODULE_NAME, "调用车聊聊传入参数:\n" + params);
+ V2XServiceManager.getCarsChattingProvider().call(params);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 判断是否可以拨打电话
+ *
+ * @param chartStatusListener 车聊聊状态监听
+ */
+ public static void isOnLine(String sn, ChartStatusListener chartStatusListener) {
+ V2XServiceManager.getCarsChattingProvider().isOnLine(
+ "CAR_CALL_TO_" + V2XConst.MODULE_NAME,
+ V2XUtils.getApp(),
+ sn,
+ new ICallChatResponse() {
+ @Override
+ public void isOnLine(boolean onLine, @Nullable String errorMsg) {
+ Logger.e(V2XConst.MODULE_NAME, "isOnLine:" + onLine + " errorMsg:" + errorMsg);
+ if (chartStatusListener != null) {
+ chartStatusListener.canCall(onLine);
+ }
+ }
+ });
+
+ }
+
+ /**
+ * 判断是否可以拨打电话
+ *
+ * @param chartStatusListener 车聊聊状态监听
+ */
+ public static void isCanCall(ChartStatusListener chartStatusListener) {
+ V2XServiceManager.getCarsChattingProvider().canCall(
+ "CAR_CALL_TO_" + V2XConst.MODULE_NAME,
+ V2XUtils.getApp(),
+ new ICallChatResponse() {
+ @Override
+ public void canCall(boolean onLine) {
+ Logger.e(V2XConst.MODULE_NAME, "isOnLine:" + onLine);
+
+ if (chartStatusListener != null) {
+ chartStatusListener.canCall(onLine);
+ }
+ }
+ });
+
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/DrivingDirectionUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/DrivingDirectionUtils.java
new file mode 100644
index 0000000000..f045b6d0c1
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/DrivingDirectionUtils.java
@@ -0,0 +1,109 @@
+package com.mogo.module.v2x.utils;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/14 1:02 PM
+ * desc : 计算车辆驾驶方向的工具类
+ * version: 1.0
+ */
+public class DrivingDirectionUtils {
+
+ /**
+ * 计算车辆行驶方向 与 poi点到车辆的连线 间的夹角
+ *
+ * @param carLon 车辆位置 lon
+ * @param carLat 车辆位置 lat
+ * @param poiLon poi 位置 lon
+ * @param poiLat poi 位置 lat
+ * @param carAngle 车辆行驶方向
+ * @return
+ */
+ public static int getDegreeOfCar2Poi(double carLon, double carLat, double poiLon, double poiLat, int carAngle) {
+ int poiAngle = 0;
+ // 以子午线作为y轴 计算两点的余切 再将余切值转化为角度
+ double _angle = Math.atan2(Math.abs(carLon - poiLon), Math.abs(carLat - poiLat)) * (180 / Math.PI);
+
+ if (poiLon > carLon) {
+ // poi 在 车辆位置的第1象限
+ if (poiLat > carLat) {
+ poiAngle = (int) _angle;
+ }
+ // poi 在 车辆位置的第2象限
+ else {
+ poiAngle = 180 - (int) _angle;
+ }
+ } else {
+ // poi 在 车辆位置的第3象限
+ if (poiLat < carLat) {
+ poiAngle = (int) _angle + 180;
+ }
+ // poi 在 车辆位置的第4象限
+ else {
+ poiAngle = 360 - (int) _angle;
+ }
+ }
+ return calculationAngle(poiAngle, carAngle);
+ }
+
+
+ /**
+ * 计算车辆行驶方向 与 poi点到车辆的连线 间的夹角
+ *
+ * @param carLat 车辆位置 lat
+ * @param carLon 车辆位置 lon
+ * @param poiLat poi 位置 lat
+ * @param poiLon poi 位置 lon
+ */
+ public static int getCarAngle(double carLat, double carLon, double poiLat, double poiLon) {
+ int poiAngle = 0;
+ // 以子午线作为y轴 计算两点的余切 再将余切值转化为角度
+ double _angle = Math.atan2(Math.abs(carLon - poiLon), Math.abs(carLat - poiLat)) * (180 / Math.PI);
+
+ if (poiLon > carLon) {
+ // poi 在 车辆位置的第1象限
+ if (poiLat > carLat) {
+ poiAngle = (int) _angle;
+ }
+ // poi 在 车辆位置的第2象限
+ else {
+ poiAngle = 180 - (int) _angle;
+ }
+ } else {
+ // poi 在 车辆位置的第3象限
+ if (poiLat < carLat) {
+ poiAngle = (int) _angle + 180;
+ }
+ // poi 在 车辆位置的第4象限
+ else {
+ poiAngle = 360 - (int) _angle;
+ }
+ }
+
+ if (poiAngle >= 355) {
+ poiAngle = 0;
+ }
+ return poiAngle;
+ }
+
+ /**
+ * 计算两个行驶方向间的夹角 计算结果小于180度
+ *
+ * @param angle0
+ * @param angle1
+ * @return
+ */
+ public static int calculationAngle(int angle0, int angle1) {
+ // 获取两方向间夹角
+ int angle = Math.abs(angle0 - angle1);
+ if (angle > 180) {
+ int minAngle = Math.min(angle0, angle1);
+ int maxAngle = Math.max(angle0, angle1);
+ return 180 - Math.abs(minAngle + 180 - maxAngle);
+ } else {
+ return angle;
+ }
+ }
+
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/EventTypeUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/EventTypeUtils.java
new file mode 100644
index 0000000000..e70f2114cd
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/EventTypeUtils.java
@@ -0,0 +1,125 @@
+package com.mogo.module.v2x.utils;
+
+import com.mogo.module.v2x.R;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+
+/**
+ * @ProjectName: MoGoModulSafeDriving
+ * @Package: com.mogo.module.v2x.utils
+ * @ClassName: EventTypeUtils
+ * @Description: java类作用描述
+ * @Author: fenghl
+ * @CreateDate: 2020/5/20 17:10
+ * @UpdateUser: 更新者:
+ * @UpdateDate: 2020/5/20 17:10
+ * @UpdateRemark: 更新说明:
+ * @Version: 1.0
+ */
+public class EventTypeUtils {
+ public static String getPoiTypeStr(String poiType) {
+ String str = "其它道路事件";
+ switch (poiType) {
+ // 停车场
+ case V2XPoiTypeEnum.FOURS_PARKING:
+ str = "停车场";
+ break;
+ // 加油站
+ case V2XPoiTypeEnum.GAS_STATION:
+ str = "加油站";
+ break;
+ // 交通检查
+ case V2XPoiTypeEnum.TRAFFIC_CHECK:
+ str = "交通检查";
+ break;
+ // 封路
+ case V2XPoiTypeEnum.ROAD_CLOSED:
+ str = "封路";
+ break;
+ // 施工
+ case V2XPoiTypeEnum.FOURS_ROAD_WORK:
+ str = "道路施工";
+ break;
+ // 拥堵
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP:
+ str = "道路拥堵";
+ break;
+ // 积水
+ case V2XPoiTypeEnum.FOURS_PONDING:
+ str = "道路积水";
+ break;
+ // 浓雾
+ case V2XPoiTypeEnum.FOURS_FOG:
+ str = "出现浓雾";
+ break;
+ // 结冰
+ case V2XPoiTypeEnum.FOURS_ICE:
+ str = "路面结冰";
+ break;
+ // 事故
+ case V2XPoiTypeEnum.FOURS_ACCIDENT:
+ str = "交通事故";
+ break;
+ default:
+ str = "其它道路事件";
+ break;
+ }
+ return str;
+ }
+
+ /**
+ * 获取道路事件的背景色
+ *
+ * @param poiType poi类型
+ * @return 背景
+ */
+ public static int getPoiTypeBg(String poiType) {
+ int strBg;
+ switch (poiType) {
+ case V2XPoiTypeEnum.FOURS_PARKING: // 停车场
+ case V2XPoiTypeEnum.GAS_STATION: // 加油站
+ strBg = R.drawable.bg_v2x_event_type_blue;
+ break;
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP: // 拥堵
+ strBg = R.drawable.bg_v2x_event_type_orange;
+ break;
+ case V2XPoiTypeEnum.TRAFFIC_CHECK:// 交通检查
+ case V2XPoiTypeEnum.ROAD_CLOSED:// 封路
+ case V2XPoiTypeEnum.FOURS_ROAD_WORK:// 施工
+ case V2XPoiTypeEnum.FOURS_PONDING:// 积水
+ case V2XPoiTypeEnum.FOURS_FOG: // 浓雾
+ case V2XPoiTypeEnum.FOURS_ICE: // 结冰
+ case V2XPoiTypeEnum.FOURS_ACCIDENT: // 事故
+ strBg = R.drawable.bg_v2x_event_type_read;
+ break;
+ default:
+ strBg = R.drawable.bg_v2x_event_type_read;
+ break;
+ }
+ return strBg;
+ }
+
+
+ /**
+ * 判断是否是道路预警事件
+ *
+ * @param poiType
+ * @return
+ */
+ public static boolean isRoadEvent(String poiType) {
+ boolean isRoadEvent = false;
+ // 进行类型分发
+ switch (poiType) {
+ case V2XPoiTypeEnum.TRAFFIC_CHECK: // 交通检查
+ case V2XPoiTypeEnum.ROAD_CLOSED://封路
+ case V2XPoiTypeEnum.FOURS_ROAD_WORK://施工
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP://拥堵
+ case V2XPoiTypeEnum.FOURS_PONDING://积水
+ case V2XPoiTypeEnum.FOURS_FOG://浓雾
+ case V2XPoiTypeEnum.FOURS_ICE://结冰
+ case V2XPoiTypeEnum.FOURS_ACCIDENT://事故
+ isRoadEvent = true;
+ break;
+ }
+ return isRoadEvent;
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/FatigueDrivingUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/FatigueDrivingUtils.java
new file mode 100644
index 0000000000..0322137b9d
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/FatigueDrivingUtils.java
@@ -0,0 +1,82 @@
+package com.mogo.module.v2x.utils;
+
+import android.text.TextUtils;
+
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.entity.net.V2XStrategyPushRes;
+import com.mogo.utils.logger.Logger;
+import com.mogo.utils.storage.SharedPrefsMgr;
+import com.zhidao.utils.common.GsonUtil;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/26 3:12 PM
+ * desc : 疲劳驾驶的工具类
+ * version: 1.0
+ */
+public class FatigueDrivingUtils {
+
+ /**
+ * 获取驾驶时常
+ *
+ * @return 驾驶市场
+ */
+ public static long getDrivingTime() {
+ // 获取 ACC ON 时间
+ String accOnTime = SharedPrefsMgr.getInstance(V2XUtils.getApp())
+ .getString(V2XConst.V2X_ACC_ON_TIME_STR);
+ if (TextUtils.isEmpty(accOnTime)) {
+ accOnTime = TimeUtils.getNowString();
+ // 记录开机时间
+ SharedPrefsMgr.getInstance(V2XUtils.getApp())
+ .putString(V2XConst.V2X_ACC_ON_TIME_STR, accOnTime);
+ }
+ return Math.abs(TimeUtils.getTimeSpanByNow(accOnTime, TimeConstants.MIN));
+ }
+
+ /**
+ * 刷新ACC ON时间记录
+ * 获取上次记录的开机事件,判断如果当前acc on时间与上次acc off时间<20min的忽略
+ */
+ public static void refreshAccOnTime() {
+ // 获取 ACC ON 时间
+ String localAccONTime = SharedPrefsMgr.getInstance(V2XUtils.getApp())
+ .getString(V2XConst.V2X_ACC_ON_TIME_STR);
+ Logger.d(V2XConst.MODULE_NAME, "ACC ON时间:" + localAccONTime);
+
+ // 如果本地没有记录时间则记录
+ if (!TextUtils.isEmpty(localAccONTime)) {
+ // 获取配置信息
+ V2XStrategyPushRes.ResultBean strategyPushEntity =
+ GsonUtil.objectFromJson(SharedPrefsMgr.getInstance(V2XUtils.getApp())
+ .getString(V2XConst.V2X_STRATEGY_PUSH), V2XStrategyPushRes.ResultBean.class);
+ if (strategyPushEntity != null) {
+ // 获取 当前 ACC ON 时间
+ String accOnTime = TimeUtils.getNowString();
+ // 获取 ACC OFF 时间
+ String accOFFTime = SharedPrefsMgr.getInstance(V2XUtils.getApp())
+ .getString(V2XConst.V2X_ACC_OFF_TIME_STR);
+ if (!TextUtils.isEmpty(accOFFTime)) {
+ // 比较开关机时间,如果acc of 比 acc on 时间还要靠近说明acc on 时间记录有问题,需要更新同步
+ long timeSpan = TimeUtils.getTimeSpan(accOnTime, accOFFTime, TimeConstants.MIN);
+ Logger.d(V2XConst.MODULE_NAME, "开关机时间间隔:" + timeSpan);
+ if (timeSpan >= strategyPushEntity.getRestIgnoreMinutes()) {
+ // 记录开机时间
+ SharedPrefsMgr.getInstance(V2XUtils.getApp())
+ .putString(V2XConst.V2X_ACC_ON_TIME_STR, accOnTime);
+ }
+ }
+ } else {
+ // 记录开机时间
+ SharedPrefsMgr.getInstance(V2XUtils.getApp())
+ .putString(V2XConst.V2X_ACC_ON_TIME_STR, TimeUtils.getNowString());
+ }
+ } else {
+ // 记录开机时间
+ SharedPrefsMgr.getInstance(V2XUtils.getApp())
+ .putString(V2XConst.V2X_ACC_ON_TIME_STR, TimeUtils.getNowString());
+ }
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ImageUtil.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ImageUtil.java
new file mode 100644
index 0000000000..76ba3e4c03
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ImageUtil.java
@@ -0,0 +1,78 @@
+package com.mogo.module.v2x.utils;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.text.TextUtils;
+
+/**
+ * 图片工具来
+ */
+public class ImageUtil {
+
+ /**
+ * 根据需求的宽和高以及图片实际的宽和高计算SampleSize
+ */
+ public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth,
+ int reqHeight) {
+ int width = options.outWidth;
+ int height = options.outHeight;
+
+ int inSampleSize = 1;
+
+ if (width > reqWidth || height > reqHeight) {
+ int widthRadio = Math.round(width * 1.0f / reqWidth);
+ int heightRadio = Math.round(height * 1.0f / reqHeight);
+
+ inSampleSize = Math.max(widthRadio, heightRadio);
+ }
+
+ return inSampleSize;
+ }
+
+ /**
+ * 根据要求的宽高,从本地路径得到bitmap
+ *
+ * @param picPath
+ * @param width
+ * @param height
+ * @return
+ */
+ public static Bitmap createBitmap(String picPath, int width, int height) {
+ if (!TextUtils.isEmpty(picPath)) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+// opts.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(picPath, opts);
+// opts.inSampleSize = caculateInSampleSize(opts, width, height);
+// opts.inJustDecodeBounds = false;
+ Bitmap bitmap = BitmapFactory.decodeFile(picPath, opts);
+ return bitmap;
+ }
+ return null;
+ }
+
+ public static Bitmap createBitmap(Context context, int resId, int width, int height) {
+ if (resId != 0) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+// opts.inJustDecodeBounds = true;
+ BitmapFactory.decodeResource(context.getResources(), resId, opts);
+// opts.inSampleSize = caculateInSampleSize(opts, width, height);
+// opts.inJustDecodeBounds = false;
+ Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId, opts);
+ return bitmap;
+ }
+ return null;
+ }
+
+ public static Bitmap getBitmap(Context context, Object path, int width, int height) {
+ Bitmap bitmap = null;
+ if (path instanceof String) {
+ bitmap = createBitmap((String) path, width, height);
+ } else if (path instanceof Integer) {
+ if (context != null) {
+ bitmap = createBitmap(context, (Integer) path, width, height);
+ }
+ }
+ return bitmap;
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/LocationUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/LocationUtils.java
new file mode 100644
index 0000000000..a3a8f4aed5
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/LocationUtils.java
@@ -0,0 +1,88 @@
+package com.mogo.module.v2x.utils;
+
+import android.location.Location;
+
+import com.mogo.map.MogoLatLng;
+import com.mogo.map.location.MogoLocation;
+import com.mogo.map.search.geo.IMogoGeoSearchListener;
+import com.mogo.map.search.geo.MogoGeocodeResult;
+import com.mogo.map.search.geo.MogoPoiItem;
+import com.mogo.map.search.geo.MogoRegeocodeResult;
+import com.mogo.map.search.geo.query.MogoRegeocodeQuery;
+import com.mogo.map.search.poisearch.IMogoPoiSearch;
+import com.mogo.map.search.poisearch.IMogoPoiSearchListener;
+import com.mogo.map.search.poisearch.MogoPoiResult;
+import com.mogo.map.search.poisearch.query.MogoPoiSearchQuery;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.utils.logger.Logger;
+import com.zhidao.utils.common.GsonUtil;
+
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/5/15 10:31 AM
+ * desc : 基于位置工具类
+ * version: 1.0
+ */
+public class LocationUtils {
+ private static final String TAG = "LocationUtils";
+
+ public static void geoCodeSearch(MogoLocation location,IMogoGeoSearchListener listener) {
+ MogoRegeocodeQuery mogoRegeocodeQuery = new MogoRegeocodeQuery();
+ mogoRegeocodeQuery.setPoint(new MogoLatLng(location.getLatitude(), location.getLongitude()));
+
+ V2XServiceManager.getMogoGeoSearch().setGeoSearchListener(listener);
+
+ V2XServiceManager.getMogoGeoSearch().getFromLocationAsyn(mogoRegeocodeQuery);
+ }
+
+ /**
+ * 根据关键词进行查询
+ */
+ public static void searchPOI(String keyword) {
+ MogoPoiSearchQuery poiSearchQuery = new MogoPoiSearchQuery(keyword, keyword);
+ poiSearchQuery.setPageSize(10);
+ poiSearchQuery.setLocation(getCurrentLatLon());
+ IMogoPoiSearch poiSearch = V2XServiceManager.getMapService().getPoiSearch(V2XUtils.getApp(), poiSearchQuery);
+
+ poiSearch.setPoiSearchListener(new IMogoPoiSearchListener() {
+ @Override
+ public void onPoiSearched(MogoPoiResult result, int errorCode) {
+ Logger.i(V2XConst.MODULE_NAME,
+ "keyword: " + keyword +
+ "\nPOI查询结果为:" + GsonUtil.jsonFromObject(result.getPois()));
+ }
+
+ @Override
+ public void onPoiItemSearched(MogoPoiItem item, int errorCode) {
+
+ }
+ });
+ poiSearch.searchPOIAsyn();
+ }
+
+ public static MogoLatLng getCurrentLatLon() {
+ MogoLatLng latLon = V2XServiceManager.getNavi().getCarLocation();
+ if (latLon == null) {
+ Location location = V2XServiceManager.getNavi().getCarLocation2();
+ if (location != null) {
+ latLon = new MogoLatLng(location.getLatitude(), location.getLongitude());
+ }
+ }
+ if (latLon == null) {
+ MogoLocation location = V2XServiceManager.getMogoLocationClient().getLastKnowLocation();
+ if (location != null) {
+ latLon = new MogoLatLng(location.getLatitude(), location.getLongitude());
+ }
+ }
+ if (latLon == null) {
+ latLon = V2XServiceManager.getMapUIController().getWindowCenterLocation();
+ }
+ return latLon;
+ }
+
+
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/MarkerUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/MarkerUtils.java
new file mode 100644
index 0000000000..f31fb111d0
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/MarkerUtils.java
@@ -0,0 +1,141 @@
+package com.mogo.module.v2x.utils;
+
+import android.content.Context;
+import android.graphics.Rect;
+
+import com.mogo.map.MogoLatLng;
+import com.mogo.map.marker.IMogoMarkerClickListener;
+import com.mogo.module.common.entity.MarkerLocation;
+import com.mogo.module.common.entity.MarkerShowEntity;
+import com.mogo.module.common.utils.CarSeries;
+import com.mogo.module.v2x.V2XConst;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes.V2XMarkerEntity;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.utils.WindowUtils;
+import com.mogo.utils.logger.Logger;
+
+import java.util.Collections;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/3 5:52 PM
+ * desc :
+ * version: 1.0
+ */
+@Deprecated
+public class MarkerUtils {
+ private static final String TAG = "MarkerUtils";
+
+ /**
+ * 特殊车辆推送绘制,实时更新的
+ *
+ * @param v2XSpecialCarRes 绘制地图气泡
+ */
+ public static void handlerV2XSpecialVehicleMarker(V2XSpecialCarRes v2XSpecialCarRes,
+ Context context,
+ IMogoMarkerClickListener clickListener) {
+ try {
+ // 移除上一次的数据
+ V2XServiceManager.getMarkerManager().removeMarkers(V2XConst.V2X_MARKER_SPECIAL_CAR);
+ // 循环绘制
+ for (V2XMarkerEntity v2XMarkerEntity : v2XSpecialCarRes.getCoordinates()) {
+ if (v2XMarkerEntity.getTargetId() != V2XPoiTypeEnum.ALERT_CAR_TROUBLE_WARNING) {
+ MarkerLocation markerLocation = new MarkerLocation();
+ markerLocation.setLon(v2XMarkerEntity.getLon());
+ markerLocation.setLat(v2XMarkerEntity.getLat());
+ // 进行数据转换,用于Marker展示
+ MarkerShowEntity markerShowEntity = new MarkerShowEntity();
+ markerShowEntity.setMarkerLocation(markerLocation);
+ markerShowEntity.setMarkerType(V2XConst.V2X_MARKER_SPECIAL_CAR);
+ markerShowEntity.setBindObj(v2XMarkerEntity);
+ V2XServiceManager.getMoGoV2XMarkerManager().drawableMarker(context, markerShowEntity, clickListener);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 自动调整地图的镜头
+ */
+ public static void zoomMap(MogoLatLng latLng, Context context) {
+ try {
+ if (latLng == null) {
+ return;
+ }
+ //Logger.d(V2XConst.MODULE_NAME, "重新调整地图缩放比:" + latLng);
+
+ Rect mBoundRect = new Rect();
+ final int paddingTop;
+ final int paddingBottom;
+ final int paddingRight;
+ final int paddingLeft;
+ if (CarSeries.getSeries() == CarSeries.CAR_SERIES_F80X) {
+ paddingTop = WindowUtils.dip2px(context, 150);
+ paddingBottom = WindowUtils.dip2px(context, 100);
+ paddingRight = WindowUtils.dip2px(context, 100);
+ paddingLeft = WindowUtils.dip2px(context, 475);
+ } else {
+ paddingTop = WindowUtils.dip2px(context, 170);
+ paddingBottom = WindowUtils.dip2px(context, 100);
+ paddingRight = WindowUtils.dip2px(context, 100);
+ paddingLeft = WindowUtils.dip2px(context, 575);
+ }
+
+ mBoundRect.bottom = paddingBottom;
+ mBoundRect.top = paddingTop;
+ mBoundRect.left = paddingLeft;
+ mBoundRect.right = paddingRight;
+
+ // 当前车辆位置
+ MogoLatLng carLocation = V2XServiceManager.getNavi().getCarLocation();
+ // 调整自适应的地图镜头
+ V2XServiceManager.getMapUIController().showBounds(TAG,
+ carLocation,
+ Collections.singletonList(latLng),
+ mBoundRect,
+ true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 设置地图的缩放级别
+ *
+ * @param zoomScale 缩放级别
+ */
+ public static void resetMapZoom(float zoomScale) {
+ V2XServiceManager.getMapUIController().changeZoom(zoomScale);
+ // 锁车就是将地图视图移植中心点,因为行驶中的车和地图要相对的跟随
+ V2XServiceManager.getMapUIController().setLockZoom((int) zoomScale);
+ V2XServiceManager.getMapUIController().recoverLockMode();
+ }
+
+ /**
+ * 根据距离调整地图的缩放比例
+ *
+ * @param distance 距离
+ */
+ public static void changeMapZoomWithDistance(double distance) {
+ Logger.d(V2XConst.MODULE_NAME, "根据距离调整地图的缩放比例:" + distance);
+ if (distance <= 500 && distance > 400) {
+ resetMapZoom(15);
+ } else if (distance <= 400 && distance > 300) {
+ resetMapZoom(16);
+ } else if (distance <= 300 && distance > 200) {
+ resetMapZoom(16.5f);
+ } else if (distance <= 200 && distance > 100) {
+ resetMapZoom(17f);
+ } else if (distance <= 100 && distance >= 0) {
+ resetMapZoom(17.5f);
+ } else if (distance >= 500) {
+ resetMapZoom(12);
+ }
+ }
+
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/RoadConditionUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/RoadConditionUtils.java
new file mode 100644
index 0000000000..4302c9d997
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/RoadConditionUtils.java
@@ -0,0 +1,101 @@
+package com.mogo.module.v2x.utils;
+
+import android.content.Intent;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import com.mogo.commons.voice.AIAssist;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.V2XServiceManager;
+import com.mogo.module.common.entity.V2XPoiTypeEnum;
+import com.mogo.utils.logger.Logger;
+
+import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020/4/16 2:25 PM
+ * desc : 调用探路模块相关的工具
+ * version: 1.0
+ */
+public class RoadConditionUtils {
+ /**
+ * 向探路模块发送消息正确的确认广播
+ *
+ * @param poiType 当前poi信息
+ */
+ public static void sendShareReceiverInfo(String poiType) {
+ if (poiType != null) {
+ switch (poiType) {
+ case V2XPoiTypeEnum.FOURS_BLOCK_UP:
+ sendShareReceiver("1");
+ break;
+ case V2XPoiTypeEnum.TRAFFIC_CHECK:
+ sendShareReceiver("2");
+ break;
+ case V2XPoiTypeEnum.ROAD_CLOSED:
+ sendShareReceiver("3");
+ break;
+ }
+ }
+ }
+
+ /**
+ * 向探路模块发送数据纠错广播
+ *
+ * @param poiType 当前poi信息
+ */
+ public static void sendDataErrorReceiverInfo(String poiType, String infoId, String updateType) {
+ sendDataErrorReceiver(infoId, poiType, updateType);
+ }
+
+
+ /**
+ * 发送广播 type: 1拥堵,2交通检查,3封路
+ */
+ public static void sendShareReceiver(String type) {
+ Intent intent = new Intent();
+ intent.setAction("com.zhidao.roadcondition.share");
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ intent.putExtra("type", type);
+ V2XUtils.getApp().sendBroadcast(intent);
+ Logger.d(MODULE_NAME, "通过广播通知探路正确。。。");
+ }
+
+ /**
+ * 数据错误
+ *
+ * @param id 事件ID
+ * @param poiType 事件类型
+ */
+ public static void sendDataErrorReceiver(String id, String poiType, String updateType) {
+ showTip();
+
+ Intent intent = new Intent();
+ intent.setAction("com.zhidao.tanlu.dataerror");
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ intent.putExtra("id", id);
+ intent.putExtra("type", poiType);
+ intent.putExtra("updateType", updateType);
+ V2XUtils.getApp().sendBroadcast(intent);
+ Logger.d(MODULE_NAME, "通过广播通知探路纠错:id = " + id + " poiType = " + poiType + " updateType = " + updateType);
+ sendShareReceiverInfo(poiType);
+ }
+
+ /**
+ * 显示Toast
+ */
+ private static void showTip() {
+ AIAssist.getInstance(V2XUtils.getApp()).speakTTSVoice("已反馈", null);
+ ToastUtils.setGravity(Gravity.CENTER, 0, 0);
+ View toastView = LayoutInflater.from(V2XServiceManager.getContext()).inflate(R.layout.toast_view, null);
+ TextView msgView = toastView.findViewById(R.id.tvFeedbackContent);
+ msgView.setText("已反馈");
+ ToastUtils.showCustomShort(toastView);
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/SpanUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/SpanUtils.java
new file mode 100644
index 0000000000..2159535918
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/SpanUtils.java
@@ -0,0 +1,1433 @@
+package com.mogo.module.v2x.utils;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+import android.text.Layout;
+import android.text.Layout.Alignment;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextPaint;
+import android.text.method.LinkMovementMethod;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.AlignmentSpan;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.CharacterStyle;
+import android.text.style.ClickableSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.LeadingMarginSpan;
+import android.text.style.LineHeightSpan;
+import android.text.style.MaskFilterSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.ReplacementSpan;
+import android.text.style.ScaleXSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
+import android.text.style.UpdateAppearance;
+import android.util.Log;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntDef;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+
+import com.mogo.utils.logger.Logger;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+
+import static android.graphics.BlurMaskFilter.Blur;
+
+/**
+ *
+ * author: Blankj
+ * blog : http://blankj.com
+ * time : 16/12/13
+ * desc : utils about span
+ *
+ */
+public final class SpanUtils {
+
+ private static final int COLOR_DEFAULT = 0xFEFFFFFF;
+
+ public static final int ALIGN_BOTTOM = 0;
+ public static final int ALIGN_BASELINE = 1;
+ public static final int ALIGN_CENTER = 2;
+ public static final int ALIGN_TOP = 3;
+
+ @IntDef({ALIGN_BOTTOM, ALIGN_BASELINE, ALIGN_CENTER, ALIGN_TOP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Align {
+ }
+
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ private TextView mTextView;
+ private CharSequence mText;
+ private int flag;
+ private int foregroundColor;
+ private int backgroundColor;
+ private int lineHeight;
+ private int alignLine;
+ private int quoteColor;
+ private int stripeWidth;
+ private int quoteGapWidth;
+ private int first;
+ private int rest;
+ private int bulletColor;
+ private int bulletRadius;
+ private int bulletGapWidth;
+ private int fontSize;
+ private boolean fontSizeIsDp;
+ private float proportion;
+ private float xProportion;
+ private boolean isStrikethrough;
+ private boolean isUnderline;
+ private boolean isSuperscript;
+ private boolean isSubscript;
+ private boolean isBold;
+ private boolean isItalic;
+ private boolean isBoldItalic;
+ private String fontFamily;
+ private Typeface typeface;
+ private Alignment alignment;
+ private int verticalAlign;
+ private ClickableSpan clickSpan;
+ private String url;
+ private float blurRadius;
+ private Blur style;
+ private Shader shader;
+ private float shadowRadius;
+ private float shadowDx;
+ private float shadowDy;
+ private int shadowColor;
+ private Object[] spans;
+
+ private Bitmap imageBitmap;
+ private Drawable imageDrawable;
+ private Uri imageUri;
+ private int imageResourceId;
+ private int alignImage;
+
+ private int spaceSize;
+ private int spaceColor;
+
+ private SerializableSpannableStringBuilder mBuilder;
+
+ private int mType;
+ private final int mTypeCharSequence = 0;
+ private final int mTypeImage = 1;
+ private final int mTypeSpace = 2;
+
+ private SpanUtils(TextView textView) {
+ this();
+ mTextView = textView;
+ }
+
+ public SpanUtils() {
+ mBuilder = new SerializableSpannableStringBuilder();
+ mText = "";
+ mType = -1;
+ setDefault();
+ }
+
+ private void setDefault() {
+ flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
+ foregroundColor = COLOR_DEFAULT;
+ backgroundColor = COLOR_DEFAULT;
+ lineHeight = -1;
+ quoteColor = COLOR_DEFAULT;
+ first = -1;
+ bulletColor = COLOR_DEFAULT;
+ fontSize = -1;
+ proportion = -1;
+ xProportion = -1;
+ isStrikethrough = false;
+ isUnderline = false;
+ isSuperscript = false;
+ isSubscript = false;
+ isBold = false;
+ isItalic = false;
+ isBoldItalic = false;
+ fontFamily = null;
+ typeface = null;
+ alignment = null;
+ verticalAlign = -1;
+ clickSpan = null;
+ url = null;
+ blurRadius = -1;
+ shader = null;
+ shadowRadius = -1;
+ spans = null;
+
+ imageBitmap = null;
+ imageDrawable = null;
+ imageUri = null;
+ imageResourceId = -1;
+
+ spaceSize = -1;
+ }
+
+ /**
+ * Set the span of flag.
+ *
+ * @param flag The flag.
+ *
+ * - {@link Spanned#SPAN_INCLUSIVE_EXCLUSIVE}
+ * - {@link Spanned#SPAN_INCLUSIVE_INCLUSIVE}
+ * - {@link Spanned#SPAN_EXCLUSIVE_EXCLUSIVE}
+ * - {@link Spanned#SPAN_EXCLUSIVE_INCLUSIVE}
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setFlag(final int flag) {
+ this.flag = flag;
+ return this;
+ }
+
+ /**
+ * Set the span of foreground's color.
+ *
+ * @param color The color of foreground
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setForegroundColor(@ColorInt final int color) {
+ this.foregroundColor = color;
+ return this;
+ }
+
+ /**
+ * Set the span of background's color.
+ *
+ * @param color The color of background
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setBackgroundColor(@ColorInt final int color) {
+ this.backgroundColor = color;
+ return this;
+ }
+
+ /**
+ * Set the span of line height.
+ *
+ * @param lineHeight The line height, in pixel.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setLineHeight(@IntRange(from = 0) final int lineHeight) {
+ return setLineHeight(lineHeight, ALIGN_CENTER);
+ }
+
+ /**
+ * Set the span of line height.
+ *
+ * @param lineHeight The line height, in pixel.
+ * @param align The alignment.
+ *
+ * - {@link Align#ALIGN_TOP }
+ * - {@link Align#ALIGN_CENTER}
+ * - {@link Align#ALIGN_BOTTOM}
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setLineHeight(@IntRange(from = 0) final int lineHeight,
+ @Align final int align) {
+ this.lineHeight = lineHeight;
+ this.alignLine = align;
+ return this;
+ }
+
+ /**
+ * Set the span of quote's color.
+ *
+ * @param color The color of quote
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setQuoteColor(@ColorInt final int color) {
+ return setQuoteColor(color, 2, 2);
+ }
+
+ /**
+ * Set the span of quote's color.
+ *
+ * @param color The color of quote.
+ * @param stripeWidth The width of stripe, in pixel.
+ * @param gapWidth The width of gap, in pixel.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setQuoteColor(@ColorInt final int color,
+ @IntRange(from = 1) final int stripeWidth,
+ @IntRange(from = 0) final int gapWidth) {
+ this.quoteColor = color;
+ this.stripeWidth = stripeWidth;
+ this.quoteGapWidth = gapWidth;
+ return this;
+ }
+
+ /**
+ * Set the span of leading margin.
+ *
+ * @param first The indent for the first line of the paragraph.
+ * @param rest The indent for the remaining lines of the paragraph.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setLeadingMargin(@IntRange(from = 0) final int first,
+ @IntRange(from = 0) final int rest) {
+ this.first = first;
+ this.rest = rest;
+ return this;
+ }
+
+ /**
+ * Set the span of bullet.
+ *
+ * @param gapWidth The width of gap, in pixel.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setBullet(@IntRange(from = 0) final int gapWidth) {
+ return setBullet(0, 3, gapWidth);
+ }
+
+ /**
+ * Set the span of bullet.
+ *
+ * @param color The color of bullet.
+ * @param radius The radius of bullet, in pixel.
+ * @param gapWidth The width of gap, in pixel.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setBullet(@ColorInt final int color,
+ @IntRange(from = 0) final int radius,
+ @IntRange(from = 0) final int gapWidth) {
+ this.bulletColor = color;
+ this.bulletRadius = radius;
+ this.bulletGapWidth = gapWidth;
+ return this;
+ }
+
+ /**
+ * Set the span of font's size.
+ *
+ * @param size The size of font.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setFontSize(@IntRange(from = 0) final int size) {
+ return setFontSize(size, false);
+ }
+
+ /**
+ * Set the span of size of font.
+ *
+ * @param size The size of font.
+ * @param isSp True to use sp, false to use pixel.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setFontSize(@IntRange(from = 0) final int size, final boolean isSp) {
+ this.fontSize = size;
+ this.fontSizeIsDp = isSp;
+ return this;
+ }
+
+ /**
+ * Set the span of proportion of font.
+ *
+ * @param proportion The proportion of font.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setFontProportion(final float proportion) {
+ this.proportion = proportion;
+ return this;
+ }
+
+ /**
+ * Set the span of transverse proportion of font.
+ *
+ * @param proportion The transverse proportion of font.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setFontXProportion(final float proportion) {
+ this.xProportion = proportion;
+ return this;
+ }
+
+ /**
+ * Set the span of strikethrough.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setStrikethrough() {
+ this.isStrikethrough = true;
+ return this;
+ }
+
+ /**
+ * Set the span of underline.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setUnderline() {
+ this.isUnderline = true;
+ return this;
+ }
+
+ /**
+ * Set the span of superscript.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setSuperscript() {
+ this.isSuperscript = true;
+ return this;
+ }
+
+ /**
+ * Set the span of subscript.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setSubscript() {
+ this.isSubscript = true;
+ return this;
+ }
+
+ /**
+ * Set the span of bold.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setBold() {
+ isBold = true;
+ return this;
+ }
+
+ /**
+ * Set the span of italic.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setItalic() {
+ isItalic = true;
+ return this;
+ }
+
+ /**
+ * Set the span of bold italic.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setBoldItalic() {
+ isBoldItalic = true;
+ return this;
+ }
+
+ /**
+ * Set the span of font family.
+ *
+ * @param fontFamily The font family.
+ *
+ * - monospace
+ * - serif
+ * - sans-serif
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setFontFamily(@NonNull final String fontFamily) {
+ this.fontFamily = fontFamily;
+ return this;
+ }
+
+ /**
+ * Set the span of typeface.
+ *
+ * @param typeface The typeface.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setTypeface(@NonNull final Typeface typeface) {
+ this.typeface = typeface;
+ return this;
+ }
+
+ /**
+ * Set the span of horizontal alignment.
+ *
+ * @param alignment The alignment.
+ *
+ * - {@link Alignment#ALIGN_NORMAL }
+ * - {@link Alignment#ALIGN_OPPOSITE}
+ * - {@link Alignment#ALIGN_CENTER }
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setHorizontalAlign(@NonNull final Alignment alignment) {
+ this.alignment = alignment;
+ return this;
+ }
+
+ /**
+ * Set the span of vertical alignment.
+ *
+ * @param align The alignment.
+ *
+ * - {@link Align#ALIGN_TOP }
+ * - {@link Align#ALIGN_CENTER }
+ * - {@link Align#ALIGN_BASELINE}
+ * - {@link Align#ALIGN_BOTTOM }
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setVerticalAlign(@Align final int align) {
+ this.verticalAlign = align;
+ return this;
+ }
+
+ /**
+ * Set the span of click.
+ * Must set {@code view.setMovementMethod(LinkMovementMethod.getInstance())}
+ *
+ * @param clickSpan The span of click.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setClickSpan(@NonNull final ClickableSpan clickSpan) {
+ if (mTextView != null && mTextView.getMovementMethod() == null) {
+ mTextView.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+ this.clickSpan = clickSpan;
+ return this;
+ }
+
+ /**
+ * Set the span of url.
+ * Must set {@code view.setMovementMethod(LinkMovementMethod.getInstance())}
+ *
+ * @param url The url.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setUrl(@NonNull final String url) {
+ if (mTextView != null && mTextView.getMovementMethod() == null) {
+ mTextView.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * Set the span of blur.
+ *
+ * @param radius The radius of blur.
+ * @param style The style.
+ *
+ * - {@link Blur#NORMAL}
+ * - {@link Blur#SOLID}
+ * - {@link Blur#OUTER}
+ * - {@link Blur#INNER}
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setBlur(@FloatRange(from = 0, fromInclusive = false) final float radius,
+ final Blur style) {
+ this.blurRadius = radius;
+ this.style = style;
+ return this;
+ }
+
+ /**
+ * Set the span of shader.
+ *
+ * @param shader The shader.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setShader(@NonNull final Shader shader) {
+ this.shader = shader;
+ return this;
+ }
+
+ /**
+ * Set the span of shadow.
+ *
+ * @param radius The radius of shadow.
+ * @param dx X-axis offset, in pixel.
+ * @param dy Y-axis offset, in pixel.
+ * @param shadowColor The color of shadow.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setShadow(@FloatRange(from = 0, fromInclusive = false) final float radius,
+ final float dx,
+ final float dy,
+ final int shadowColor) {
+ this.shadowRadius = radius;
+ this.shadowDx = dx;
+ this.shadowDy = dy;
+ this.shadowColor = shadowColor;
+ return this;
+ }
+
+
+ /**
+ * Set the spans.
+ *
+ * @param spans The spans.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils setSpans(@NonNull final Object... spans) {
+ if (spans.length > 0) {
+ this.spans = spans;
+ }
+ return this;
+ }
+
+ /**
+ * Append the text text.
+ *
+ * @param text The text.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils append(@NonNull final CharSequence text) {
+ apply(mTypeCharSequence);
+ mText = text;
+ return this;
+ }
+
+ /**
+ * Append one line.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendLine() {
+ apply(mTypeCharSequence);
+ mText = LINE_SEPARATOR;
+ return this;
+ }
+
+ /**
+ * Append text and one line.
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendLine(@NonNull final CharSequence text) {
+ apply(mTypeCharSequence);
+ mText = text + LINE_SEPARATOR;
+ return this;
+ }
+
+ /**
+ * Append one image.
+ *
+ * @param bitmap The bitmap of image.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendImage(@NonNull final Bitmap bitmap) {
+ return appendImage(bitmap, ALIGN_BOTTOM);
+ }
+
+ /**
+ * Append one image.
+ *
+ * @param bitmap The bitmap.
+ * @param align The alignment.
+ *
+ * - {@link Align#ALIGN_TOP }
+ * - {@link Align#ALIGN_CENTER }
+ * - {@link Align#ALIGN_BASELINE}
+ * - {@link Align#ALIGN_BOTTOM }
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendImage(@NonNull final Bitmap bitmap, @Align final int align) {
+ apply(mTypeImage);
+ this.imageBitmap = bitmap;
+ this.alignImage = align;
+ return this;
+ }
+
+ /**
+ * Append one image.
+ *
+ * @param drawable The drawable of image.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendImage(@NonNull final Drawable drawable) {
+ return appendImage(drawable, ALIGN_BOTTOM);
+ }
+
+ /**
+ * Append one image.
+ *
+ * @param drawable The drawable of image.
+ * @param align The alignment.
+ *
+ * - {@link Align#ALIGN_TOP }
+ * - {@link Align#ALIGN_CENTER }
+ * - {@link Align#ALIGN_BASELINE}
+ * - {@link Align#ALIGN_BOTTOM }
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendImage(@NonNull final Drawable drawable, @Align final int align) {
+ apply(mTypeImage);
+ this.imageDrawable = drawable;
+ this.alignImage = align;
+ return this;
+ }
+
+ /**
+ * Append one image.
+ *
+ * @param uri The uri of image.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendImage(@NonNull final Uri uri) {
+ return appendImage(uri, ALIGN_BOTTOM);
+ }
+
+ /**
+ * Append one image.
+ *
+ * @param uri The uri of image.
+ * @param align The alignment.
+ *
+ * - {@link Align#ALIGN_TOP }
+ * - {@link Align#ALIGN_CENTER }
+ * - {@link Align#ALIGN_BASELINE}
+ * - {@link Align#ALIGN_BOTTOM }
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendImage(@NonNull final Uri uri, @Align final int align) {
+ apply(mTypeImage);
+ this.imageUri = uri;
+ this.alignImage = align;
+ return this;
+ }
+
+ /**
+ * Append one image.
+ *
+ * @param resourceId The resource id of image.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendImage(@DrawableRes final int resourceId) {
+ return appendImage(resourceId, ALIGN_BOTTOM);
+ }
+
+ /**
+ * Append one image.
+ *
+ * @param resourceId The resource id of image.
+ * @param align The alignment.
+ *
+ * - {@link Align#ALIGN_TOP }
+ * - {@link Align#ALIGN_CENTER }
+ * - {@link Align#ALIGN_BASELINE}
+ * - {@link Align#ALIGN_BOTTOM }
+ *
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendImage(@DrawableRes final int resourceId, @Align final int align) {
+ apply(mTypeImage);
+ this.imageResourceId = resourceId;
+ this.alignImage = align;
+ return this;
+ }
+
+ /**
+ * Append space.
+ *
+ * @param size The size of space.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendSpace(@IntRange(from = 0) final int size) {
+ return appendSpace(size, Color.TRANSPARENT);
+ }
+
+ /**
+ * Append space.
+ *
+ * @param size The size of space.
+ * @param color The color of space.
+ * @return the single {@link SpanUtils} instance
+ */
+ public SpanUtils appendSpace(@IntRange(from = 0) final int size, @ColorInt final int color) {
+ apply(mTypeSpace);
+ spaceSize = size;
+ spaceColor = color;
+ return this;
+ }
+
+ private void apply(final int type) {
+ applyLast();
+ mType = type;
+ }
+
+ public SpannableStringBuilder get() {
+ return mBuilder;
+ }
+
+ /**
+ * Create the span string.
+ *
+ * @return the span string
+ */
+ public SpannableStringBuilder create() {
+ applyLast();
+ if (mTextView != null) {
+ mTextView.setText(mBuilder);
+ }
+ return mBuilder;
+ }
+
+ private void applyLast() {
+ if (mType == mTypeCharSequence) {
+ updateCharCharSequence();
+ } else if (mType == mTypeImage) {
+ updateImage();
+ } else if (mType == mTypeSpace) {
+ updateSpace();
+ }
+ setDefault();
+ }
+
+ private void updateCharCharSequence() {
+ if (mText.length() == 0) {
+ return;
+ }
+ int start = mBuilder.length();
+ if (start == 0 && lineHeight != -1) {// bug of LineHeightSpan when first line
+ mBuilder.append(Character.toString((char) 2))
+ .append("\n")
+ .setSpan(new AbsoluteSizeSpan(0), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ start = 2;
+ }
+ mBuilder.append(mText);
+ int end = mBuilder.length();
+ if (verticalAlign != -1) {
+ mBuilder.setSpan(new VerticalAlignSpan(verticalAlign), start, end, flag);
+ }
+ if (foregroundColor != COLOR_DEFAULT) {
+ mBuilder.setSpan(new ForegroundColorSpan(foregroundColor), start, end, flag);
+ }
+ if (backgroundColor != COLOR_DEFAULT) {
+ mBuilder.setSpan(new BackgroundColorSpan(backgroundColor), start, end, flag);
+ }
+ if (first != -1) {
+ mBuilder.setSpan(new LeadingMarginSpan.Standard(first, rest), start, end, flag);
+ }
+ if (quoteColor != COLOR_DEFAULT) {
+ mBuilder.setSpan(
+ new CustomQuoteSpan(quoteColor, stripeWidth, quoteGapWidth),
+ start,
+ end,
+ flag
+ );
+ }
+ if (bulletColor != COLOR_DEFAULT) {
+ mBuilder.setSpan(
+ new CustomBulletSpan(bulletColor, bulletRadius, bulletGapWidth),
+ start,
+ end,
+ flag
+ );
+ }
+ if (fontSize != -1) {
+ mBuilder.setSpan(new AbsoluteSizeSpan(fontSize, fontSizeIsDp), start, end, flag);
+ }
+ if (proportion != -1) {
+ mBuilder.setSpan(new RelativeSizeSpan(proportion), start, end, flag);
+ }
+ if (xProportion != -1) {
+ mBuilder.setSpan(new ScaleXSpan(xProportion), start, end, flag);
+ }
+ if (lineHeight != -1) {
+ mBuilder.setSpan(new CustomLineHeightSpan(lineHeight, alignLine), start, end, flag);
+ }
+ if (isStrikethrough) {
+ mBuilder.setSpan(new StrikethroughSpan(), start, end, flag);
+ }
+ if (isUnderline) {
+ mBuilder.setSpan(new UnderlineSpan(), start, end, flag);
+ }
+ if (isSuperscript) {
+ mBuilder.setSpan(new SuperscriptSpan(), start, end, flag);
+ }
+ if (isSubscript) {
+ mBuilder.setSpan(new SubscriptSpan(), start, end, flag);
+ }
+ if (isBold) {
+ mBuilder.setSpan(new StyleSpan(Typeface.BOLD), start, end, flag);
+ }
+ if (isItalic) {
+ mBuilder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flag);
+ }
+ if (isBoldItalic) {
+ mBuilder.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, flag);
+ }
+ if (fontFamily != null) {
+ mBuilder.setSpan(new TypefaceSpan(fontFamily), start, end, flag);
+ }
+ if (typeface != null) {
+ mBuilder.setSpan(new CustomTypefaceSpan(typeface), start, end, flag);
+ }
+ if (alignment != null) {
+ mBuilder.setSpan(new AlignmentSpan.Standard(alignment), start, end, flag);
+ }
+ if (clickSpan != null) {
+ mBuilder.setSpan(clickSpan, start, end, flag);
+ }
+ if (url != null) {
+ mBuilder.setSpan(new URLSpan(url), start, end, flag);
+ }
+ if (blurRadius != -1) {
+ mBuilder.setSpan(
+ new MaskFilterSpan(new BlurMaskFilter(blurRadius, style)),
+ start,
+ end,
+ flag
+ );
+ }
+ if (shader != null) {
+ mBuilder.setSpan(new ShaderSpan(shader), start, end, flag);
+ }
+ if (shadowRadius != -1) {
+ mBuilder.setSpan(
+ new ShadowSpan(shadowRadius, shadowDx, shadowDy, shadowColor),
+ start,
+ end,
+ flag
+ );
+ }
+ if (spans != null) {
+ for (Object span : spans) {
+ mBuilder.setSpan(span, start, end, flag);
+ }
+ }
+ }
+
+ private void updateImage() {
+ int start = mBuilder.length();
+ mText = "
";
+ updateCharCharSequence();
+ int end = mBuilder.length();
+ if (imageBitmap != null) {
+ mBuilder.setSpan(new CustomImageSpan(imageBitmap, alignImage), start, end, flag);
+ } else if (imageDrawable != null) {
+ mBuilder.setSpan(new CustomImageSpan(imageDrawable, alignImage), start, end, flag);
+ } else if (imageUri != null) {
+ mBuilder.setSpan(new CustomImageSpan(imageUri, alignImage), start, end, flag);
+ } else if (imageResourceId != -1) {
+ mBuilder.setSpan(new CustomImageSpan(imageResourceId, alignImage), start, end, flag);
+ }
+ }
+
+ private void updateSpace() {
+ int start = mBuilder.length();
+ mText = "< >";
+ updateCharCharSequence();
+ int end = mBuilder.length();
+ mBuilder.setSpan(new SpaceSpan(spaceSize, spaceColor), start, end, flag);
+ }
+
+ static class VerticalAlignSpan extends ReplacementSpan {
+
+ static final int ALIGN_CENTER = 2;
+ static final int ALIGN_TOP = 3;
+
+ final int mVerticalAlignment;
+
+ VerticalAlignSpan(int verticalAlignment) {
+ mVerticalAlignment = verticalAlignment;
+ }
+
+ @Override
+ public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {
+ text = text.subSequence(start, end);
+ return (int) paint.measureText(text.toString());
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
+ text = text.subSequence(start, end);
+ Paint.FontMetricsInt fm = paint.getFontMetricsInt();
+// int need = height - (v + fm.descent - fm.ascent - spanstartv);
+// if (need > 0) {
+// if (mVerticalAlignment == ALIGN_TOP) {
+// fm.descent += need;
+// } else if (mVerticalAlignment == ALIGN_CENTER) {
+// fm.descent += need / 2;
+// fm.ascent -= need / 2;
+// } else {
+// fm.ascent -= need;
+// }
+// }
+// need = height - (v + fm.bottom - fm.top - spanstartv);
+// if (need > 0) {
+// if (mVerticalAlignment == ALIGN_TOP) {
+// fm.bottom += need;
+// } else if (mVerticalAlignment == ALIGN_CENTER) {
+// fm.bottom += need / 2;
+// fm.top -= need / 2;
+// } else {
+// fm.top -= need;
+// }
+// }
+
+ canvas.drawText(text.toString(), x, y - ((y + fm.descent + y + fm.ascent) / 2 - (bottom + top) / 2), paint);
+ }
+ }
+
+ static class CustomLineHeightSpan implements LineHeightSpan {
+
+ private final int height;
+
+ static final int ALIGN_CENTER = 2;
+ static final int ALIGN_TOP = 3;
+
+ final int mVerticalAlignment;
+ static Paint.FontMetricsInt sfm;
+
+ CustomLineHeightSpan(int height, int verticalAlignment) {
+ this.height = height;
+ mVerticalAlignment = verticalAlignment;
+ }
+
+ @Override
+ public void chooseHeight(final CharSequence text, final int start, final int end,
+ final int spanstartv, final int v, final Paint.FontMetricsInt fm) {
+ if (sfm == null) {
+ sfm = new Paint.FontMetricsInt();
+ sfm.top = fm.top;
+ sfm.ascent = fm.ascent;
+ sfm.descent = fm.descent;
+ sfm.bottom = fm.bottom;
+ sfm.leading = fm.leading;
+ } else {
+ fm.top = sfm.top;
+ fm.ascent = sfm.ascent;
+ fm.descent = sfm.descent;
+ fm.bottom = sfm.bottom;
+ fm.leading = sfm.leading;
+ }
+ int need = height - (v + fm.descent - fm.ascent - spanstartv);
+ if (need > 0) {
+ if (mVerticalAlignment == ALIGN_TOP) {
+ fm.descent += need;
+ } else if (mVerticalAlignment == ALIGN_CENTER) {
+ fm.descent += need / 2;
+ fm.ascent -= need / 2;
+ } else {
+ fm.ascent -= need;
+ }
+ }
+ need = height - (v + fm.bottom - fm.top - spanstartv);
+ if (need > 0) {
+ if (mVerticalAlignment == ALIGN_TOP) {
+ fm.bottom += need;
+ } else if (mVerticalAlignment == ALIGN_CENTER) {
+ fm.bottom += need / 2;
+ fm.top -= need / 2;
+ } else {
+ fm.top -= need;
+ }
+ }
+ if (end == ((Spanned) text).getSpanEnd(this)) {
+ sfm = null;
+ }
+ }
+ }
+
+ static class SpaceSpan extends ReplacementSpan {
+
+ private final int width;
+ private final Paint paint = new Paint();
+
+ private SpaceSpan(final int width) {
+ this(width, Color.TRANSPARENT);
+ }
+
+ private SpaceSpan(final int width, final int color) {
+ super();
+ this.width = width;
+ paint.setColor(color);
+ paint.setStyle(Paint.Style.FILL);
+ }
+
+ @Override
+ public int getSize(@NonNull final Paint paint, final CharSequence text,
+ @IntRange(from = 0) final int start,
+ @IntRange(from = 0) final int end,
+ @Nullable final Paint.FontMetricsInt fm) {
+ return width;
+ }
+
+ @Override
+ public void draw(@NonNull final Canvas canvas, final CharSequence text,
+ @IntRange(from = 0) final int start,
+ @IntRange(from = 0) final int end,
+ final float x, final int top, final int y, final int bottom,
+ @NonNull final Paint paint) {
+ canvas.drawRect(x, top, x + width, bottom, this.paint);
+ }
+ }
+
+ static class CustomQuoteSpan implements LeadingMarginSpan {
+
+ private final int color;
+ private final int stripeWidth;
+ private final int gapWidth;
+
+ private CustomQuoteSpan(final int color, final int stripeWidth, final int gapWidth) {
+ super();
+ this.color = color;
+ this.stripeWidth = stripeWidth;
+ this.gapWidth = gapWidth;
+ }
+
+ @Override
+ public int getLeadingMargin(final boolean first) {
+ return stripeWidth + gapWidth;
+ }
+
+ @Override
+ public void drawLeadingMargin(final Canvas c, final Paint p, final int x, final int dir,
+ final int top, final int baseline, final int bottom,
+ final CharSequence text, final int start, final int end,
+ final boolean first, final Layout layout) {
+ Paint.Style style = p.getStyle();
+ int color = p.getColor();
+
+ p.setStyle(Paint.Style.FILL);
+ p.setColor(this.color);
+
+ c.drawRect(x, top, x + dir * stripeWidth, bottom, p);
+
+ p.setStyle(style);
+ p.setColor(color);
+ }
+ }
+
+ static class CustomBulletSpan implements LeadingMarginSpan {
+
+ private final int color;
+ private final int radius;
+ private final int gapWidth;
+
+ private Path sBulletPath = null;
+
+ private CustomBulletSpan(final int color, final int radius, final int gapWidth) {
+ this.color = color;
+ this.radius = radius;
+ this.gapWidth = gapWidth;
+ }
+
+ @Override
+ public int getLeadingMargin(final boolean first) {
+ return 2 * radius + gapWidth;
+ }
+
+ @Override
+ public void drawLeadingMargin(final Canvas c, final Paint p, final int x, final int dir,
+ final int top, final int baseline, final int bottom,
+ final CharSequence text, final int start, final int end,
+ final boolean first, final Layout l) {
+ if (((Spanned) text).getSpanStart(this) == start) {
+ Paint.Style style = p.getStyle();
+ int oldColor = 0;
+ oldColor = p.getColor();
+ p.setColor(color);
+ p.setStyle(Paint.Style.FILL);
+ if (c.isHardwareAccelerated()) {
+ if (sBulletPath == null) {
+ sBulletPath = new Path();
+ // Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
+ sBulletPath.addCircle(0.0f, 0.0f, radius, Path.Direction.CW);
+ }
+ c.save();
+ c.translate(x + dir * radius, (top + bottom) / 2.0f);
+ c.drawPath(sBulletPath, p);
+ c.restore();
+ } else {
+ c.drawCircle(x + dir * radius, (top + bottom) / 2.0f, radius, p);
+ }
+ p.setColor(oldColor);
+ p.setStyle(style);
+ }
+ }
+ }
+
+ @SuppressLint("ParcelCreator")
+ static class CustomTypefaceSpan extends TypefaceSpan {
+
+ private final Typeface newType;
+
+ private CustomTypefaceSpan(final Typeface type) {
+ super("");
+ newType = type;
+ }
+
+ @Override
+ public void updateDrawState(final TextPaint textPaint) {
+ apply(textPaint, newType);
+ }
+
+ @Override
+ public void updateMeasureState(final TextPaint paint) {
+ apply(paint, newType);
+ }
+
+ private void apply(final Paint paint, final Typeface tf) {
+ int oldStyle;
+ Typeface old = paint.getTypeface();
+ if (old == null) {
+ oldStyle = 0;
+ } else {
+ oldStyle = old.getStyle();
+ }
+
+ int fake = oldStyle & ~tf.getStyle();
+ if ((fake & Typeface.BOLD) != 0) {
+ paint.setFakeBoldText(true);
+ }
+
+ if ((fake & Typeface.ITALIC) != 0) {
+ paint.setTextSkewX(-0.25f);
+ }
+
+ paint.getShader();
+
+ paint.setTypeface(tf);
+ }
+ }
+
+ static class CustomImageSpan extends CustomDynamicDrawableSpan {
+ private Drawable mDrawable;
+ private Uri mContentUri;
+ private int mResourceId;
+
+ private CustomImageSpan(final Bitmap b, final int verticalAlignment) {
+ super(verticalAlignment);
+ mDrawable = new BitmapDrawable(V2XUtils.getApp().getResources(), b);
+ mDrawable.setBounds(
+ 0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()
+ );
+ }
+
+ private CustomImageSpan(final Drawable d, final int verticalAlignment) {
+ super(verticalAlignment);
+ mDrawable = d;
+ mDrawable.setBounds(
+ 0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()
+ );
+ }
+
+ private CustomImageSpan(final Uri uri, final int verticalAlignment) {
+ super(verticalAlignment);
+ mContentUri = uri;
+ }
+
+ private CustomImageSpan(@DrawableRes final int resourceId, final int verticalAlignment) {
+ super(verticalAlignment);
+ mResourceId = resourceId;
+ }
+
+ @Override
+ public Drawable getDrawable() {
+ Drawable drawable = null;
+ if (mDrawable != null) {
+ drawable = mDrawable;
+ } else if (mContentUri != null) {
+ Bitmap bitmap;
+ try {
+ InputStream is =
+ V2XUtils.getApp().getContentResolver().openInputStream(mContentUri);
+ bitmap = BitmapFactory.decodeStream(is);
+ drawable = new BitmapDrawable(V2XUtils.getApp().getResources(), bitmap);
+ drawable.setBounds(
+ 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()
+ );
+ if (is != null) {
+ is.close();
+ }
+ } catch (Exception e) {
+ Log.e("sms", "Failed to loaded content " + mContentUri, e);
+ }
+ } else {
+ try {
+ drawable = ContextCompat.getDrawable(V2XUtils.getApp(), mResourceId);
+ drawable.setBounds(
+ 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()
+ );
+ } catch (Exception e) {
+ Log.e("sms", "Unable to find resource: " + mResourceId);
+ }
+ }
+ return drawable;
+ }
+ }
+
+ static abstract class CustomDynamicDrawableSpan extends ReplacementSpan {
+
+ static final int ALIGN_BOTTOM = 0;
+
+ static final int ALIGN_BASELINE = 1;
+
+ static final int ALIGN_CENTER = 2;
+
+ static final int ALIGN_TOP = 3;
+
+ final int mVerticalAlignment;
+
+ private CustomDynamicDrawableSpan() {
+ mVerticalAlignment = ALIGN_BOTTOM;
+ }
+
+ private CustomDynamicDrawableSpan(final int verticalAlignment) {
+ mVerticalAlignment = verticalAlignment;
+ }
+
+ public abstract Drawable getDrawable();
+
+ @Override
+ public int getSize(@NonNull final Paint paint, final CharSequence text,
+ final int start, final int end, final Paint.FontMetricsInt fm) {
+ Drawable d = getCachedDrawable();
+ Rect rect = d.getBounds();
+ if (fm != null) {
+// LogUtils.d("fm.top: " + fm.top,
+// "fm.ascent: " + fm.ascent,
+// "fm.descent: " + fm.descent,
+// "fm.bottom: " + fm.bottom,
+// "lineHeight: " + (fm.bottom - fm.top));
+ int lineHeight = fm.bottom - fm.top;
+ if (lineHeight < rect.height()) {
+ if (mVerticalAlignment == ALIGN_TOP) {
+ fm.top = fm.top;
+ fm.bottom = rect.height() + fm.top;
+ } else if (mVerticalAlignment == ALIGN_CENTER) {
+ fm.top = -rect.height() / 2 - lineHeight / 4;
+ fm.bottom = rect.height() / 2 - lineHeight / 4;
+ } else {
+ fm.top = -rect.height() + fm.bottom;
+ fm.bottom = fm.bottom;
+ }
+ fm.ascent = fm.top;
+ fm.descent = fm.bottom;
+ }
+ }
+ return rect.right;
+ }
+
+ @Override
+ public void draw(@NonNull final Canvas canvas, final CharSequence text,
+ final int start, final int end, final float x,
+ final int top, final int y, final int bottom, @NonNull final Paint paint) {
+ Drawable d = getCachedDrawable();
+ Rect rect = d.getBounds();
+ canvas.save();
+ float transY;
+ int lineHeight = bottom - top;
+// LogUtils.d("rectHeight: " + rect.height(),
+// "lineHeight: " + (bottom - top));
+ if (rect.height() < lineHeight) {
+ if (mVerticalAlignment == ALIGN_TOP) {
+ transY = top;
+ } else if (mVerticalAlignment == ALIGN_CENTER) {
+ transY = (bottom + top - rect.height()) / 2;
+ } else if (mVerticalAlignment == ALIGN_BASELINE) {
+ transY = y - rect.height();
+ } else {
+ transY = bottom - rect.height();
+ }
+ canvas.translate(x, transY);
+ } else {
+ canvas.translate(x, top);
+ }
+ d.draw(canvas);
+ canvas.restore();
+ }
+
+ private Drawable getCachedDrawable() {
+ WeakReference wr = mDrawableRef;
+ Drawable d = null;
+ if (wr != null) {
+ d = wr.get();
+ }
+ if (d == null) {
+ d = getDrawable();
+ mDrawableRef = new WeakReference<>(d);
+ }
+ return d;
+ }
+
+ private WeakReference mDrawableRef;
+ }
+
+ static class ShaderSpan extends CharacterStyle implements UpdateAppearance {
+ private Shader mShader;
+
+ private ShaderSpan(final Shader shader) {
+ this.mShader = shader;
+ }
+
+ @Override
+ public void updateDrawState(final TextPaint tp) {
+ tp.setShader(mShader);
+ }
+ }
+
+ static class ShadowSpan extends CharacterStyle implements UpdateAppearance {
+ private float radius;
+ private float dx, dy;
+ private int shadowColor;
+
+ private ShadowSpan(final float radius,
+ final float dx,
+ final float dy,
+ final int shadowColor) {
+ this.radius = radius;
+ this.dx = dx;
+ this.dy = dy;
+ this.shadowColor = shadowColor;
+ }
+
+ @Override
+ public void updateDrawState(final TextPaint tp) {
+ tp.setShadowLayer(radius, dx, dy, shadowColor);
+ }
+ }
+
+ private static class SerializableSpannableStringBuilder extends SpannableStringBuilder
+ implements Serializable {
+
+ private static final long serialVersionUID = 4909567650765875771L;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // static
+ ///////////////////////////////////////////////////////////////////////////
+
+ public static SpanUtils with(final TextView textView) {
+ return new SpanUtils(textView);
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/TestOnLineCarUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/TestOnLineCarUtils.java
new file mode 100644
index 0000000000..75f5490c78
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/TestOnLineCarUtils.java
@@ -0,0 +1,225 @@
+package com.mogo.module.v2x.utils;
+
+import com.mogo.module.common.entity.MarkerExploreWay;
+import com.mogo.module.common.entity.MarkerResponse;
+import com.mogo.module.v2x.R;
+import com.mogo.module.v2x.entity.net.V2XSpecialCarRes;
+import com.mogo.module.common.entity.V2XMessageEntity;
+import com.mogo.module.common.entity.V2XPushMessageEntity;
+import com.mogo.module.common.entity.V2XRoadEventEntity;
+import com.mogo.utils.network.utils.GsonUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * author : donghongyu
+ * e-mail : 1358506549@qq.com
+ * date : 2020-01-0918:20
+ * desc : 生成测试数据
+ * version: 1.0
+ */
+public class TestOnLineCarUtils {
+
+
+ /**
+ * 获取测试的违章停车数据
+ */
+ public static V2XMessageEntity> getV2XIllegalParkData() {
+ try {
+ InputStream inputStream = V2XUtils.getApp().getResources().openRawResource(R.raw.illegal_park_data);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len = -1;
+ byte[] buffer = new byte[1024];
+ while ((len = inputStream.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+ inputStream.close();
+
+ // 加载数据源
+ MarkerResponse markerResponse = GsonUtil.objectFromJson(baos.toString(), MarkerResponse.class);
+
+ V2XMessageEntity> v2xMessageEntity = new V2XMessageEntity<>();
+ // 控制类型
+ v2xMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_ILLEGAL_PARK_WARNING);
+ // 设置数据
+ v2xMessageEntity.setContent(markerResponse.getResult().getExploreWay());
+ // 控制展示状态
+ v2xMessageEntity.setShowState(true);
+ return v2xMessageEntity;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ /**
+ * 模拟道路事件测试数据
+ */
+ public static V2XMessageEntity getV2XScenarioRoadEventData() {
+ try {
+ InputStream inputStream = V2XUtils.getApp()
+ .getResources()
+ .openRawResource(R.raw.scenario_road_event_data);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len = -1;
+ byte[] buffer = new byte[1024];
+ while ((len = inputStream.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+ inputStream.close();
+
+ // 加载数据源
+ V2XRoadEventEntity v2xRoadEventEntity = GsonUtil.objectFromJson(baos.toString(), V2XRoadEventEntity.class);
+
+ V2XMessageEntity v2xMessageEntity = new V2XMessageEntity<>();
+ // 控制类型
+ v2xMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING);
+ // 设置数据
+ v2xMessageEntity.setContent(v2xRoadEventEntity);
+ // 控制展示状态
+ v2xMessageEntity.setShowState(true);
+ return v2xMessageEntity;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 模拟H5推送数据
+ */
+ public static V2XMessageEntity getV2XScenarioPushEventData() {
+ try {
+ InputStream inputStream = V2XUtils.getApp()
+ .getResources()
+ .openRawResource(R.raw.scenario_push_event_data);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len = -1;
+ byte[] buffer = new byte[1024];
+ while ((len = inputStream.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+ inputStream.close();
+
+ // 加载数据源
+ V2XPushMessageEntity v2xRoadEventEntity = GsonUtil.objectFromJson(baos.toString(), V2XPushMessageEntity.class);
+
+ V2XMessageEntity v2xMessageEntity = new V2XMessageEntity<>();
+ // 控制类型
+ v2xMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_PUSH_WINDOW_WARNING);
+ // 设置数据
+ v2xMessageEntity.setContent(v2xRoadEventEntity);
+ // 控制展示状态
+ v2xMessageEntity.setShowState(true);
+ return v2xMessageEntity;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 模拟H5推送场景动画数据
+ */
+ public static V2XMessageEntity getV2XScenarioAnimationEventData() {
+ try {
+ InputStream inputStream = V2XUtils.getApp()
+ .getResources()
+ .openRawResource(R.raw.scenario_push_animation_event_data);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len = -1;
+ byte[] buffer = new byte[1024];
+ while ((len = inputStream.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+ inputStream.close();
+
+ // 加载数据源
+ V2XPushMessageEntity v2xRoadEventEntity = GsonUtil.objectFromJson(baos.toString(), V2XPushMessageEntity.class);
+
+ V2XMessageEntity v2xMessageEntity = new V2XMessageEntity<>();
+ // 控制类型
+ v2xMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_ANIMATION_WARNING);
+ // 设置数据
+ v2xMessageEntity.setContent(v2xRoadEventEntity);
+ // 控制展示状态
+ v2xMessageEntity.setShowState(true);
+ return v2xMessageEntity;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 模拟 疲劳驾驶
+ */
+ public static V2XMessageEntity getV2XScenarioFatigueDrivingData() {
+ try {
+ InputStream inputStream = V2XUtils.getApp()
+ .getResources()
+ .openRawResource(R.raw.scenario_fatigue_driving_data);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len = -1;
+ byte[] buffer = new byte[1024];
+ while ((len = inputStream.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+ inputStream.close();
+
+ // 加载数据源
+ V2XPushMessageEntity v2xRoadEventEntity =
+ GsonUtil.objectFromJson(baos.toString(), V2XPushMessageEntity.class);
+
+ V2XMessageEntity v2xMessageEntity = new V2XMessageEntity<>();
+ // 控制类型
+ v2xMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_FATIGUE_DRIVING);
+ // 设置数据
+ v2xMessageEntity.setContent(v2xRoadEventEntity);
+ // 控制展示状态
+ v2xMessageEntity.setShowState(true);
+ return v2xMessageEntity;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 模拟 疲劳驾驶
+ */
+ public static V2XMessageEntity> getV2XScenarioSeekHelpData() {
+ try {
+ InputStream inputStream = V2XUtils.getApp()
+ .getResources()
+ .openRawResource(R.raw.scenario_seek_help);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int len = -1;
+ byte[] buffer = new byte[1024];
+ while ((len = inputStream.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+ inputStream.close();
+
+ // 加载数据源
+ V2XSpecialCarRes v2xRoadEventEntity =
+ GsonUtil.objectFromJson(baos.toString(), V2XSpecialCarRes.class);
+
+ V2XMessageEntity> v2xMessageEntity = new V2XMessageEntity<>();
+ // 控制类型
+ v2xMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_SEEK_WARNING);
+ // 设置数据
+ v2xMessageEntity.setContent(v2xRoadEventEntity.getCoordinates());
+ // 控制展示状态
+ v2xMessageEntity.setShowState(true);
+ return v2xMessageEntity;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ThreadUtils.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ThreadUtils.java
new file mode 100644
index 0000000000..18ea02e1c8
--- /dev/null
+++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/utils/ThreadUtils.java
@@ -0,0 +1,1338 @@
+package com.mogo.module.v2x.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ *
+ */
+public final class ThreadUtils {
+
+ private static final Map> TYPE_PRIORITY_POOLS = new HashMap<>();
+
+ private static final Map TASK_TASKINFO_MAP = new ConcurrentHashMap<>();
+
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ private static final Timer TIMER = new Timer();
+
+ private static final byte TYPE_SINGLE = -1;
+ private static final byte TYPE_CACHED = -2;
+ private static final byte TYPE_IO = -4;
+ private static final byte TYPE_CPU = -8;
+
+ private static Executor sDeliver;
+
+ /**
+ * Return whether the thread is the main thread.
+ *
+ * @return {@code true}: yes
{@code false}: no
+ */
+ public static boolean isMainThread() {
+ return Looper.myLooper() == Looper.getMainLooper();
+ }
+
+ public static void runOnUiThread(final Runnable runnable) {
+ V2XUtils.runOnUiThread(runnable);
+ }
+
+ public static void runOnUiThreadDelayed(final Runnable runnable, long delayMillis) {
+ V2XUtils.runOnUiThreadDelayed(runnable, delayMillis);
+ }
+
+ /**
+ * Return a thread pool that reuses a fixed number of threads
+ * operating off a shared unbounded queue, using the provided
+ * ThreadFactory to create new threads when needed.
+ *
+ * @param size The size of thread in the pool.
+ * @return a fixed thread pool
+ */
+ public static ExecutorService getFixedPool(@IntRange(from = 1) final int size) {
+ return getPoolByTypeAndPriority(size);
+ }
+
+ /**
+ * Return a thread pool that reuses a fixed number of threads
+ * operating off a shared unbounded queue, using the provided
+ * ThreadFactory to create new threads when needed.
+ *
+ * @param size The size of thread in the pool.
+ * @param priority The priority of thread in the poll.
+ * @return a fixed thread pool
+ */
+ public static ExecutorService getFixedPool(@IntRange(from = 1) final int size,
+ @IntRange(from = 1, to = 10) final int priority) {
+ return getPoolByTypeAndPriority(size, priority);
+ }
+
+ /**
+ * Return a thread pool that uses a single worker thread operating
+ * off an unbounded queue, and uses the provided ThreadFactory to
+ * create a new thread when needed.
+ *
+ * @return a single thread pool
+ */
+ public static ExecutorService getSinglePool() {
+ return getPoolByTypeAndPriority(TYPE_SINGLE);
+ }
+
+ /**
+ * Return a thread pool that uses a single worker thread operating
+ * off an unbounded queue, and uses the provided ThreadFactory to
+ * create a new thread when needed.
+ *
+ * @param priority The priority of thread in the poll.
+ * @return a single thread pool
+ */
+ public static ExecutorService getSinglePool(@IntRange(from = 1, to = 10) final int priority) {
+ return getPoolByTypeAndPriority(TYPE_SINGLE, priority);
+ }
+
+ /**
+ * Return a thread pool that creates new threads as needed, but
+ * will reuse previously constructed threads when they are
+ * available.
+ *
+ * @return a cached thread pool
+ */
+ public static ExecutorService getCachedPool() {
+ return getPoolByTypeAndPriority(TYPE_CACHED);
+ }
+
+ /**
+ * Return a thread pool that creates new threads as needed, but
+ * will reuse previously constructed threads when they are
+ * available.
+ *
+ * @param priority The priority of thread in the poll.
+ * @return a cached thread pool
+ */
+ public static ExecutorService getCachedPool(@IntRange(from = 1, to = 10) final int priority) {
+ return getPoolByTypeAndPriority(TYPE_CACHED, priority);
+ }
+
+ /**
+ * Return a thread pool that creates (2 * CPU_COUNT + 1) threads
+ * operating off a queue which size is 128.
+ *
+ * @return a IO thread pool
+ */
+ public static ExecutorService getIoPool() {
+ return getPoolByTypeAndPriority(TYPE_IO);
+ }
+
+ /**
+ * Return a thread pool that creates (2 * CPU_COUNT + 1) threads
+ * operating off a queue which size is 128.
+ *
+ * @param priority The priority of thread in the poll.
+ * @return a IO thread pool
+ */
+ public static ExecutorService getIoPool(@IntRange(from = 1, to = 10) final int priority) {
+ return getPoolByTypeAndPriority(TYPE_IO, priority);
+ }
+
+ /**
+ * Return a thread pool that creates (CPU_COUNT + 1) threads
+ * operating off a queue which size is 128 and the maximum
+ * number of threads equals (2 * CPU_COUNT + 1).
+ *
+ * @return a cpu thread pool for
+ */
+ public static ExecutorService getCpuPool() {
+ return getPoolByTypeAndPriority(TYPE_CPU);
+ }
+
+ /**
+ * Return a thread pool that creates (CPU_COUNT + 1) threads
+ * operating off a queue which size is 128 and the maximum
+ * number of threads equals (2 * CPU_COUNT + 1).
+ *
+ * @param priority The priority of thread in the poll.
+ * @return a cpu thread pool for
+ */
+ public static ExecutorService getCpuPool(@IntRange(from = 1, to = 10) final int priority) {
+ return getPoolByTypeAndPriority(TYPE_CPU, priority);
+ }
+
+ /**
+ * Executes the given task in a fixed thread pool.
+ *
+ * @param size The size of thread in the fixed thread pool.
+ * @param task The task to execute.
+ * @param The type of the task's result.
+ */
+ public static void executeByFixed(@IntRange(from = 1) final int size, final Task task) {
+ execute(getPoolByTypeAndPriority(size), task);
+ }
+
+ /**
+ * Executes the given task in a fixed thread pool.
+ *
+ * @param size The size of thread in the fixed thread pool.
+ * @param task The task to execute.
+ * @param priority The priority of thread in the poll.
+ * @param The type of the task's result.
+ */
+ public static void executeByFixed(@IntRange(from = 1) final int size,
+ final Task task,
+ @IntRange(from = 1, to = 10) final int priority) {
+ execute(getPoolByTypeAndPriority(size, priority), task);
+ }
+
+ /**
+ * Executes the given task in a fixed thread pool after the given delay.
+ *
+ * @param size The size of thread in the fixed thread pool.
+ * @param task The task to execute.
+ * @param delay The time from now to delay execution.
+ * @param unit The time unit of the delay parameter.
+ * @param The type of the task's result.
+ */
+ public static void executeByFixedWithDelay(@IntRange(from = 1) final int size,
+ final Task task,
+ final long delay,
+ final TimeUnit unit) {
+ executeWithDelay(getPoolByTypeAndPriority(size), task, delay, unit);
+ }
+
+ /**
+ * Executes the given task in a fixed thread pool after the given delay.
+ *
+ * @param size The size of thread in the fixed thread pool.
+ * @param task The task to execute.
+ * @param delay The time from now to delay execution.
+ * @param unit The time unit of the delay parameter.
+ * @param priority The priority of thread in the poll.
+ * @param The type of the task's result.
+ */
+ public static void executeByFixedWithDelay(@IntRange(from = 1) final int size,
+ final Task task,
+ final long delay,
+ final TimeUnit unit,
+ @IntRange(from = 1, to = 10) final int priority) {
+ executeWithDelay(getPoolByTypeAndPriority(size, priority), task, delay, unit);
+ }
+
+ /**
+ * Executes the given task in a fixed thread pool at fix rate.
+ *
+ * @param size The size of thread in the fixed thread pool.
+ * @param task The task to execute.
+ * @param period The period between successive executions.
+ * @param unit The time unit of the period parameter.
+ * @param The type of the task's result.
+ */
+ public static void executeByFixedAtFixRate(@IntRange(from = 1) final int size,
+ final Task task,
+ final long period,
+ final TimeUnit unit) {
+ executeAtFixedRate(getPoolByTypeAndPriority(size), task, 0, period, unit);
+ }
+
+ /**
+ * Executes the given task in a fixed thread pool at fix rate.
+ *
+ * @param size The size of thread in the fixed thread pool.
+ * @param task The task to execute.
+ * @param period The period between successive executions.
+ * @param unit The time unit of the period parameter.
+ * @param priority The priority of thread in the poll.
+ * @param The type of the task's result.
+ */
+ public static void executeByFixedAtFixRate(@IntRange(from = 1) final int size,
+ final Task task,
+ final long period,
+ final TimeUnit unit,
+ @IntRange(from = 1, to = 10) final int priority) {
+ executeAtFixedRate(getPoolByTypeAndPriority(size, priority), task, 0, period, unit);
+ }
+
+ /**
+ * Executes the given task in a fixed thread pool at fix rate.
+ *
+ * @param size The size of thread in the fixed thread pool.
+ * @param task The task to execute.
+ * @param initialDelay The time to delay first execution.
+ * @param period The period between successive executions.
+ * @param unit The time unit of the initialDelay and period parameters.
+ * @param The type of the task's result.
+ */
+ public static void executeByFixedAtFixRate(@IntRange(from = 1) final int size,
+ final Task task,
+ long initialDelay,
+ final long period,
+ final TimeUnit unit) {
+ executeAtFixedRate(getPoolByTypeAndPriority(size), task, initialDelay, period, unit);
+ }
+
+ /**
+ * Executes the given task in a fixed thread pool at fix rate.
+ *
+ * @param size The size of thread in the fixed thread pool.
+ * @param task The task to execute.
+ * @param initialDelay The time to delay first execution.
+ * @param period The period between successive executions.
+ * @param unit The time unit of the initialDelay and period parameters.
+ * @param priority The priority of thread in the poll.
+ * @param The type of the task's result.
+ */
+ public static void executeByFixedAtFixRate(@IntRange(from = 1) final int size,
+ final Task task,
+ long initialDelay,
+ final long period,
+ final TimeUnit unit,
+ @IntRange(from = 1, to = 10) final int priority) {
+ executeAtFixedRate(getPoolByTypeAndPriority(size, priority), task, initialDelay, period, unit);
+ }
+
+ /**
+ * Executes the given task in a single thread pool.
+ *
+ * @param task The task to execute.
+ * @param The type of the task's result.
+ */
+ public static