diff --git a/core/function-impl/mogo-core-function-datacenter/src/main/java/com/mogo/eagle/core/function/datacenter/obu/MogoPrivateObuNewManager.kt b/core/function-impl/mogo-core-function-datacenter/src/main/java/com/mogo/eagle/core/function/datacenter/obu/MogoPrivateObuNewManager.kt index 501cb50af4..abb73a1638 100644 --- a/core/function-impl/mogo-core-function-datacenter/src/main/java/com/mogo/eagle/core/function/datacenter/obu/MogoPrivateObuNewManager.kt +++ b/core/function-impl/mogo-core-function-datacenter/src/main/java/com/mogo/eagle/core/function/datacenter/obu/MogoPrivateObuNewManager.kt @@ -8,12 +8,11 @@ import com.mogo.eagle.core.data.msgbox.MsgBoxBean import com.mogo.eagle.core.data.msgbox.MsgBoxType import com.mogo.eagle.core.data.msgbox.V2XMsg import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWarningStatusListener +import com.mogo.eagle.core.function.api.map.angle.* import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager import com.mogo.eagle.core.function.call.hmi.CallerHmiManager import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager -import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager.Scene.Default -import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager.Scene.TooClose import com.mogo.eagle.core.function.call.msgbox.CallerMsgBoxManager import com.mogo.eagle.core.function.call.obu.CallerObuConnectListenerManager import com.mogo.eagle.core.function.call.obu.CallerObuLocationWGS84ListenerManager @@ -805,13 +804,13 @@ private fun handleSdkObu( override fun onShow() { super.onShow() if (changeVisualAngle) { - CallerVisualAngleManager.changeVisualAngle(TooClose) + CallerVisualAngleManager.changeAngle(TooClose) } } override fun onDismiss() { if (changeVisualAngle) { - CallerVisualAngleManager.changeVisualAngle(Default()) + CallerVisualAngleManager.changeAngle(Default()) } } }, direction diff --git a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/vehicle/TurnLightViewStatus.kt b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/vehicle/TurnLightViewStatus.kt index e21e0514a6..e38d41ccbd 100644 --- a/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/vehicle/TurnLightViewStatus.kt +++ b/core/function-impl/mogo-core-function-hmi/src/main/java/com/mogo/eagle/core/function/hmi/ui/vehicle/TurnLightViewStatus.kt @@ -11,8 +11,8 @@ import android.view.animation.Animation import android.widget.ImageView import androidx.constraintlayout.widget.ConstraintLayout import chassis.Chassis +import com.mogo.eagle.core.function.api.map.angle.* import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager -import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager.Scene.Turning import com.mogo.eagle.core.function.hmi.R import kotlinx.android.synthetic.main.view_turn_light_status.view.* import kotlinx.coroutines.Dispatchers @@ -55,7 +55,7 @@ class TurnLightViewStatus @JvmOverloads constructor( //根据左右进行显示和隐藏,实际要判断每个来的时间和频度 when (directionLight) { Chassis.LightSwitch.LIGHT_LEFT -> { //左转向 - CallerVisualAngleManager.changeVisualAngle(Turning(true)) + CallerVisualAngleManager.changeAngle(Turning(true)) showNormalAnimation() left_select_image.visibility = View.VISIBLE right_select_image.visibility = View.GONE @@ -63,7 +63,7 @@ class TurnLightViewStatus @JvmOverloads constructor( setAnimation(left_select_image) } Chassis.LightSwitch.LIGHT_RIGHT -> { //右转向 - CallerVisualAngleManager.changeVisualAngle(Turning(true)) + CallerVisualAngleManager.changeAngle(Turning(true)) showNormalAnimation() left_select_image.visibility = View.GONE right_select_image.visibility = View.VISIBLE @@ -71,7 +71,7 @@ class TurnLightViewStatus @JvmOverloads constructor( setAnimation(right_select_image) } else -> { //消失 - CallerVisualAngleManager.changeVisualAngle(Turning(false)) + CallerVisualAngleManager.changeAngle(Turning(false)) animationDisappear() } } diff --git a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/angle/MoGoVisualAngleChangeProvider.kt b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/angle/MoGoVisualAngleChangeProvider.kt new file mode 100644 index 0000000000..52216f9c14 --- /dev/null +++ b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/angle/MoGoVisualAngleChangeProvider.kt @@ -0,0 +1,306 @@ +package com.mogo.eagle.core.function.angle + +import android.content.* +import android.os.* +import android.util.* +import androidx.lifecycle.* +import androidx.lifecycle.Lifecycle.Event +import androidx.lifecycle.Lifecycle.Event.ON_DESTROY +import com.alibaba.android.arouter.facade.annotation.Route +import com.mogo.eagle.core.data.config.* +import com.mogo.eagle.core.data.constants.MogoServicePaths +import com.mogo.eagle.core.data.map.* +import com.mogo.eagle.core.data.map.MapRoadInfo.StopLine +import com.mogo.eagle.core.function.api.map.angle.* +import com.mogo.eagle.core.function.api.map.angle.Scene +import com.mogo.eagle.core.function.call.autopilot.* +import com.mogo.eagle.core.function.call.map.* +import com.mogo.eagle.core.function.call.map.CallerMapRoadListenerManager.OnRoadListener +import com.mogo.eagle.core.utilcode.kotlin.* +import com.mogo.eagle.core.utilcode.mogo.* +import com.mogo.eagle.core.utilcode.mogo.logger.* +import com.zhidaoauto.map.sdk.open.tools.* +import kotlinx.coroutines.* +import kotlinx.coroutines.android.* +import java.util.* +import java.util.concurrent.atomic.* + +@Route(path = MogoServicePaths.PATH_VISUAL_ANGLE) +class MoGoVisualAngleChangeProvider: IMoGoVisualAngleChangeProvider { + + override val functionName: String + get() = "VisualAngleChange" + + companion object { + const val TAG = "VisualAngleChange" + } + + private val triggerLocation = AtomicReference() + + private val distanceOfCarToStopLine = AtomicReference(0.0) + + private val travelled by lazy { AtomicReference(0.0) } + + /** + * 业务实体,不对外暴露 + * @param target: 目标场景 + * @param isDisplay: 是否正在展示 + * @param triggerTime: 触发时间 + */ + private data class Record(val target: Scene, var isDisplay: Boolean = false, var triggerTime: Long): Comparable { + override fun compareTo(other: Record): Int { + //如果时间一样,优先级越高,越靠近堆顶 + return other.target.priority - target.priority + } + } + + private val queue by lazy { + PriorityQueue() + } + + private val listener = object : OnRoadListener { + private val roadId = AtomicReference() + private val triggerRoadId = AtomicReference() + + override fun onRoadIdInfo(roadId: String) { + this.roadId.set(roadId) + Log.d(TAG, "-- onRoadIdInfo --: prev: ${this.triggerRoadId.get()} -> curr: $roadId") + val loc = CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02() + var triggerClose = false + val distance = distanceOfCarToStopLine.get() + 5 + if (hasCrossRoad && distance > 0) { + val prev = triggerLocation.get() + if (prev != null && loc != null) { + travelled.set(MapTools.distance(loc.longitude, loc.latitude, prev.longitude, prev.latitude) + travelled.get()) + triggerLocation.set(loc) + } + val oldRoadId = triggerRoadId.get() + Log.d(TAG, "-- onRoadIdInfo --: travelled --: ${travelled.get()}") + if ((travelled.get() > distance) && oldRoadId != null && oldRoadId != roadId) { + distanceOfCarToStopLine.set(0.0) + hasCrossRoad = false + triggerRoadId.set(null) + travelled.set(0.0) + triggerLocation.set(null) + Log.d(TAG, "-- onRoadIdInfo --: trigger close --") + triggerClose = true + } + } + if (triggerClose) { + changeAngle(CrossRoad(false)) + } + } + + + override fun onStopLineInfo(info: StopLine) { + Log.d(TAG, "-- onStopLineInfo --: ${info.distanceOfCarToStopLine}") + if (!hasCrossRoad && info.distanceOfCarToStopLine <= 30.0) { + hasCrossRoad = true + triggerRoadId.set(this.roadId.get()) + distanceOfCarToStopLine.set(info.distanceOfCarToStopLine) + triggerLocation.set(CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()) + changeAngle(CrossRoad(true)) + } + } + } + + override fun init(context: Context?) { + if (Thread.currentThread() != Looper.getMainLooper().thread) { + scope.launch { + initListen(context) + } + } else { + initListen(context) + } + } + + private fun initListen(ctx: Context?) { + ctx ?: return + ctx.lifeCycleOwner.lifecycle.addObserver(object : LifecycleEventObserver { + override fun onStateChanged(source: LifecycleOwner, event: Event) { + if (event == ON_DESTROY) { + CallerMapRoadListenerManager.unRegisterRoadListener("VisualAngleChange") + } + } + }) + CallerMapRoadListenerManager.registerRoadListener("VisualAngleChange", listener) + } + + @Volatile + private var hasCrossRoad = false + + + private var scope: CoroutineScope = acquireScope() + @Synchronized + get() { + if (field.isActive) { + return field + } + val scope = acquireScope() + field = scope + return field + } + + private var defaultDelayJob: Job? = null + + private fun acquireScope(): CoroutineScope { + return CoroutineScope(Handler(Looper.getMainLooper()).asCoroutineDispatcher("change-visual-angle") + SupervisorJob()) + } + + + override fun onDestroy() { + CallerMapRoadListenerManager.unRegisterRoadListener("VisualAngleChange") + } + + @Volatile + private var mLevel:Boolean = false + + override fun updateLongSightLevel(level:Boolean){ + mLevel = level + } + + override fun changeAngle(scene: Scene) { + val appIdentityMode = FunctionBuildConfig.appIdentityMode + if (AppIdentityModeUtils.isBus(appIdentityMode) && AppIdentityModeUtils.isPassenger(appIdentityMode)) { + return + } + if(mLevel){ + return + } + val triggerTime = SystemClock.elapsedRealtime() + scope.launch { + Log.d(TAG, "--- 1 ---") + val displayed = getDisplayed() + if (displayed == null) { + Log.d(TAG, "--- 2 ---") + if (scene is Turning) { + if (!scene.open) { + changeAngle(Default()) + return@launch + } + } + if (scene is CrossRoad) { + if (!scene.open) { + changeAngle(Default()) + return@launch + } + } + doRealVisualAngleChange(triggerTime, scene, null) + } else { + val prev = displayed.target + Log.d(TAG, "--- 3 --- old: $prev -> cur: $scene") + val prevTriggerTime = displayed.triggerTime + if (scene !is Default && prev.priority > scene.priority && (prev is RoadEvent || prev is TooClose)) { + val displayDuration = triggerTime - prevTriggerTime + Log.d(TAG, "--- 4 ---:场景[$prev], 已展示时长: duration: $displayDuration") + if (displayDuration < prev.displayThreshold) { + Log.d(TAG, "--- 5 --- 场景[$prev]:仍在保护展示时长内,直接return") + return@launch + } else { + Log.d(TAG, "--- 6 --- 场景[$prev]:已过保护展示时长,从展示的队列中移除,显示默认视角") + queue -= displayed + changeAngle(Default()) + return@launch + } + } + if (prev is Turning && scene is Turning) { + val isOpen = scene.open + if (!isOpen) { + Log.d(TAG, "--- 7 --- 场景[$scene], 收到关闭通知") + queue -= displayed + changeAngle(Default()) + return@launch + } + } + if (prev is CrossRoad && scene is CrossRoad) { + val isOpen = scene.open + if (!isOpen) { + Log.d(TAG, "--- 8 --- old: $prev -> cur: $scene") + queue -= displayed + changeAngle(Default()) + return@launch + } + } + if (prev.priority == scene.priority) { + Log.d(TAG, "--- 9 --- 场景[$prev]正在展示,尚未收到关闭,优先级一致,直接return") + return@launch + } + if (prev.priority > scene.priority && prev.displayThreshold < 0) { + Log.d(TAG, "--- 10 --- 场景[$prev]正在展示,尚未收到关闭,场景,依然展示当前场景,直接return") + return@launch + } + doRealVisualAngleChange(triggerTime, scene, displayed) + } + } + } + + private fun CoroutineScope.doRealVisualAngleChange(triggerTime: Long, target: Scene, displayed: Record? = null) { + if (target is Default) { + Log.d(TAG, "--- doRealVisualAngleChange --- 1 ---") + displayed?.also { + queue -= it + } + defaultDelayJob?.safeCancel() + launch { + val delay = target.unit.toMillis(target.delay) + Log.d(TAG, "--- doRealVisualAngleChange --- 2 ---") + delay(delay) + Log.d(TAG, "--- doRealVisualAngleChange --- 3 ---") + doChangeAngle(Record(target, triggerTime = triggerTime)) + }.also { itx -> + itx.invokeOnCompletion { + if (it is CancellationException) { + Log.d(TAG, "--- doRealVisualAngleChange --- 4 ---") + } + } + defaultDelayJob = itx + } + } else { + Log.d(TAG, "--- doRealVisualAngleChange --- 5 ---") + defaultDelayJob?.safeCancel() + if (displayed == null || displayed.target.priority <= target.priority) { + Log.d(TAG, "--- doRealVisualAngleChange --- 6 ---") + displayed?.also { + queue -= it + } + if (target is Turning) { + if (!target.open) { + Log.d(TAG, "--- doRealVisualAngleChange --- 7 ---") + changeAngle(Default()) + return + } + } + if (target is CrossRoad) { + if (!target.open) { + Log.d(TAG, "--- doRealVisualAngleChange --- 8 ---") + changeAngle(Default()) + return + } + } + Log.d(TAG, "--- doRealVisualAngleChange --- 10 ---") + doChangeAngle(Record(target, triggerTime = triggerTime)) + } + } + } + + @OptIn(InternalCoroutinesApi::class) + private fun doChangeAngle(record: Record) { + val angle = record.target.angle + CallerMapUIServiceManager.getMapUIController()?.also { + Log.d(TAG, "--- doChangeAngle ---: ${record.target}") + if (record.target !is Default) { + record.isDisplay = true + kotlinx.coroutines.internal.synchronized(queue) { + queue += record + } + } + it.changeMapVisualAngle(angle, null) + } + } + + /** + * 是否有正在展示的 + */ + @Synchronized + private fun getDisplayed() = queue.firstOrNull() +} \ No newline at end of file diff --git a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/impl/MogoMapService.java b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/impl/MogoMapService.java index 1516eeddbc..651b2a8a74 100644 --- a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/impl/MogoMapService.java +++ b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/impl/MogoMapService.java @@ -52,6 +52,5 @@ public class MogoMapService implements IMogoMapService { @Override public void init(Context context) { - CallerVisualAngleManager.INSTANCE.init(context); } } diff --git a/core/function-impl/mogo-core-function-v2x/src/main/java/com/mogo/eagle/core/function/v2x/events/V2XEventManager.kt b/core/function-impl/mogo-core-function-v2x/src/main/java/com/mogo/eagle/core/function/v2x/events/V2XEventManager.kt index 1c40d47f56..920a297268 100644 --- a/core/function-impl/mogo-core-function-v2x/src/main/java/com/mogo/eagle/core/function/v2x/events/V2XEventManager.kt +++ b/core/function-impl/mogo-core-function-v2x/src/main/java/com/mogo/eagle/core/function/v2x/events/V2XEventManager.kt @@ -31,6 +31,7 @@ import com.mogo.eagle.core.data.traffic.TrafficData import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWarningStatusListener +import com.mogo.eagle.core.function.api.map.angle.* import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager @@ -38,8 +39,6 @@ import com.mogo.eagle.core.function.call.hmi.CallerHmiManager import com.mogo.eagle.core.function.call.map.CallerMapIdentifyManager import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager -import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager.Scene.Default -import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager.Scene.TooClose import com.mogo.eagle.core.function.call.msgbox.CallerMsgBoxManager import com.mogo.eagle.core.function.v2x.events.alarm.V2XAlarmServer import com.mogo.eagle.core.function.v2x.events.bridge.BridgeApi @@ -373,13 +372,13 @@ object V2XEventManager : IMoGoChassisLocationGCJ02Listener, IMoGoTokenCallback, val change = changeVisualAngle override fun onShow() { if (change) { - CallerVisualAngleManager.changeVisualAngle(TooClose) + CallerVisualAngleManager.changeAngle(TooClose) } } override fun onDismiss() { if (change) { - CallerVisualAngleManager.changeVisualAngle(Default()) + CallerVisualAngleManager.changeAngle(Default()) } } } diff --git a/core/function-impl/mogo-core-function-v2x/src/main/java/com/mogo/eagle/core/function/v2x/events/scenario/scene/road/V2XRoadEventScenario.java b/core/function-impl/mogo-core-function-v2x/src/main/java/com/mogo/eagle/core/function/v2x/events/scenario/scene/road/V2XRoadEventScenario.java index 1c4d34d86a..c604e02cc3 100644 --- a/core/function-impl/mogo-core-function-v2x/src/main/java/com/mogo/eagle/core/function/v2x/events/scenario/scene/road/V2XRoadEventScenario.java +++ b/core/function-impl/mogo-core-function-v2x/src/main/java/com/mogo/eagle/core/function/v2x/events/scenario/scene/road/V2XRoadEventScenario.java @@ -11,6 +11,8 @@ import com.mogo.eagle.core.data.msgbox.MsgBoxBean; import com.mogo.eagle.core.data.msgbox.MsgBoxType; import com.mogo.eagle.core.data.msgbox.V2XMsg; import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWarningStatusListener; +import com.mogo.eagle.core.function.api.map.angle.Default; +import com.mogo.eagle.core.function.api.map.angle.RoadEvent; import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager; import com.mogo.eagle.core.function.call.hmi.CallerHmiManager; import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager; @@ -151,7 +153,7 @@ public class V2XRoadEventScenario extends AbsV2XScenario imp @Override public void onShow() { if (isNeedChangeAngle()) { - CallerVisualAngleManager.INSTANCE.changeVisualAngle(CallerVisualAngleManager.Scene.RoadEvent.INSTANCE); + CallerVisualAngleManager.INSTANCE.changeAngle(RoadEvent.INSTANCE); } V2XMessageEntity entity = getV2XMessageEntity(); if (entity != null) { @@ -177,7 +179,7 @@ public class V2XRoadEventScenario extends AbsV2XScenario imp public void onDismiss() { CallerHmiManager.INSTANCE.dismissWarning(WarningDirectionEnum.ALERT_WARNING_TOP); if (isNeedChangeAngle()) { - CallerVisualAngleManager.INSTANCE.changeVisualAngle(new CallerVisualAngleManager.Scene.Default(3, TimeUnit.SECONDS)); + CallerVisualAngleManager.INSTANCE.changeAngle(new Default(3, TimeUnit.SECONDS)); } release(); } diff --git a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/constants/MogoServicePaths.java b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/constants/MogoServicePaths.java index b67cf92f55..f319c9556a 100644 --- a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/constants/MogoServicePaths.java +++ b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/constants/MogoServicePaths.java @@ -96,4 +96,7 @@ public class MogoServicePaths { @Keep public static final String PATH_IDENTIFY = "/map/identify"; + + @Keep + public static final String PATH_VISUAL_ANGLE = "/map/angle_change"; } diff --git a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/map/angle/IMoGoVisualAngleChangeProvider.kt b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/map/angle/IMoGoVisualAngleChangeProvider.kt new file mode 100644 index 0000000000..a4c1ec1712 --- /dev/null +++ b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/map/angle/IMoGoVisualAngleChangeProvider.kt @@ -0,0 +1,10 @@ +package com.mogo.eagle.core.function.api.map.angle + +import com.mogo.eagle.core.function.api.base.* + +interface IMoGoVisualAngleChangeProvider: IMoGoFunctionServerProvider { + + fun updateLongSightLevel(level: Boolean) + + fun changeAngle(scene: Scene) +} \ No newline at end of file diff --git a/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/map/angle/Scenes.kt b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/map/angle/Scenes.kt new file mode 100644 index 0000000000..296f992b05 --- /dev/null +++ b/core/mogo-core-function-api/src/main/java/com/mogo/eagle/core/function/api/map/angle/Scenes.kt @@ -0,0 +1,104 @@ +package com.mogo.eagle.core.function.api.map.angle + +import com.mogo.map.uicontroller.* +import java.util.concurrent.* +import java.util.concurrent.TimeUnit.SECONDS + +private interface IAttach { + val angle: VisualAngleMode + val priority: Int + val displayThreshold: Long //最大展示时长 > 0; 表示最长展示多长时间, -1 表示,一直展示,直到触发默认视角, 0: 默认视角专用值, + } + + +sealed class Scene: IAttach + + +/** + * 默认视图 + * @param delay: 表示多少稍后,默认值为2 + * @param unit: 时间单位,默认为秒 + */ +class Default(val delay: Long = 2, val unit: TimeUnit = SECONDS): Scene() { + + override val angle: VisualAngleMode = VisualAngleMode.MODE_MEDIUM_SIGHT + + override val priority: Int = 1 + + override val displayThreshold: Long + get() = 0 + + override fun toString(): String { + return "Default(delay=$delay, unit=$unit, angle=$angle, priority=$priority)" + } +} + + +/** + * 变道-接收到转向灯信息号 + */ +class Turning(var open: Boolean = false): Scene() { + + override val angle: VisualAngleMode = VisualAngleMode.MAP_STYLE_VR_ANGLE_TOP + + override val priority: Int = 3 + + override val displayThreshold: Long + get() = -1 + + override fun toString(): String { + return "Turning(open: ${open}, priority=$priority, displayThreshold: $displayThreshold, priority=$priority)" + } +} + + +/** + * 后方车辆离自车过近 + */ +object TooClose: Scene() { + + override val angle: VisualAngleMode = VisualAngleMode.MAP_STYLE_VR_ANGLE_300 + + override val priority: Int = 2 + + override val displayThreshold: Long + get() = SECONDS.toMillis(8) + + override fun toString(): String { + return "TooClose(priority=$priority, displayThreshold: $displayThreshold, priority=$priority)" + } +} + + +/** + * 道路事件 + */ +object RoadEvent: Scene() { + + override val angle: VisualAngleMode = VisualAngleMode.MODE_LONG_SIGHT + + override val priority: Int = 5 + + override val displayThreshold: Long + get() = SECONDS.toMillis(8) + + override fun toString(): String { + return "RoadEvent(priority=${priority}, displayThreshold: ${displayThreshold}, priority=${priority})" + } +} + + +/** + * 十字路口 + */ +class CrossRoad(var open: Boolean = false): Scene() { + override val angle: VisualAngleMode = VisualAngleMode.MAP_STYLE_VR_ANGLE_CROSS + override val priority: Int = 4 + override val displayThreshold: Long + get() = -1 + + override fun toString(): String { + return "CrossRoad(open: ${open}, priority=${priority}, displayThreshold: ${displayThreshold}, priority=${priority})" + } +} + diff --git a/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/map/CallerVisualAngleManager.kt b/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/map/CallerVisualAngleManager.kt index 5970ebf407..4387d4a198 100644 --- a/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/map/CallerVisualAngleManager.kt +++ b/core/mogo-core-function-call/src/main/java/com/mogo/eagle/core/function/call/map/CallerVisualAngleManager.kt @@ -1,398 +1,24 @@ package com.mogo.eagle.core.function.call.map -import android.content.Context -import android.os.Handler -import android.os.Looper -import android.os.SystemClock -import android.util.* -import androidx.lifecycle.Lifecycle.Event -import androidx.lifecycle.Lifecycle.Event.ON_DESTROY -import androidx.lifecycle.LifecycleEventObserver -import androidx.lifecycle.LifecycleOwner -import com.mogo.eagle.core.data.config.FunctionBuildConfig -import com.mogo.eagle.core.data.map.* -import com.mogo.eagle.core.data.map.MapRoadInfo.StopLine -import com.mogo.eagle.core.function.call.autopilot.* -import com.mogo.eagle.core.function.call.map.CallerMapRoadListenerManager.OnRoadListener -import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager.Scene.* -import com.mogo.eagle.core.utilcode.kotlin.lifeCycleOwner -import com.mogo.eagle.core.utilcode.kotlin.safeCancel -import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils -import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_DEVA -import com.mogo.map.uicontroller.VisualAngleMode -import com.zhidaoauto.map.sdk.open.tools.MapTools -import kotlinx.coroutines.* -import kotlinx.coroutines.android.asCoroutineDispatcher -import kotlinx.coroutines.internal.synchronized -import java.util.* -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeUnit.SECONDS -import java.util.concurrent.atomic.* -import kotlin.math.* +import com.alibaba.android.arouter.launcher.* +import com.mogo.eagle.core.data.constants.* +import com.mogo.eagle.core.function.api.map.angle.* + /** * 高精地图视角管理类 */ object CallerVisualAngleManager { - private const val TAG = "VisualAngle" - - @Volatile - private var hasCrossRoad = false - - - private var scope: CoroutineScope = acquireScope() - @Synchronized - get() { - if (field.isActive) { - return field - } - val scope = acquireScope() - field = scope - return field - } - - private var defaultDelayJob: Job? = null - - private fun acquireScope(): CoroutineScope { - return CoroutineScope(Handler(Looper.getMainLooper()).asCoroutineDispatcher("change-visual-angle") + SupervisorJob()) + private val provider by lazy { ARouter.getInstance().build(MogoServicePaths.PATH_VISUAL_ANGLE) + .navigation() as? IMoGoVisualAngleChangeProvider } - private interface IAttach { - val angle: VisualAngleMode - val priority: Int - val displayThreshold: Long //最大展示时长 > 0; 表示最长展示多长时间, -1 表示,一直展示,直到触发默认视角, 0: 默认视角专用值, + fun changeAngle(scene: Scene) { + provider?.changeAngle(scene) } - private val triggerLocation = AtomicReference() - - private val distanceOfCarToStopLine = AtomicReference(0.0) - - private val travelled by lazy { AtomicReference(0.0) } - - private val listener = object : OnRoadListener { - private val roadId = AtomicReference() - private val triggerRoadId = AtomicReference() - - override fun onRoadIdInfo(roadId: String) { - this.roadId.set(roadId) - Log.d(TAG, "-- onRoadIdInfo --: prev: ${this.triggerRoadId.get()} -> curr: $roadId") - val loc = CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02() - var triggerClose = false - val distance = distanceOfCarToStopLine.get() + 5 - if (hasCrossRoad && distance > 0) { - val prev = triggerLocation.get() - if (prev != null && loc != null) { - travelled.set(MapTools.distance(loc.longitude, loc.latitude, prev.longitude, prev.latitude) + travelled.get()) - triggerLocation.set(loc) - } - val oldRoadId = triggerRoadId.get() - Log.d(TAG, "-- onRoadIdInfo --: travelled --: ${travelled.get()}") - if ((travelled.get() > distance) && oldRoadId != null && oldRoadId != roadId) { - distanceOfCarToStopLine.set(0.0) - hasCrossRoad = false - triggerRoadId.set(null) - travelled.set(0.0) - triggerLocation.set(null) - Log.d(TAG, "-- onRoadIdInfo --: trigger close --") - triggerClose = true - } - } - if (triggerClose) { - changeVisualAngle(CrossRoad(false)) - } - } - - - override fun onStopLineInfo(info: StopLine) { - Log.d(TAG, "-- onStopLineInfo --: ${info.distanceOfCarToStopLine}") - if (!hasCrossRoad && info.distanceOfCarToStopLine <= 30.0) { - hasCrossRoad = true - triggerRoadId.set(this.roadId.get()) - distanceOfCarToStopLine.set(info.distanceOfCarToStopLine) - triggerLocation.set(CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()) - changeVisualAngle(CrossRoad(true)) - } - } + fun updateLongSightLevel(level: Boolean) { + provider?.updateLongSightLevel(level) } - - fun init(ctx: Context) { - if (Thread.currentThread() != Looper.getMainLooper().thread) { - scope.launch { - initListen(ctx) - } - } else { - initListen(ctx) - } - } - - private fun initListen(ctx: Context) { - ctx.lifeCycleOwner.lifecycle.addObserver(object : LifecycleEventObserver { - override fun onStateChanged(source: LifecycleOwner, event: Event) { - if (event == ON_DESTROY) { - CallerMapRoadListenerManager.unRegisterRoadListener("VisualAngleChange") - } - } - }) - CallerMapRoadListenerManager.registerRoadListener("VisualAngleChange", listener) - } - - sealed class Scene private constructor(): IAttach { - - /** - * 默认视图 - * @param delay: 表示多少稍后,默认值为2 - * @param unit: 时间单位,默认为秒 - */ - class Default(val delay: Long = 2, val unit: TimeUnit = SECONDS): Scene() { - - override val angle: VisualAngleMode = VisualAngleMode.MODE_MEDIUM_SIGHT - - override val priority: Int = 1 - - override val displayThreshold: Long - get() = 0 - - override fun toString(): String { - return "Default(delay=$delay, unit=$unit, angle=$angle, priority=$priority)" - } - } - - /** - * 变道-接收到转向灯信息号 - */ - class Turning(var open: Boolean = false): Scene() { - - override val angle: VisualAngleMode = VisualAngleMode.MAP_STYLE_VR_ANGLE_TOP - - override val priority: Int = 3 - - override val displayThreshold: Long - get() = -1 - - override fun toString(): String { - return "Turning(open: ${open}, priority=$priority, displayThreshold: $displayThreshold, priority=$priority)" - } - } - - /** - * 后方车辆离自车过近 - */ - object TooClose: Scene() { - - override val angle: VisualAngleMode = VisualAngleMode.MAP_STYLE_VR_ANGLE_300 - - override val priority: Int = 2 - - override val displayThreshold: Long - get() = SECONDS.toMillis(8) - - override fun toString(): String { - return "TooClose(priority=$priority, displayThreshold: $displayThreshold, priority=$priority)" - } - } - - /** - * 道路事件 - */ - object RoadEvent: Scene() { - - override val angle: VisualAngleMode = VisualAngleMode.MODE_LONG_SIGHT - - override val priority: Int = 5 - - override val displayThreshold: Long - get() = SECONDS.toMillis(8) - - override fun toString(): String { - return "RoadEvent(priority=${priority}, displayThreshold: ${displayThreshold}, priority=${priority})" - } - } - - - /** - * 十字路口 - */ - class CrossRoad(var open: Boolean = false): Scene() { - override val angle: VisualAngleMode = VisualAngleMode.MAP_STYLE_VR_ANGLE_CROSS - override val priority: Int = 4 - override val displayThreshold: Long - get() = -1 - - override fun toString(): String { - return "CrossRoad(open: ${open}, priority=${priority}, displayThreshold: ${displayThreshold}, priority=${priority})" - } - } - } - - - /** - * 业务实体,不对外暴露 - * @param target: 目标场景 - * @param isDisplay: 是否正在展示 - * @param triggerTime: 触发时间 - */ - private data class Record(val target: Scene, var isDisplay: Boolean = false, var triggerTime: Long): Comparable { - override fun compareTo(other: Record): Int { - //如果时间一样,优先级越高,越靠近堆顶 - return other.target.priority - target.priority - } - } - - private val queue by lazy { - PriorityQueue() - } - - @Volatile - private var mLevel:Boolean = false - - fun updateLongSightLevel(level:Boolean){ - mLevel = level - } - - fun changeVisualAngle(current: Scene) { - val appIdentityMode = FunctionBuildConfig.appIdentityMode - if (AppIdentityModeUtils.isBus(appIdentityMode) && AppIdentityModeUtils.isPassenger(appIdentityMode)) { - return - } - if(mLevel){ - return - } - val triggerTime = SystemClock.elapsedRealtime() - scope.launch { - Log.d("${M_DEVA}${TAG}", "--- 1 ---") - val displayed = getDisplayed() - if (displayed == null) { - Log.d("${M_DEVA}${TAG}", "--- 2 ---") - if (current is Turning) { - if (!current.open) { - changeVisualAngle(Default()) - return@launch - } - } - if (current is CrossRoad) { - if (!current.open) { - changeVisualAngle(Default()) - return@launch - } - } - doRealVisualAngleChange(triggerTime, current, null) - } else { - val prev = displayed.target - Log.d("${M_DEVA}${TAG}", "--- 3 --- old: $prev -> cur: $current") - val prevTriggerTime = displayed.triggerTime - if (current !is Default && prev.priority > current.priority && (prev is RoadEvent || prev is TooClose)) { - val displayDuration = triggerTime - prevTriggerTime - Log.d("${M_DEVA}${TAG}", "--- 4 ---:场景[$prev], 已展示时长: duration: $displayDuration") - if (displayDuration < prev.displayThreshold) { - Log.d("${M_DEVA}${TAG}", "--- 5 --- 场景[$prev]:仍在保护展示时长内,直接return") - return@launch - } else { - Log.d("${M_DEVA}${TAG}", "--- 6 --- 场景[$prev]:已过保护展示时长,从展示的队列中移除,显示默认视角") - queue -= displayed - changeVisualAngle(Default()) - return@launch - } - } - if (prev is Turning && current is Turning) { - val isOpen = current.open - if (!isOpen) { - Log.d("${M_DEVA}${TAG}", "--- 7 --- 场景[$current], 收到关闭通知") - queue -= displayed - changeVisualAngle(Default()) - return@launch - } - } - if (prev is CrossRoad && current is CrossRoad) { - val isOpen = current.open - if (!isOpen) { - Log.d("${M_DEVA}${TAG}", "--- 8 --- old: $prev -> cur: $current") - queue -= displayed - changeVisualAngle(Default()) - return@launch - } - } - if (prev.priority == current.priority) { - Log.d("${M_DEVA}${TAG}", "--- 9 --- 场景[$prev]正在展示,尚未收到关闭,优先级一致,直接return") - return@launch - } - if (prev.priority > current.priority && prev.displayThreshold < 0) { - Log.d("${M_DEVA}${TAG}", "--- 10 --- 场景[$prev]正在展示,尚未收到关闭,场景,依然展示当前场景,直接return") - return@launch - } - doRealVisualAngleChange(triggerTime, current, displayed) - } - } - } - - private fun CoroutineScope.doRealVisualAngleChange(triggerTime: Long, target: Scene, displayed: Record? = null) { - if (target is Default) { - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 1 ---") - displayed?.also { - queue -= it - } - defaultDelayJob?.safeCancel() - launch { - val delay = target.unit.toMillis(target.delay) - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 2 ---") - delay(delay) - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 3 ---") - doChangeAngle(Record(target, triggerTime = triggerTime)) - }.also { itx -> - itx.invokeOnCompletion { - if (it is CancellationException) { - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 4 ---") - } - } - defaultDelayJob = itx - } - } else { - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 5 ---") - defaultDelayJob?.safeCancel() - if (displayed == null || displayed.target.priority <= target.priority) { - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 6 ---") - displayed?.also { - queue -= it - } - if (target is Turning) { - if (!target.open) { - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 7 ---") - changeVisualAngle(Default()) - return - } - } - if (target is CrossRoad) { - if (!target.open) { - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 8 ---") - changeVisualAngle(Default()) - return - } - } - Log.d("${M_DEVA}${TAG}", "--- doRealVisualAngleChange --- 10 ---") - doChangeAngle(Record(target, triggerTime = triggerTime)) - } - } - } - - @OptIn(InternalCoroutinesApi::class) - private fun doChangeAngle(record: Record) { - val angle = record.target.angle - CallerMapUIServiceManager.getMapUIController()?.also { - Log.d("${M_DEVA}${TAG}", "--- doChangeAngle ---: ${record.target}") - if (record.target !is Default) { - record.isDisplay = true - synchronized(queue) { - queue += record - } - } - it.changeMapVisualAngle(angle, null) - } - } - - /** - * 是否有正在展示的 - */ - @Synchronized - private fun getDisplayed() = queue.firstOrNull() - } \ No newline at end of file