[fea]
[m2]
[mogomind]
This commit is contained in:
yangyakun
2025-06-10 18:58:35 +08:00
parent 4375c7a0c7
commit 6cf2dce10c
42 changed files with 2621 additions and 32 deletions

View File

@@ -0,0 +1,64 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
import java.util.concurrent.CopyOnWriteArrayList
object AIMessageManager {
// 使用 CopyOnWriteArrayList 来存储消息回调列表,保证线程安全
private val messageListeners: MutableList<AIMessageListener> = CopyOnWriteArrayList()
/**
* 注册一个消息监听器。
*
* @param listener 要注册的 AiMessageListener 实例。
*/
fun registerListener(listener: AIMessageListener) {
messageListeners.add(listener)
}
/**
* 取消注册一个消息监听器。
*
* @param listener 要取消注册的 AiMessageListener 实例。
*/
fun unregisterListener(listener: AIMessageListener) {
messageListeners.remove(listener)
}
/**
* 发布一条消息。
*
* 这条消息会被发送给所有已注册的监听器。
*
* @param msg 要发布的消息。
*/
fun post(msg: AIMessage) {
// 遍历所有已注册的监听器,并调用它们的 onReceive 方法
messageListeners.forEach { callback ->
callback.onReceive(msg)
}
}
fun clearData(){
messageListeners.forEach { callback ->
callback.clear()
}
}
/**
* 消息监听器接口。
*/
interface AIMessageListener {
/**
* 当接收到消息时,会调用此方法。
*
* @param msg 接收到的消息。
*/
fun onReceive(msg: AIMessage)
fun clear()
}
}

View File

@@ -0,0 +1,211 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mogo.eagle.core.data.enums.EventTypeEnumNew
import com.mogo.eagle.core.function.call.hmi.CallerRoadV2NEventWindowListenerManager
import com.mogo.eagle.core.function.call.map.CallerMapRoadListenerManager
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.util.UriUtils
import com.mogo.och.shuttle.weaknet.passenger.R
import com.mogo.och.shuttle.weaknet.passenger.ui.line.lineinfo.LineViewModel
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter.AIMessageAdapter
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter.OnItemClickListener
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter.PaddingItemDecoration
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.data.AutomaticExplorationViewModel
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.data.NDEViewModel
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.data.RoadCrossRoamViewModel
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.data.RoadV2NEventViewModel
import kotlinx.android.synthetic.main.m2_mind_view.view.rv_mind_list
import kotlinx.android.synthetic.main.m2_mind_view.view.test1
import kotlinx.android.synthetic.main.m2_mind_view.view.test2
import kotlinx.android.synthetic.main.m2_mind_view.view.test3
import kotlinx.coroutines.launch
class MindView : ConstraintLayout, MindViewModel.AiViewCallback {
private val TAG = "MindView"
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)
private var roadV2NEventModel: RoadV2NEventViewModel?= null
private var roadCrossRoamModel: RoadCrossRoamViewModel?= null
private var automaticExplorationModel: AutomaticExplorationViewModel?= null
private var ndeViewModel: NDEViewModel?= null
private var viewModel:MindViewModel?=null
private var isUserScrollingTime = 0L
private val SCROLL_THRESHOLD = 2000L
private val messageAdapter: AIMessageAdapter by lazy { AIMessageAdapter() }
private val messageLayoutManager: LinearLayoutManager by lazy {
LinearLayoutManager(context).apply {
stackFromEnd = true
}
}
private fun initView() {
LayoutInflater.from(context).inflate(R.layout.m2_mind_view, this, true)
rv_mind_list.layoutManager = messageLayoutManager
rv_mind_list.adapter = messageAdapter
rv_mind_list.addItemDecoration(PaddingItemDecoration(200, 300))
messageAdapter.onItemClickListener = OnItemClickListener { item, position ->
if (item is AIMessage.Event) {
}
}
rv_mind_list.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState != RecyclerView.SCROLL_STATE_IDLE) {
isUserScrollingTime = System.currentTimeMillis()
}
}
})
test1.onClick {
CallerRoadV2NEventWindowListenerManager.showImage(
System.currentTimeMillis().toString(),
System.currentTimeMillis(),
EventTypeEnumNew.getUpdateIconRes(EventTypeEnumNew.TYPE_USECASE_ROAD_BUS_STATION.poiType),
String.format(
EventTypeEnumNew.getAlarmContent(EventTypeEnumNew.TYPE_USECASE_ROAD_BUS_STATION.poiType),
100
),
false,
EventTypeEnumNew.getWarningTts(EventTypeEnumNew.TYPE_USECASE_ROAD_BUS_STATION.poiType),
UriUtils.res2Uri(
EventTypeEnumNew.getPoiTypeBg(
EventTypeEnumNew.TYPE_USECASE_ROAD_BUS_STATION.poiType,
false
).toString()
).toString()
)
}
test2.onClick {
CallerMapRoadListenerManager.invokeCrossDevice(true)
}
test3.onClick {
val one = AIMessage.RoadMsg(201,1,true,false)
val two = AIMessage.RoadMsg(202,2,false,false)
val three = AIMessage.RoadMsg(203,3,false,true)
val sortedList = ArrayList<AIMessage.RoadMsg>()
sortedList.add(one)
sortedList.add(two)
sortedList.add(three)
val ndeEvent = AIMessage.NDEData(System.currentTimeMillis().toString(),"路口车龙","前方路口有车龙",sortedList)
AIMessageManager.post(ndeEvent)
// CallerRoadV2NEventWindowListenerManager.showImage(
// System.currentTimeMillis().toString(),
// System.currentTimeMillis(),
// EventTypeEnumNew.getUpdateIconRes(EventTypeEnumNew.TYPE_USECASE_ROAD_BUS_STATION.poiType),
// String.format(
// EventTypeEnumNew.getAlarmContent(EventTypeEnumNew.TYPE_USECASE_ROAD_BUS_STATION.poiType),
// 100
// ),
// false,
// String.format(
// EventTypeEnumNew.getWarningTts(EventTypeEnumNew.TYPE_USECASE_ROAD_BUS_STATION.poiType),
// 100
// ),
// UriUtils.res2Uri(
// EventTypeEnumNew.getPoiTypeBg(
// EventTypeEnumNew.TYPE_USECASE_ROAD_BUS_STATION.poiType,
// false
// ).toString()
// ).toString()
// )
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
viewModel = findViewTreeViewModelStoreOwner()?.let {
ViewModelProvider(it).get(MindViewModel::class.java)
}
viewModel?.setViewCallback(this)
roadV2NEventModel = findViewTreeViewModelStoreOwner()?.let{
ViewModelProvider(it)[RoadV2NEventViewModel::class.java]
}
roadV2NEventModel?.init()
roadCrossRoamModel = findViewTreeViewModelStoreOwner()?.let{
ViewModelProvider(it)[RoadCrossRoamViewModel::class.java]
}
roadCrossRoamModel?.init(context)
automaticExplorationModel = findViewTreeViewModelStoreOwner()?.let{
ViewModelProvider(it)[AutomaticExplorationViewModel::class.java]
}
automaticExplorationModel?.init()
ndeViewModel = findViewTreeViewModelStoreOwner()?.let{
ViewModelProvider(it)[NDEViewModel::class.java]
}
ndeViewModel?.init()
findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
viewModel?.messagesFlow?.collect {
Log.d(TAG, "${tName()} onMessages update: ${it}")
if(it.isNotEmpty()){
rv_mind_list.visibility = View.VISIBLE
}else{
rv_mind_list.visibility = View.VISIBLE
}
messageAdapter.submitList(it) {
Log.d(TAG, "${tName()} adapter submit: ")
scrollToBottom()
}
}
}
}
// 滚动到RecyclerView底部
private fun scrollToBottom() {
val delay = System.currentTimeMillis() - isUserScrollingTime
if (delay < SCROLL_THRESHOLD) {
return
}
val layoutManager = rv_mind_list.layoutManager as LinearLayoutManager
layoutManager.scrollToPositionWithOffset(messageAdapter.itemCount - 1, 0)
}
fun tName(): String {
return "${Thread.currentThread().name}"
}
override fun onVisibilityAggregated(isVisible: Boolean) {
super.onVisibilityAggregated(isVisible)
}
init {
try {
initView()
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@@ -0,0 +1,205 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mogo.eagle.core.data.ai.V2XRepository
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
import com.mogo.och.bridge.autopilot.location.OchLocationManager
import com.mogo.och.common.module.biz.birdge.BridgeListener
import com.mogo.och.common.module.biz.birdge.BridgeManager
import com.mogo.och.common.module.voice.VoiceNotice
import com.mogo.och.shuttle.weaknet.passenger.callback.ICommonCallback
import com.mogo.och.shuttle.weaknet.passenger.model.CommonModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class MindViewModel : ViewModel(), AIMessageManager.AIMessageListener,
BridgeListener {
private val msgList = mutableListOf<AIMessage>()
private var lastTimestamp = System.currentTimeMillis()
// 记录最后一次事件发生的时间,使用 private set 限制外部修改
// private val TIMESTAMP_THRESHOLD = 1000 * 60 * 5 // 5分钟
private val TIMESTAMP_THRESHOLD = 1000 * 30
private var llmResultJob: Job? = null
private var isChecking = false
private val _messagesFlow = MutableStateFlow<List<AIMessage>>(emptyList())
val messagesFlow: SharedFlow<List<AIMessage>> get() = _messagesFlow
private val _asrUIStateFlow = MutableStateFlow<AsrUIState>(AsrUIState.Idle)
val asrUIStateFlow: StateFlow<AsrUIState> get() = _asrUIStateFlow
private val tipsTimeOut = "小智还没反应过来,请稍后再试~"
private val tipsExit = "我先退下了"
val tipsQuestions = listOf(
"可以问我:前方路口有拥堵吗?",
"可以对我说:介绍一下蘑菇车联?",
"想要对话时可以点击:小智智能体",
"可以问我:今天天气怎么样?",
)
private val tipsWakeUpList = listOf(
"嗯,我在呢",
"在的",
"在呢",
"你说",
)
private val commontCallback = object : ICommonCallback{
override fun showNoTaskView(isTrue: Boolean) {
if(isTrue){
synchronized(msgList) {
msgList.clear()
_messagesFlow.value = msgList.toList()
}
}
}
}
private val locationCallback = object : IMoGoChassisLocationGCJ02Listener {
override fun onChassisLocationGCJ02(mogoLocation: MogoLocation?) {
mogoLocation?.let {
V2XRepository.provideLocation(it, 0)
}
}
}
init {
}
fun setViewCallback(aiView: AiViewCallback) {
CommonModel.setRouteLineInfoCallback(TAG,commontCallback)
OchLocationManager.addGCJ02Listener(TAG, 1, locationCallback)
AIMessageManager.registerListener(this)
BridgeManager.addBridgeListener(TAG,this)
}
override fun onCleared() {
AIMessageManager.unregisterListener(this)
llmResultJob?.cancel()
CommonModel.setRouteLineInfoCallback(TAG,null)
OchLocationManager.removeGCJ02Listener(TAG)
super.onCleared()
}
override fun onReceive(msg: AIMessage) {
Log.d(TAG, "onReceive: $msg")
if (isChecking) {
if (msg is AIMessage.Event) {
msg.showScanFlag = true
}
}
// 获更新消息
updateMsg(msg)
}
override fun clear() {
clearMsg()
}
private fun handleMsg(newMessage: AIMessage) {
val existingIndex = findMessageIndex(newMessage.id)
if (existingIndex != -1) {
handleExistingMessage(existingIndex, newMessage)
} else {
handleNewMessage(newMessage)
}
}
private fun findMessageIndex(messageId: String): Int {
return msgList.indexOfFirst { it.id == messageId }
}
private fun handleExistingMessage(index: Int, newMessage: AIMessage) {
val oldMessage = msgList[index]
newMessage.showTimestamp = oldMessage.showTimestamp
msgList[index] = newMessage
speakMessageIfNeeded(newMessage, isLastMessage = msgList.last() == newMessage)
}
private fun handleNewMessage(newMessage: AIMessage) {
updateTimestampIfNeeded(newMessage)
msgList.add(newMessage)
speakMessageIfNeeded(newMessage, isLastMessage = true)
}
private fun updateTimestampIfNeeded(newMessage: AIMessage) {
val time = newMessage.timestamp - lastTimestamp
if (time >= TIMESTAMP_THRESHOLD) {
newMessage.showTimestamp = true
lastTimestamp = newMessage.timestamp
}
}
private fun speakMessageIfNeeded(newMessage: AIMessage, isLastMessage: Boolean) {
if (isLastMessage && newMessage.tts.isNotEmpty()) {
VoiceNotice.showNotice(newMessage.tts)
}
}
companion object {
private const val TAG = "AiViewModel"
}
private fun updateMsg(msg: AIMessage) {
synchronized(msgList) {
handleMsg(msg)
_messagesFlow.value = msgList.toList()
}
}
private fun deleteMsg(msgId: String) {
synchronized(msgList) {
val iterator = msgList.iterator()
while (iterator.hasNext()) {
if (iterator.next().id == msgId) {
iterator.remove()
}
}
_messagesFlow.value = msgList.toList()
}
}
private fun clearMsg() {
synchronized(msgList) {
msgList.clear()
_messagesFlow.value = msgList.toList()
}
}
fun getQuestionsRandomIndex(): Int {
return tipsQuestions.indexOf(tipsQuestions.random())
}
interface AiViewCallback {
}
}
sealed class AsrUIState {
object Idle : AsrUIState()
data class Listening(val partialText: String) : AsrUIState()
data class Recognized(val finalText: String) : AsrUIState()
}

View File

@@ -0,0 +1,51 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import com.mogo.och.shuttle.weaknet.passenger.R
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
class AIMessageAdapter : ListAdapter<AIMessage, MessageViewHolder>(MessageDiffCallback()) {
var onItemClickListener: OnItemClickListener? = null
override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
getItem(position)?.let {
holder.bind(it, onItemClickListener)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
AIMessage.TYPE_PNC_ACTION -> PNCActionViewHolder(inflater.inflate(R.layout.item_ai_pnc_action,parent,false))
AIMessage.TYPE_ROAD_V2N -> RoadV2NEventViewHolder(inflater.inflate(R.layout.item_ai_road_v2n_event,parent,false))
AIMessage.TYPE_ROAD_CROSS -> RoadCrossRoamViewHolder(inflater.inflate(R.layout.item_ai_road_cross_roam,parent,false))// 全息路口
AIMessage.TYPE_AUTOMATIC_EXPLORATION -> AutomaticExplorationViewHolder(inflater.inflate(R.layout.item_ai_automatic_exploration,parent,false))// 探查
AIMessage.TYPE_NDE -> NDEViewHolder(inflater.inflate(R.layout.item_ai_nde_event,parent,false))// 车龙
else -> throw IllegalArgumentException("Invalid view type")
}
}
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is AIMessage.Event -> AIMessage.TYPE_EVENT
is AIMessage.Scan -> AIMessage.TYPE_SCAN
is AIMessage.Light -> AIMessage.TYPE_LIGHT
is AIMessage.Speed -> AIMessage.TYPE_SPEED
is AIMessage.Warning -> AIMessage.TYPE_WARNING
is AIMessage.PNCAction -> AIMessage.TYPE_PNC_ACTION
is AIMessage.RoadV2NEvent -> AIMessage.TYPE_ROAD_V2N
is AIMessage.RoadCrossRoam -> AIMessage.TYPE_ROAD_CROSS
is AIMessage.AutomaticExploration -> AIMessage.TYPE_AUTOMATIC_EXPLORATION
is AIMessage.NDEData -> AIMessage.TYPE_NDE
else -> AIMessage.TYPE_EVENT
}
}
override fun onViewRecycled(holder: MessageViewHolder) {
super.onViewRecycled(holder)
holder.viewRecycled(holder)
}
}

View File

@@ -0,0 +1,235 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Rect
import android.util.Log
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.mogo.eagle.core.data.notice.AutoExplorationEntity
import com.mogo.eagle.core.data.v2x.RoadV2NEventType
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationWGS84ListenerManager
import com.mogo.eagle.core.function.hmi.ui.notice.exploration.AutomaticExplorationAdapter
import com.mogo.eagle.core.function.hmi.ui.v2n.RoadV2NEventLivePlayView
import com.mogo.eagle.core.function.view.MapRoamView
import com.mogo.eagle.core.function.view.RoadCrossRoamListAdapter
import com.mogo.eagle.core.utilcode.mogo.glide.GlideImageLoader
import com.mogo.eagle.core.utilcode.mogo.imageloader.MogoImageView
import com.mogo.eagle.core.utilcode.util.DateTimeUtils
import com.mogo.och.shuttle.weaknet.passenger.R
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
abstract class MessageViewHolder(view: View) : RecyclerView.ViewHolder(view) {
abstract fun bind(item: AIMessage, onItemClickListener: OnItemClickListener? = null)
open fun viewRecycled(holder: MessageViewHolder){}
private val sampleDateFormat = SimpleDateFormat("HH:mm", Locale.CHINA)
protected val TAG = javaClass.simpleName
fun handleTimestamp(item: AIMessage, tvTimestamp: TextView) {
if (item.showTimestamp) {
tvTimestamp.visibility = View.VISIBLE
tvTimestamp.text = sampleDateFormat.format(Date(item.timestamp))
} else {
tvTimestamp.visibility = View.GONE
}
}
fun View.setVisibilityBasedOn(condition: Boolean) {
visibility = if (condition) View.VISIBLE else View.GONE
}
fun TextView.setTextAndVisibility(text: String) {
if (text.isEmpty()) {
visibility = View.GONE
this.text = ""
} else {
visibility = View.VISIBLE
this.text = text
}
}
fun ImageView.showOrHideWithUrl(url: String) {
if (url.isEmpty()) {
visibility = View.GONE
} else {
visibility = View.VISIBLE
Glide.with(this)
.load(url)
.placeholder(R.drawable.icon_pic_holder)
.error(R.drawable.icon_pic_error)
// .error(R.drawable.icon_marker_window_place_holder)
// .placeholder(R.drawable.icon_marker_window_place_holder)
.into(this)
}
}
}
class PNCActionViewHolder(binding: View) : MessageViewHolder(binding){
private var tvPncActionDesc: TextView = binding.findViewById(R.id.tvPNCHintContent)
override fun bind(item: AIMessage, onItemClickListener: OnItemClickListener?) {
if(item is AIMessage.PNCAction){
tvPncActionDesc.text = item.actionDesc
}
}
}
class RoadV2NEventViewHolder(binding: View) : MessageViewHolder(binding){
private var tvV2XHintContent: TextView = binding.findViewById(R.id.tvV2XHintContent)
private var containerImageAndLiveVideo: FrameLayout = binding.findViewById(R.id.containerImageAndLiveVideo)
private var livePlayView: RoadV2NEventLivePlayView = binding.findViewById(R.id.livePlayView)
private var contentImageView: MogoImageView = binding.findViewById(R.id.contentImageView)
override fun bind(item: AIMessage, onItemClickListener: OnItemClickListener?) {
if(item is AIMessage.RoadV2NEvent){
tvV2XHintContent.text = item.title
when (item.eventType) {
RoadV2NEventType.TEXT -> {
containerImageAndLiveVideo.visibility = View.GONE
contentImageView.visibility = View.GONE
livePlayView.visibility = View.GONE
}
RoadV2NEventType.IMAGE -> {
containerImageAndLiveVideo.visibility = View.VISIBLE
contentImageView.visibility = View.VISIBLE
livePlayView.visibility = View.GONE
GlideImageLoader.getInstance()
.displayImage(item.contentImageUrl, contentImageView)
}
RoadV2NEventType.LIVE_VIDEO -> {
containerImageAndLiveVideo.visibility = View.VISIBLE
contentImageView.visibility = View.GONE
livePlayView.visibility = View.VISIBLE
val cityCode =
CallerChassisLocationWGS84ListenerManager.getChassisLocationWGS84().cityCode
livePlayView.startRoadCameraLive(
item.id,
item.cameraIp, item.lon, item.lat, cityCode
)
}
}
}
}
}
class RoadCrossRoamViewHolder(binding: View) : MessageViewHolder(binding){
private var tvRoadRoamTitle: TextView = binding.findViewById(R.id.tvRoadRoamTitle)
private var lvRoadCrossRoamTip: RecyclerView = binding.findViewById(R.id.lvRoadCrossRoamTip)
override fun bind(item: AIMessage, onItemClickListener: OnItemClickListener?) {
if(item is AIMessage.RoadCrossRoam){
lvRoadCrossRoamTip.layoutManager = NoScrollLayoutManager(itemView.context)
// lvRoadCrossRoamTip.addItemDecoration(object : RecyclerView.ItemDecoration() {
// override fun getItemOffsets(
// outRect: Rect,
// view: View,
// parent: RecyclerView,
// state: RecyclerView.State
// ) {
// super.getItemOffsets(outRect, view, parent, state)
// outRect.bottom = 24
// }
// })
lvRoadCrossRoamTip.adapter = RoadCrossRoamListB2Adapter(itemView.context)
tvRoadRoamTitle.setTextColor(itemView.context.getColor(R.color.color_131415))
}
}
}
class AutomaticExplorationViewHolder(binding: View) : MessageViewHolder(binding){
private var rvExplorationList: RecyclerView = binding.findViewById(R.id.rvExplorationList)
private lateinit var automaticExplorationAdapter: AutomaticExplorationB2Adapter
override fun bind(item: AIMessage, onItemClickListener: OnItemClickListener?) {
val linearLayoutManager = LinearLayoutManager(itemView.context)
linearLayoutManager.orientation = LinearLayoutManager.VERTICAL
automaticExplorationAdapter = AutomaticExplorationB2Adapter(itemView.context)
rvExplorationList.adapter = automaticExplorationAdapter
rvExplorationList.layoutManager = linearLayoutManager
initData()
}
private fun initData() {
val dataList = ArrayList<AutoExplorationEntity>(7)
dataList.add(AutoExplorationEntity("当前道路事件分析",2000L,false))
dataList.add(AutoExplorationEntity("前方车辆",2000L,false))
dataList.add(AutoExplorationEntity("两侧车辆",2600L,false))
dataList.add(AutoExplorationEntity("后方车辆",3000L,false))
dataList.add(AutoExplorationEntity("前方路口车辆流速分析",4000L,false))
dataList.add(AutoExplorationEntity("前方路口行人/非机动车分析",4300L,false))
dataList.add(AutoExplorationEntity("路侧视频分析",5000L,false))
automaticExplorationAdapter.setListener(object: AutomaticExplorationB2Adapter.CompleteListener{
override fun onComplete(entity: AutoExplorationEntity) {
dataList.forEach {
if(it.explorationContent == entity.explorationContent){
it.explorationComplete = true
}
}
}
})
automaticExplorationAdapter.setData(dataList)
}
}
class NDEViewHolder(binding: View) : MessageViewHolder(binding){
private var tvNdeContent: TextView = binding.findViewById(R.id.tvNdeHintContent)
private var rvNdeList: RecyclerView = binding.findViewById(R.id.rvNdeList)
override fun bind(item: AIMessage, onItemClickListener: OnItemClickListener?) {
if(item is AIMessage.NDEData){
tvNdeContent.text = item.desc
val linearLayoutManager = LinearLayoutManager(itemView.context)
linearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL
val ndeRoadAdapter = AINDERoadAdapter(itemView.context)
rvNdeList.adapter = ndeRoadAdapter
rvNdeList.layoutManager = linearLayoutManager
ndeRoadAdapter.setData(item.roadList)
}
}
}
private class NoScrollLayoutManager(context: Context?) : LinearLayoutManager(context) {
override fun canScrollVertically(): Boolean {
return false
}
override fun canScrollHorizontally(): Boolean {
return false
}
}
fun interface OnItemClickListener {
fun onItemClick(item: AIMessage, position: Int)
}

View File

@@ -0,0 +1,237 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter
import android.content.Context
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.mogo.och.shuttle.weaknet.passenger.R
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
class AINDERoadAdapter(private val context: Context): RecyclerView.Adapter<AINDERoadAdapter.AIRoadHolder>() {
private var roadList: List<AIMessage.RoadMsg>?= null
fun setData(list: List<AIMessage.RoadMsg>){
roadList = list
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AIRoadHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.b2_item_ai_nde_road, parent, false)
return AIRoadHolder(view)
}
override fun onBindViewHolder(holder: AIRoadHolder, position: Int) {
roadList?.let{
val roadMsg = it[position]
if(it.size < 3){
//设置item宽度为最大宽度180dp
val params = ConstraintLayout.LayoutParams(
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90f,
context.resources.displayMetrics).toInt(),
ConstraintLayout.LayoutParams.WRAP_CONTENT)
holder.clRoadLayout.layoutParams = params
}else if(it.size == 3){
//设置item宽度为最大宽度180dp
val params = ConstraintLayout.LayoutParams(
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60f,
context.resources.displayMetrics).toInt(),
ConstraintLayout.LayoutParams.WRAP_CONTENT)
holder.clRoadLayout.layoutParams = params
}else if(it.size == 4){
//设置item宽度为最大宽度180dp
val params = ConstraintLayout.LayoutParams(
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f,
context.resources.displayMetrics).toInt(),
ConstraintLayout.LayoutParams.WRAP_CONTENT)
holder.clRoadLayout.layoutParams = params
}else{
val params = ConstraintLayout.LayoutParams(
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45f,
context.resources.displayMetrics).toInt(),
ConstraintLayout.LayoutParams.WRAP_CONTENT)
holder.clRoadLayout.layoutParams = params
}
when(roadMsg.arrowType){
//直行
201->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_forward
))
}
//直行或左转
202->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_forward_or_turn_left
))
}
//直行或右转
203->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_forward_or_turn_right
))
}
//直行或掉头
204->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_forward_or_reverse
))
}
//左转
205->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_turn_left
))
}
//左转或掉头
206->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_turn_left_or_reverse
))
}
//左弯或向左合流
207->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_turn_or_merge_left
))
}
//右转
208->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_turn_right
))
}
//右转或向右合流
209->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_turn_or_merge_right
))
}
//左右转弯
210->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_turn_left_or_right
))
}
//掉头
211->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_reverse
))
}
//禁止左转
212->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_prohibit_turn_left
))
}
//禁止右转
213->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_prohibit_turn_right
))
}
//禁止掉头
214->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_prohibit_reverse
))
}
//直行或左转或右转
215->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_forward_turn_left_right
))
}
//直行或掉头或左转
216->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_forward_turn_left_reverse
))
}
//右转或掉头
217->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_turn_right_or_reverse
))
}
//禁止右转或向右合流
218->{
holder.ivRoadType.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_road_prohibit_turn_or_merge_right
))
}
}
//是否是推荐车道
if(roadMsg.isRecommend){
holder.tvRoadStatus.text = context.getString(R.string.nde_road_recommend)
holder.tvRoadStatus.setTextColor(context.getColor(R.color.msg_nde_road_recommend))
holder.clRoadLayout.background = ContextCompat.getDrawable(
context,
R.drawable.bg_nde_road_recommend
)
}
//是否有车龙,代表拥堵、行驶缓慢
if(roadMsg.isCheLong){
holder.tvRoadStatus.text = context.getString(R.string.nde_road_slow)
holder.tvRoadStatus.setTextColor(context.getColor(R.color.msg_nde_road_slow))
}
if(position == it.lastIndex){
holder.viewDivider.visibility = View.INVISIBLE
}
}
}
override fun getItemCount() = roadList?.size ?: 0
class AIRoadHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var clRoadLayout: ConstraintLayout = itemView.findViewById(R.id.clRoadLayout)
var ivRoadType: ImageView = itemView.findViewById(R.id.ivRoadType)
var tvRoadStatus: TextView = itemView.findViewById(R.id.tvRoadStatus)
var viewDivider: View = itemView.findViewById(R.id.viewDivider)
}
}

View File

@@ -0,0 +1,83 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.mogo.eagle.core.data.notice.AutoExplorationEntity
import com.mogo.och.shuttle.weaknet.passenger.R
/**
* 自动探查适配器
* 鹰眼650需求
*/
class AutomaticExplorationB2Adapter(val context: Context): RecyclerView.Adapter<AutomaticExplorationB2Adapter.ExplorationHolder>() {
private var data: List<AutoExplorationEntity> ?= null
private var completeListener: CompleteListener ?= null
fun setData(data: List<AutoExplorationEntity>){
this.data = data
notifyDataSetChanged()
}
fun setListener(listener: CompleteListener){
completeListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExplorationHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_auto_exploration_b2, parent, false)
return ExplorationHolder(view)
}
override fun getItemCount() = data?.size ?: 0
override fun onBindViewHolder(holder: ExplorationHolder, position: Int) {
data?.let {
val entity = it[position]
holder.tvExplorationContent.text = entity.explorationContent
holder.ivExplorationLoading.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_exploration_loading_p
))
val rotationAnim = ObjectAnimator.ofFloat(holder.ivExplorationLoading, "rotation", 0f, 360f)
rotationAnim.repeatCount = entity.explorationDuration.toInt()/1000
rotationAnim.repeatMode = ValueAnimator.RESTART
rotationAnim.duration = 1000
rotationAnim.interpolator = LinearInterpolator()
rotationAnim.addListener(object: AnimatorListenerAdapter(){
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
completeListener?.onComplete(entity)
holder.ivExplorationLoading.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_exploration_done_p
))
}
})
rotationAnim.start()
}
}
class ExplorationHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var ivExplorationLoading: ImageView = itemView.findViewById(R.id.ivExplorationLoading)
var tvExplorationContent: TextView = itemView.findViewById(R.id.tvExplorationContent)
}
interface CompleteListener{
fun onComplete(entity: AutoExplorationEntity)
}
}

View File

@@ -0,0 +1,15 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter
import androidx.recyclerview.widget.DiffUtil
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
class MessageDiffCallback: DiffUtil.ItemCallback<AIMessage>() {
override fun areContentsTheSame(oldItem: AIMessage, newItem: AIMessage): Boolean {
return oldItem == newItem
}
override fun areItemsTheSame(oldItem: AIMessage, newItem: AIMessage): Boolean {
return oldItem.id == newItem.id
}
}

View File

@@ -0,0 +1,94 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.mogo.commons.AbsMogoApplication
import com.mogo.och.common.module.utils.ResourcesUtils
import com.mogo.och.shuttle.weaknet.passenger.R
import me.jessyan.autosize.utils.AutoSizeUtils
class PaddingItemDecoration(private val topPadding: Int, private val bottomPadding: Int) : RecyclerView.ItemDecoration() {
private var divider: Drawable
private var padding = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),13f)
init {
val shapeDrawable = GradientDrawable()
shapeDrawable.setColor(ResourcesUtils.getColor(R.color.b2_BBC9D4))
shapeDrawable.shape = GradientDrawable.RECTANGLE
val width = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),343f)
val height = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),1f)
shapeDrawable.setSize(width, height)
divider = shapeDrawable
}
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
// 只有第一个 item 的顶部添加空白
if (parent.getChildAdapterPosition(view) == 0) {
outRect.top = topPadding
} else{
outRect.top = 0
}
// 最后一个 item 的底部添加空白
// if (parent.getChildAdapterPosition(view) == state.itemCount - 1) {
// outRect.bottom = bottomPadding
// } else{
// outRect.bottom = 0
// }
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
val childCount = parent.childCount //获取可见item的数量
val spanCount: Int = getSpanCount(parent)
for (i in 0 until childCount) {
val child = parent.getChildAt(i)
val params = child
.layoutParams as RecyclerView.LayoutParams
val left = child.left - params.leftMargin + padding
val right = child.right - padding //- params.rightMargin - divider.intrinsicWidth
val top = child.bottom + params.bottomMargin
val bottom = top + divider.intrinsicHeight
divider.setBounds(left, top, right, bottom)
divider.draw(c)
if (i < spanCount) { //画第一行顶部的分割线
drawHorizontalForFirstRow(c, child)
}
}
}
private fun drawHorizontalForFirstRow(c: Canvas, child: View) {
val params = child
.layoutParams as RecyclerView.LayoutParams
val left = child.left - params.leftMargin - divider.intrinsicWidth
val top = child.top - params.topMargin - divider.intrinsicHeight
val right = child.right + params.rightMargin + divider.intrinsicWidth
val bottom = top + divider.intrinsicHeight
divider.setBounds(left, top, right, bottom)
divider.draw(c)
}
private fun getSpanCount(parent: RecyclerView): Int {
// 列数
var spanCount = -1
val layoutManager = parent.layoutManager
if (layoutManager is GridLayoutManager) {
spanCount = layoutManager.spanCount
} else if (layoutManager is StaggeredGridLayoutManager) {
spanCount = layoutManager
.spanCount
}
return spanCount
}
}

View File

@@ -0,0 +1,65 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.mogo.och.shuttle.weaknet.passenger.R
import kotlin.random.Random
class RoadCrossRoamListB2Adapter(private val mContext: Context) : RecyclerView.Adapter<RoadCrossRoamListB2Adapter.ViewHolder>() {
private val items: MutableList<String> = mutableListOf()
init {
items.add("前方路况拥堵分析")
items.add("路口危险车辆分析")
items.add("路口交通事故分析")
items.add("路口行人碰撞分析")
items.add("路口非机动车分析")
items.add("路口灯态分析")
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view: View =
LayoutInflater.from(mContext).inflate(R.layout.item_road_cross_ai_roam_tip_b2, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return 6
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
holder.textView.setTextColor(mContext.getColor(R.color.color_191A1C))
holder.textView.text = item
// 随机决定是否显示ProgressBar
// if (Random.nextBoolean()) { // 50%的几率显示ProgressBar
holder.progressBar.visibility = View.VISIBLE
holder.checkIcon.visibility = View.INVISIBLE
val r0 = Random.nextInt(0,3)
val r1 = Random.nextInt(1,9)
// 模拟加载完成
holder.itemView.postDelayed({
holder.progressBar.visibility = View.INVISIBLE
holder.checkIcon.visibility = View.VISIBLE
},r0 * 1000L + r1 * 100L)
// } else {
// holder.progressBar.visibility = View.GONE
// holder.checkIcon.visibility = View.VISIBLE
// }
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textView: TextView = itemView.findViewById(R.id.tvRoadItemTip)
var progressBar: ProgressBar = itemView.findViewById(R.id.pbRoadItemTip)
var checkIcon: ImageView = itemView.findViewById(R.id.ivRoadItemTip)
}
}

View File

@@ -0,0 +1,185 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean
import android.os.CountDownTimer
import android.util.Log
import com.mogo.eagle.core.data.v2x.RoadV2NEventType
import kotlin.math.floor
sealed class AIMessage(
open val id: String,
open val title: String,
open val tts: String = "",
val timestamp: Long = System.currentTimeMillis(),
var showTimestamp: Boolean = false
) {
companion object {
const val TYPE_SCAN = 0
const val TYPE_EVENT = 1
const val TYPE_LIGHT = 3
const val TYPE_SPEED = 4
const val TYPE_WARNING = 5
const val TYPE_PNC_ACTION = 6
const val TYPE_ROAD_V2N = 7
const val TYPE_ROAD_CROSS = 8
const val TYPE_AUTOMATIC_EXPLORATION = 9
const val TYPE_NDE = 11
}
data class Scan(
override val id: String,
override val title: String,
val pictureUrl: String = "",
var showScanFlag: Boolean = false
) : AIMessage(id, title)
data class Event(
override val id: String,
override val title: String,
override val tts: String = "",
val position: String = "",
val distance: String = "",
val range: String = "",
val time: String = "",
val pictureUrl: String = "",
val videoUrl: String = "",
var showScanFlag: Boolean = false
) : AIMessage(id, title, tts)
data class QA(
override val id: String,
override val title: String,
override val tts: String = "",
val question: String,
val answer: String,
var state: QuestionState = QuestionState.UNDERSTAND,
val pictureUrl: String = "",
var pictureUrlList: List<String> = listOf(),
val videoUrl: String = "",
) : AIMessage(id, title, tts) {
enum class QuestionState(val code: Int) {
UNDERSTAND(1),
ANALYZE(2),
ANSWER(3),
FINISH(4),
ERROR(-1),
}
}
data class Light(
override val id: String,
override val title: String,
override val tts: String = "",
var seconds: Int,
val status: Int
) : AIMessage(id, title, tts) {
private var countDownTimer: CountDownTimer? = null
private var listener: OnCountdownUpdateListener? = null
fun startCountdown(millisInFuture: Long, countDownInternal: Long) {
countDownTimer?.cancel()
countDownTimer = object : CountDownTimer(millisInFuture, countDownInternal) {
override fun onTick(millisUntilFinished: Long) {
//倒计时开始
Log.d(
"StartOrSlowDownTip",
"millisUntilFinished = $millisUntilFinished, countDownInternal = $countDownInternal"
)
val cd = millisUntilFinished / 1000.0
// val split = String.format("%.2f", cd).split(".")
seconds = floor(cd).toInt()
listener?.onCountdownUpdate()
}
override fun onFinish() {
//倒计时完成
seconds = 0
listener?.onCountdownFinish()
}
}
countDownTimer?.start()
}
fun stopCountdown() {
countDownTimer?.cancel()
}
fun setOnCountdownUpdateListener(listener: OnCountdownUpdateListener) {
this.listener = listener
}
interface OnCountdownUpdateListener {
fun onCountdownUpdate()
fun onCountdownFinish()
}
}
data class Speed(
override val id: String,
override val title: String,
override val tts: String = "",
val speedMax: Int,
val speedMin: Int,
) : AIMessage(id, title, tts)
data class Warning(
override val id: String,
override val title: String,
override val tts: String = "",
) : AIMessage(id, title, tts)
data class PNCAction(
override val id: String,
override val title: String,
var actionDesc: String,
var timeStamp: Long = 0, //事件发生事件戳
): AIMessage(id,title)
data class RoadV2NEvent(
override val id: String,
override val title: String,
override val tts: String = "", //TTS的文案
var eventType: RoadV2NEventType, //事件弹框类型
var timeStamp: Long = 0, //事件发生事件戳
var iconResId: Int, //事件icon res id
var isNeedTTS: Boolean = false, //事件文案是否需要同步tts
var contentImageUrl: String, // Image 类型时图片 url
var cameraIp: String, // 路侧camera ip,用于请求获取拉流地址
var lon: Double, //事件坐标-经度
var lat: Double, //事件坐标-纬度
): AIMessage(id,title,tts)
data class RoadCrossRoam(
override val id: String,
override val title: String
): AIMessage(id,title)
data class AutomaticExploration(
override val id: String,
override val title: String
): AIMessage(id,title)
data class EvaluateData(
override val id: String,
override val title: String,
var isFirst:Boolean = true
): AIMessage(id,title)
data class NDEData(
override val id: String,
override val title: String,
var desc: String,
var roadList: List<RoadMsg>
): AIMessage(id,title)
data class RoadMsg(
var arrowType: Int, // 车道类型如直行201(详情参考文件message_pad.proto)
var laneNum: Int,// 车道号
var isRecommend: Boolean,// 是否是推荐车道
var isCheLong: Boolean// 是否有车龙,代表拥堵、行驶缓慢
)
}

View File

@@ -0,0 +1,3 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean
data class ListenUIState(val show: Boolean, val text: String, val showTips: Boolean = false)

View File

@@ -0,0 +1,118 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.data
import android.os.CountDownTimer
import androidx.lifecycle.ViewModel
import com.mogo.eagle.core.data.msgbox.MsgBoxBean
import com.mogo.eagle.core.data.msgbox.MsgBoxType
import com.mogo.eagle.core.data.msgbox.MsgCategory
import com.mogo.eagle.core.function.api.datacenter.msgbox.IMsgBoxListener
import com.mogo.eagle.core.function.call.msgbox.CallerMsgBoxListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.och.shuttle.weaknet.passenger.callback.ICommonCallback
import com.mogo.och.shuttle.weaknet.passenger.model.CommonModel
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.AIMessageManager
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
/**
* 自动探查
*/
class AutomaticExplorationViewModel: ViewModel(), IMsgBoxListener, ICommonCallback {
companion object{
private const val TAG = "AutomaticExplorationViewModel"
private const val EXPLORATION_SHOW_TIME = 300000L //距离用户在触发上一次事件播报的时间5分钟后自动触发常规道路情况检测
}
private var showViewTimer: CountDownTimer?= null //展示自动探查倒计时
private var isCountingDown: Boolean = false //是否处于倒计时中
private var hasOrder: Boolean = false // 车当前是否有订单
fun init(){
CommonModel.setRouteLineInfoCallback(TAG,this)
CallerMsgBoxListenerManager.addListener(TAG,this)
}
override fun onCleared() {
super.onCleared()
CommonModel.setRouteLineInfoCallback(TAG,null)
CallerMsgBoxListenerManager.removeListener(TAG)
}
override fun showNoTaskView(isTrue: Boolean) {
super.showNoTaskView(isTrue)
if(isTrue){
cancelTimer()
currentOrderStatus(false)
}else{
startShowTimer()
currentOrderStatus(true)
}
}
override fun onDataChanged(category: MsgCategory, msgBoxList: MsgBoxBean) {
if(category == MsgCategory.NOTICE){
if(msgBoxList.type == MsgBoxType.V2X){
//重置倒计时时长
cancelTimer()
if(hasOrder){
startShowTimer()
}
}
}
}
/**
* 取消倒计时
*/
private fun cancelTimer(){
CallerLogger.d(TAG,"cancelTimer")
showViewTimer?.cancel()
showViewTimer = null
isCountingDown = false
}
/**
* 开始倒计时
*/
private fun startShowTimer(){
CallerLogger.d(TAG,"startShowTimer")
if(!isCountingDown){
ThreadUtils.runOnUiThread {
if(showViewTimer == null){
showViewTimer = object: CountDownTimer(EXPLORATION_SHOW_TIME,EXPLORATION_SHOW_TIME){
override fun onTick(millisUntilFinished: Long) {
CallerLogger.d(TAG,"倒计时+:${millisUntilFinished}")
}
override fun onFinish() {
if(hasOrder){
showAutoExploration()
}
isCountingDown = false
}
}
}
isCountingDown = true
showViewTimer?.start()
}
}
}
/**
* 设置当前订单状态
* @param orderStatus true有订单false没有订单
*/
private fun currentOrderStatus(orderStatus: Boolean){
hasOrder = orderStatus
}
private fun showAutoExploration(){
val automaticExploration = AIMessage.AutomaticExploration(System.currentTimeMillis().toString(),"")
AIMessageManager.post(automaticExploration)
}
}

View File

@@ -0,0 +1,87 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.data
import androidx.lifecycle.ViewModel
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.AIMessageManager
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
import mogo.telematics.pad.MessagePad.TrackedObject
/**
* 车龙信息
*/
class NDEViewModel: ViewModel(), IMoGoAutopilotIdentifyListener {
companion object{
private const val TAG = "NDEViewModel"
}
private var lastMap2 = HashMap<Int, String>()
private var lastTime = 0L
fun init(){
CallerAutopilotIdentifyListenerManager.addListener(TAG, this)
}
override fun onAutopilotIdentifyDataUpdate(trafficData: List<TrackedObject>?){
super.onAutopilotIdentifyDataUpdate(trafficData)
handleCheLong(trafficData)
}
private fun handleCheLong(trafficData: List<TrackedObject>?) {
var hasCheLong = false
var isNewData = false
val roadMsgList = ArrayList<AIMessage.RoadMsg>()
val curMap = HashMap<Int, String>()
if (lastTime > 0 && System.currentTimeMillis() - lastTime > 60000) {
lastMap2.clear()// 清除上次车龙事件的缓存
}
var lastLocStr: String? = ""
trafficData?.forEach { obj ->
if (obj.type == 707) {// 当前方向所有车道
if (obj.polygonCount > 1) {// 当次数据有车龙事件
lastLocStr = lastMap2[obj.laneNum]
// 1. 上次无车龙但此次有车龙,认为是新车龙事件
// 2. 两次都有车龙,则判断车龙的长度是否相同
if (lastLocStr == null || lastLocStr == "0" || lastLocStr != "${obj.polygonList[0]}-${obj.polygonList[obj.polygonCount - 1]}") {
isNewData = true
}
hasCheLong = true
// key: 车道号value: 非0代表有车龙且首、尾两个点可计算车龙长度
curMap[obj.laneNum] =
"${obj.polygonList[0]}-${obj.polygonList[obj.polygonCount - 1]}"
} else {// 当次没有车龙
if (lastMap2[obj.laneNum] != null && lastMap2[obj.laneNum] != "0") {// 3. 上次有车龙,这次无车龙
isNewData = true
}
// key: 车道号value: 0代表无车龙
curMap[obj.laneNum] = "0"
}
// 保存所有车道信息
roadMsgList.add(
AIMessage.RoadMsg(
obj.arrowType,
laneNum = obj.laneNum,
isRecommend = obj.suggestedLanes,
isCheLong = obj.polygonCount > 1
)
)
}
}
if (isNewData) {
// 清除上次车道信息
lastMap2.clear()
if (hasCheLong) {
// 缓存当次车龙事件所有车道信息
lastMap2.putAll(curMap)
lastTime = System.currentTimeMillis()
val sortedList = roadMsgList.sortedWith(compareByDescending { it.laneNum })
val ndeEvent = AIMessage.NDEData(System.currentTimeMillis().toString(),"路口车龙","前方路口有车龙",sortedList)
AIMessageManager.post(ndeEvent)
}
}
}
}

View File

@@ -0,0 +1,259 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.data
import androidx.lifecycle.ViewModel
import com.mogo.eagle.core.data.autopilot.pnc.PncActionsHelper
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotPlanningActionsListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningActionsListenerManager
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import com.mogo.och.common.module.manager.loop.BizLoopManager
import com.mogo.och.data.bean.BusStationBean
import com.mogo.och.shuttle.weaknet.passenger.callback.ICommonCallback
import com.mogo.och.shuttle.weaknet.passenger.model.CommonModel
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.AIMessageManager
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
import mogo.telematics.pad.MessagePad
class PNCActionsViewModel: ViewModel(), IMoGoAutopilotPlanningActionsListener, ICommonCallback {
companion object{
private const val TAG = "PNCActionsViewModel"
}
private var currentAction = ""
private var currentStation:BusStationBean?=null
fun init(){
CallerPlanningActionsListenerManager.addListener(TAG, this)
CommonModel.setRouteLineInfoCallback(TAG,this)
}
override fun onCleared() {
super.onCleared()
CallerPlanningActionsListenerManager.removeListener(TAG)
CommonModel.setRouteLineInfoCallback(TAG,null)
}
override fun updateStationsInfo(
stations: MutableList<BusStationBean>?,
currentStationIndex: Int,
isArrived: Boolean
) {
try {
currentStation = stations?.get(currentStationIndex)
}catch (e:Exception){
OchChainLogManager.writeChainLogError("PNCActionsViewModel 设置错误","${e.message}")
}
}
override fun showNoTaskView(isTrue: Boolean) {
super.showNoTaskView(isTrue)
if(isTrue){
currentStation = null
}
}
override fun pncActions(planningActionMsg: MessagePad.PlanningActionMsg) {
try {
BizLoopManager.runInMainThread {
if (CallerAutoPilotStatusListenerManager.getState() == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING) {
var actions: String? = null
planningActionMsg.actionMsg?.let {
try {
actions = PncActionsHelper.getAction(
it.drivingState.number,
it.drivingAction.number
)
} catch (e: Exception) {
e.printStackTrace()
}
}
planningActionMsg.v2NActionMsgList?.forEach { v2nAction ->
actions = PncActionsHelper.getAction(
v2nAction.drivingState.number,
v2nAction.drivingAction.number
)
}
// update view
actions?.let {
if(it.isNotEmpty() && it != currentAction){
currentAction = it
val title = getActionTitle(it)
if(title.isNotEmpty()){
val desc = getActionDesc(title)
val action = AIMessage.PNCAction(it+System.currentTimeMillis(),title,desc,System.currentTimeMillis())
AIMessageManager.post(action)
}
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun getActionTitle(pncAction: String): String{
return when(pncAction){
"正在进站"->{
"车辆进站"
}
"等待进站"->{
"车辆等待进站"
}
"正在出站"->{
"车辆出站"
}
"等待出站"->{
"车辆等待出站"
}
"正在向左变道"->{
"车辆向左变道"
}
"正在向右变道"->{
"车辆向右变道"
}
"正在完成变道"->{
"车辆完成变道"
}
"正在绕过障碍物"->{
"车辆正在绕过前方障碍物"
}
"正在向左绕行避让前方静止障碍物"->{
"车辆正在绕过前方障碍物"
}
"正在向右绕行避让前方静止障碍物"->{
"车辆正在绕过前方障碍物"
}
"正在避让障碍物"->{
"车辆正在避让前方障碍物"
}
"正在向左变道避让前方静止障碍物"->{
"车辆正在避让前方障碍物"
}
"正在向右变道避让前方静止障碍物"->{
"车辆正在避让前方障碍物"
}
"正在等红灯"->{
"路口等红灯"
}
"正在向左变道避让前方道路施工"->{
"车辆正在变道避让前方道路施工"
}
"正在向右变道避让前方道路施工"->{
"车辆正在变道避让前方道路施工"
}
"正在向左绕行避让前方道路施工"->{
"车辆正在绕行避让前方道路施工"
}
"正在向右绕行避让前方道路施工"->{
"车辆正在绕行避让前方道路施工"
}
"正在向左变道避让前方道路事故"->{
"车辆正在变道避让前方道路事故"
}
"正在向右变道避让前方道路事故"->{
"车辆正在变道避让前方道路事故"
}
"正在向左绕行避让前方道路事故"->{
"车辆正在绕行避让前方道路事故"
}
"正在向右绕行避让前方道路事故"->{
"车辆正在绕行避让前方道路事故"
}
"正在跟随车辆行驶"->{
"车辆正在跟车通行"
}
"正在跟车行驶"->{
"车辆正在跟车通行"
}
"正在向左变道避让前方车龙"->{
"车辆正在绕行前方车龙"
}
"正在向右变道避让前方车龙"->{
"车辆正在绕行前方车龙"
}
"正在使用云端规划通过路口"->{
"车辆正在使用云端轨迹通行"
}
"正在避让后方来车"->{
"车辆正在避让后方来车"
}
else->{
""
}
}
}
private fun getActionDesc(action: String): String{
return when(action){
"车辆进站"->{
"前方即将到达${CommonModel.routesResult}" +
"车辆正在规划减速并进站停靠,请安心等待车辆停稳再下车哦~"
}
"车辆等待进站"->{
"车辆待环境安全后进站,耐心等几秒,安全比赶路更重要~"
}
"车辆出站"->{
"欢迎乘坐MOGO RoboTaxi车辆正在规划出站坐稳扶好哦我们出发啦小智持续守护您的行程。"
}
"车辆等待出站"->{
"车辆待环境安全后出站,耐心等几秒,安全比赶路更重要~"
}
"车辆向左变道"->{
"确认环境安全,车辆正在规划平稳向左变道,同时持续监测周边交通参与者动向,放心交给我们吧!"
}
"车辆向右变道"->{
"确认环境安全,车辆正在规划平稳向右变道,同时持续监测周边交通参与者动向,放心交给我们吧!"
}
"车辆完成变道"->{
"变道完成啦,继续前进!小智持续守护您的行程。"
}
"车辆正在绕过前方障碍物"->{
"发现前方障碍物,车辆正在规划平稳变道,即将画出一条完美弧线~"
}
"车辆正在避让前方障碍物"->{
"发现前方障碍物,车辆正在规划平稳避让,诠释优雅与丝滑~"
}
"车辆完成绕障"->{
"绕障完成啦,继续前进!小智持续守护您的行程。"
}
"路口等红灯"->{
"车辆正在路口等红灯,可以安心放空望望窗外~小智一直在您身边哦!"
}
"车辆正在变道避让前方道路施工"->{
"车辆正在提前规划变道避让前方道路施工,稳稳的很安心~您已体验到车路云一体化协同应用场景,是当之无愧的先锋体验官!"
}
"车辆正在绕行避让前方道路施工"->{
"车辆正在提前规划绕行避让前方道路施工,稳稳的很安心~您已体验到车路云一体化协同应用场景,是当之无愧的先锋体验官!"
}
"车辆正在变道避让前方道路事故"->{
"车辆正在提前规划变道避让前方道路事故,放心看我表现吧!您已体验到车路云一体化协同应用场景,小智为您欢呼!"
}
"车辆正在绕行避让前方道路事故"->{
"车辆正在提前规划绕行避让前方道路事故,放心看我表现吧!您已体验到车路云一体化协同应用场景,小智为您欢呼!"
}
"车辆正在跟车通行"->{
"车辆正在跟随前车通行舒适度MAX您已体验到车路云一体化协同应用场景超越全国99%的乘客!"
}
"车辆正在绕行前方车龙"->{
"车辆正在提前规划变道避让路口车龙舒适度MAX。您已体验到车路云一体化协同应用场景超越全国99%的乘客!"
}
"车辆正在使用云端轨迹通行"->{
"前方智慧路口内有障碍物车辆正在使用云端规划轨迹通行。您已体验到车路云一体化协同应用场景超越全国99%的乘客!"
}
"车辆正在避让后方来车"->{
"车辆正在避让后方来车,耐心等几秒,安全比赶路更重要~"
}
else->{
""
}
}
}
}

View File

@@ -0,0 +1,73 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.data
import android.content.Context
import androidx.lifecycle.ViewModel
import com.mogo.commons.voice.AIAssist
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.function.api.map.road.IMoGoMapRoadListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerServicesEventManager
import com.mogo.eagle.core.function.call.hmi.CallerHmiViewControlListenerManager
import com.mogo.eagle.core.function.call.map.CallerMapIdentifyManager
import com.mogo.eagle.core.function.call.map.CallerMapRoadListenerManager
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.AIMessageManager
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
class RoadCrossRoamViewModel: ViewModel(), IMoGoMapRoadListener {
companion object{
const val TAG = "RoadCrossRoamViewModel"
}
private lateinit var mContext: Context
fun init(context: Context){
CallerMapRoadListenerManager.addListener(TAG, this)
mContext = context
}
override fun onCrossDevice(trigger: Boolean) {
super.onCrossDevice(trigger)
if(trigger){
show()
}
}
private fun show(){
// 没有路线不做提示
if (CallerAutoPilotStatusListenerManager.getLineId() == 0L) {
CallerLogger.d(TAG, "没有路线不做提示")
return
}
// 处于漫游模式下不做处理
if (CallerMapIdentifyManager.roam.second) {
if (CallerMapIdentifyManager.roam.first != TAG) {
ToastUtils.showLong("正在漫游中,不展示路口漫游")
}
CallerLogger.d(TAG, "正在漫游中,不展示路口漫游")
return
}
// 首页被遮挡不做提示
if (!CallerHmiViewControlListenerManager.getMainPageVisible()) {
CallerLogger.d(TAG, "attachView return , mainPageVisible is false")
return
}
// 没有路侧设备,不做处理
CallerLogger.d(TAG, "命中attachView")
val cross = CallerMapRoadListenerManager.getCrossEndInfo()
if (cross.isNullOrEmpty()) {
CallerLogger.d(TAG, "未触发路口ID:$cross")
return
}
if (AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)){
val disStr = "为您提供路口全息影像,助力出行"
AIAssist.getInstance(mContext).speakTTSVoiceWithLevel(disStr, AIAssist.NEW_LEVEL_2)
}
CallerServicesEventManager.updateServicesNum(CallerServicesEventManager.ServiceType.ROAD)
AIMessageManager.post(AIMessage.RoadCrossRoam(System.currentTimeMillis().toString(),""))
}
}

View File

@@ -0,0 +1,74 @@
package com.mogo.och.shuttle.weaknet.passenger.ui.mind.data
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.lifecycleScope
import com.mogo.commons.utils.MogoAnalyticUtils
import com.mogo.eagle.core.data.v2x.RoadV2NEventWindowBean
import com.mogo.eagle.core.function.api.hmi.v2n.IRoadV2NEventWindowListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.hmi.CallerHmiViewControlListenerManager
import com.mogo.eagle.core.function.call.hmi.CallerRoadV2NEventWindowListenerManager
import com.mogo.eagle.core.function.hmi.ui.utils.HmiActionLog
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.AIMessageManager
import com.mogo.och.shuttle.weaknet.passenger.ui.mind.bean.AIMessage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class RoadV2NEventViewModel: ViewModel(), IRoadV2NEventWindowListener {
companion object{
const val TAG = "RoadV2NEventViewModel"
const val ANALYTICS_KEY = "hmi_road_event_window_view"
fun trackEvent(msg: String) {
ProcessLifecycleOwner.get().lifecycleScope.launch(Dispatchers.IO) {
val map: MutableMap<String, Any> = HashMap()
map["msg"] = msg
MogoAnalyticUtils.track(
ANALYTICS_KEY,
map
)
HmiActionLog.hmiAction(TAG, msg)
}
CallerLogger.i(TAG, msg)
}
}
fun init(){
CallerRoadV2NEventWindowListenerManager.addListener(TAG, this)
}
override fun show(dataBean: RoadV2NEventWindowBean) {
trackEvent("show --> $dataBean")
val canShowV2NEventWindowView = CallerHmiViewControlListenerManager.getMainPageVisible()
if (!canShowV2NEventWindowView) {
trackEvent("show --> 当前不在高精地图页面,跳过")
return
}
val lineId = CallerAutoPilotStatusListenerManager.getLineId()
if (lineId <= 0) {
trackEvent("show --> 当前无订单,跳过")
return
}
val event = AIMessage.RoadV2NEvent(
dataBean.eventId,
dataBean.hintStr,
"",
dataBean.eventType,
dataBean.timestamp,
dataBean.iconResId,
false,
dataBean.contentImageUrl,
dataBean.cameraIp,
dataBean.lon,
dataBean.lat
)
AIMessageManager.post(event)
}
override fun dismiss(eventId: String) {
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient android:startColor="@color/b2_D7F1FF" android:endColor="@color/common_color_00000000" android:angle="-90"/>
<corners android:radius="@dimen/dp_18"/>
</shape>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#DDDDDD" />
</shape>
</item>
<item>
<inset android:inset="100dp">
<bitmap android:src="@drawable/icon_image_error" />
</inset>
</item>
</layer-list>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#DDDDDD" />
</shape>
</item>
<item>
<inset android:inset="100dp">
<bitmap android:src="@drawable/icon_image_holder" />
</inset>
</item>
</layer-list>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/clRoadLayout"
android:layout_width="@dimen/dp_90"
android:layout_height="@dimen/dp_120"
android:minWidth="@dimen/dp_90"
android:maxWidth="@dimen/dp_180"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<ImageView
android:id="@+id/ivRoadType"
android:layout_width="@dimen/dp_55"
android:layout_height="@dimen/dp_55"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/nde_road_icon"
/>
<TextView
android:id="@+id/tvRoadStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/ivRoadType"
android:textSize="@dimen/sp_22"
android:layout_marginTop="@dimen/dp_12"
/>
<View
android:id="@+id/viewDivider"
android:layout_width="@dimen/dp_4"
android:layout_height="@dimen/dp_0"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:background="@color/msg_nde_road_divider"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/dp_373"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/ivScan"
android:layout_width="@dimen/dp_18"
android:layout_height="@dimen/dp_18"
android:layout_marginStart="@dimen/dp_18"
android:layout_marginTop="@dimen/dp_28"
android:src="@drawable/m2_mind_item_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvExplorationTitle"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_11"
android:layout_marginTop="@dimen/dp_19"
android:layout_marginEnd="@dimen/dp_13"
android:ellipsize="end"
android:gravity="start|top"
android:textColor="@color/b2_394047"
android:textSize="@dimen/dp_20"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/ivScan"
app:layout_constraintTop_toTopOf="parent"
android:text="@string/exploration_title" />
<androidx.recyclerview.widget.RecyclerView
android:id="@id/rvExplorationList"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_300"
android:layout_marginTop="@dimen/dp_14"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/dp_29"
app:layout_constraintLeft_toLeftOf="@id/tvExplorationTitle"
app:layout_constraintTop_toBottomOf="@id/tvExplorationTitle" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/dp_373"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/ivNdeImage"
android:layout_width="@dimen/dp_18"
android:layout_height="@dimen/dp_18"
android:layout_marginStart="@dimen/dp_18"
android:layout_marginTop="@dimen/dp_28"
android:src="@drawable/m2_mind_item_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvNdeHintContent"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_11"
android:layout_marginTop="@dimen/dp_19"
android:layout_marginEnd="@dimen/dp_13"
android:ellipsize="end"
android:gravity="start|top"
android:textColor="@color/b2_394047"
android:textSize="@dimen/dp_20"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/ivNdeImage"
app:layout_constraintTop_toTopOf="parent"
tools:text="前方路口有车龙" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvNdeList"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_140"
android:layout_marginTop="@dimen/dp_15"
android:layout_marginEnd="@dimen/dp_13"
android:layout_marginStart="@dimen/dp_11"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/ivNdeImage"
app:layout_constraintTop_toBottomOf="@id/ivNdeImage" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/dp_373"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/ivPncImage"
android:layout_width="@dimen/dp_18"
android:layout_height="@dimen/dp_18"
android:layout_marginStart="@dimen/dp_18"
android:layout_marginTop="@dimen/dp_28"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/m2_mind_item_icon" />
<TextView
android:id="@+id/tvPNCHintContent"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_13"
android:layout_marginTop="@dimen/dp_19"
android:layout_marginStart="@dimen/dp_11"
android:ellipsize="end"
android:gravity="start|top"
android:textColor="@color/b2_394047"
android:layout_marginBottom="@dimen/dp_29"
android:textSize="@dimen/dp_20"
app:layout_constraintStart_toEndOf="@+id/ivPncImage"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="前方100米有道路施工施工长度100米影响第1、2车道通行。正在向第3车道变道避让前方施工区域" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/dp_373"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/ivZhiRoadRoamView"
android:layout_width="@dimen/dp_18"
android:layout_height="@dimen/dp_18"
android:layout_marginStart="@dimen/dp_18"
android:layout_marginTop="@dimen/dp_28"
android:src="@drawable/m2_mind_item_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvRoadRoamTitle"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_11"
android:layout_marginTop="@dimen/dp_19"
android:layout_marginEnd="@dimen/dp_13"
android:ellipsize="end"
android:gravity="start|top"
android:textColor="@color/b2_394047"
android:textSize="@dimen/dp_20"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/ivZhiRoadRoamView"
app:layout_constraintTop_toTopOf="parent"
android:text="@string/road_cross_roam_tip" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lvRoadCrossRoamTip"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_29"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="@+id/tvRoadRoamTitle"
app:layout_constraintRight_toRightOf="@+id/tvRoadRoamTitle"
app:layout_constraintTop_toBottomOf="@+id/tvRoadRoamTitle" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/containerEventContent"
android:layout_width="@dimen/dp_373"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/ivV2XImage"
android:layout_width="@dimen/dp_18"
android:layout_height="@dimen/dp_18"
android:layout_marginStart="@dimen/dp_18"
android:layout_marginTop="@dimen/dp_28"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/m2_mind_item_icon" />
<TextView
android:id="@+id/tvV2XHintContent"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_13"
android:layout_marginTop="@dimen/dp_19"
android:layout_marginStart="@dimen/dp_11"
android:ellipsize="end"
android:gravity="start|top"
android:textColor="@color/b2_394047"
android:textSize="@dimen/dp_20"
app:layout_constraintStart_toEndOf="@+id/ivV2XImage"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="前方100米有道路施工施工长度100米影响第1、2车道通行。正在向第3车道变道避让前方施工区域" />
<FrameLayout
android:id="@+id/containerImageAndLiveVideo"
android:layout_width="@dimen/dp_321"
android:layout_height="@dimen/dp_207"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_29"
app:layout_constraintStart_toStartOf="@+id/tvV2XHintContent"
app:layout_constraintEnd_toEndOf="@+id/tvV2XHintContent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvV2XHintContent">
<com.mogo.eagle.core.function.hmi.ui.v2n.RoadV2NEventLivePlayView
android:id="@+id/livePlayView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" />
<com.mogo.eagle.core.utilcode.mogo.imageloader.MogoImageView
android:id="@+id/contentImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:visibility="visible"
app:miv_radius="@dimen/dp_16"
app:miv_shape="round"
tools:src="@drawable/bg_v2x_bus_station" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="@dimen/dp_6"
android:layout_marginBottom="@dimen/dp_6"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/ivExplorationLoading"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
android:src="@drawable/icon_exploration_loading_p"
android:contentDescription="@string/exploration_loading"
android:layout_gravity="center_vertical"
/>
<TextView
android:id="@+id/tvExplorationContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/sp_19"
android:textColor="@color/auto_exploration_content_p"
android:layout_marginStart="@dimen/dp_7"
android:layout_gravity="center_vertical"
/>
</LinearLayout>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_marginTop="@dimen/dp_6"
android:layout_marginBottom="@dimen/dp_6"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/pbRoadItemTip"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
android:indeterminateBehavior="repeat"
android:indeterminateDrawable="@drawable/anim_road_cross_progress"
android:indeterminateOnly="true"
android:visibility="visible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ivRoadItemTip"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
android:scaleType="fitXY"
android:src="@drawable/icon_road_roam_tip"
android:visibility="visible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tvRoadItemTip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="@dimen/sp_19"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="@id/ivRoadItemTip"
android:layout_marginStart="@dimen/dp_7"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"
android:background="@drawable/b2_mind_bg"
android:layout_width="@dimen/dp_411"
android:layout_height="@dimen/dp_650">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_mind_list"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/dp_17"
android:layout_marginStart="@dimen/dp_19"
android:layout_marginEnd="@dimen/dp_19"
android:layout_marginBottom="@dimen/dp_21"
/>
<androidx.appcompat.widget.AppCompatImageView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/b2_mind_top_shader"
android:layout_marginTop="@dimen/dp_17"
android:layout_width="@dimen/dp_373"
android:layout_height="@dimen/dp_70"/>
<androidx.appcompat.widget.AppCompatTextView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="@dimen/dp_34"
android:text="MogoMind"
android:textSize="@dimen/dp_24"
android:textColor="@color/b2_394047"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/test1"
android:text="测试1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/test2"
android:text="测试2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/test3"
android:text="测试3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</merge>

View File

@@ -62,9 +62,9 @@
<androidx.appcompat.widget.AppCompatImageView
app:layout_constraintBottom_toBottomOf="parent"
android:src="@drawable/m2_bottom_bg"
android:src="@drawable/m2_map_video_bg"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="1290dp"/>

View File

@@ -4,40 +4,99 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 全览地图带站点-->
<com.mogo.eagle.core.function.view.OverMapView
android:id="@+id/overMapView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:visibility="gone"
app:carDrawable="@drawable/shuttle_p_m2_map_car_icon"
app:isClearArrived="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintWidth_percent="0.6"
app:mapStyleExtraPath="@string/shuttle_p_m2_over_map_style_extra_path"
app:mapStylePath="@string/shuttle_p_m2_over_map_style_path"
app:resetDrawableMarginBottom="@dimen/dp_54"
app:resetDrawableMarginRight="@dimen/dp_34"
app:compassDrawable="@drawable/shuttle_p_m2_amap_custom_corner"
app:arrivedDrawable="@drawable/shuttle_p_m2_amap_arrived_road"
app:unArrivedDrawable="@drawable/shuttle_p_m2_amap_arriving_road"
app:mapTilt="0"
app:leftPadding="200"
app:topPadding="150"
app:bottomPadding="50" />
<!-- 高精地图 -->
<com.mogo.och.shuttle.weaknet.passenger.ui.widget.OchMapBizPView
android:id="@+id/mHomeView"
app:styleMode="MAP_STYLE_DAY_VR"
app:carPosition="-1"
app:default_perspective="MAP_STYLE_VR_ERHAI_B2"
app:isWeatherEnable="false"
app:locationIcon3DRes="@raw/m2"
<androidx.constraintlayout.widget.ConstraintLayout
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="@dimen/dp_996"
android:layout_height="@dimen/dp_650"/>
android:layout_height="@dimen/dp_650">
<!-- 高精地图 -->
<com.mogo.och.shuttle.weaknet.passenger.ui.widget.OchMapBizPView
android:id="@+id/mHomeView"
app:styleMode="MAP_STYLE_DAY_VR"
app:isWeatherEnable="false"
app:locationIcon3DRes="@raw/m2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="-170dp"
android:layout_width="@dimen/dp_996"
android:layout_height="@dimen/dp_650"/>
<!--虚化阴影-->
<androidx.appcompat.widget.AppCompatImageView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:src="@drawable/m2_map_right_top"
android:layout_width="@dimen/dp_430"
android:layout_height="@dimen/dp_650"/>
<androidx.constraintlayout.widget.ConstraintLayout
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
android:background="@drawable/b2_aip_bg"
android:layout_marginTop="@dimen/dp_3"
android:layout_marginEnd="@dimen/dp_1"
android:layout_width="@dimen/dp_411"
android:layout_height="@dimen/dp_332"/>
<androidx.constraintlayout.widget.ConstraintLayout
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
android:background="@drawable/b2_prediction_bg"
android:layout_marginEnd="@dimen/dp_1"
android:layout_marginBottom="@dimen/dp_m_1"
android:layout_width="@dimen/dp_411"
android:layout_height="@dimen/dp_332"/>
<com.mogo.och.shuttle.weaknet.passenger.ui.mind.MindView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@drawable/b2_mind_bg"
android:layout_width="@dimen/dp_411"
android:layout_height="@dimen/dp_650"/>
<!-- 全览地图带站点-->
<com.mogo.eagle.core.function.view.OverMapView
android:id="@+id/overMapView"
android:layout_width="@dimen/dp_996"
android:layout_height="@dimen/dp_650"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:carDrawable="@drawable/shuttle_p_m2_map_car_icon"
app:isClearArrived="false"
app:mapStyleExtraPath="@string/shuttle_p_m2_over_map_style_extra_path"
app:mapStylePath="@string/shuttle_p_m2_over_map_style_path"
app:compassDrawable="@drawable/shuttle_p_m2_amap_custom_corner"
app:arrivedDrawable="@drawable/shuttle_p_m2_amap_arrived_road"
app:unArrivedDrawable="@drawable/shuttle_p_m2_amap_arriving_road"
app:mapTilt="0" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/leftEndGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.666" />
<!-- <View-->
<!-- android:background="@drawable/b2_map_top_shader"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- android:layout_width="@dimen/dp_996"-->
<!-- android:layout_height="@dimen/dp_200"/>-->
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -27,4 +27,7 @@
<color name="m2_516582">#516582</color>
<color name="b2_95B1D6">#95B1D6</color>
<color name="b2_1F82FB">#1F82FB</color>
<color name="b2_D7F1FF">#D7F1FF</color>
<color name="b2_394047">#394047</color>
<color name="b2_BBC9D4">#BBC9D4</color>
</resources>