diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/OchRoundImageView.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/OchRoundImageView.kt
new file mode 100644
index 0000000000..2f86b5a86f
--- /dev/null
+++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/OchRoundImageView.kt
@@ -0,0 +1,241 @@
+package com.mogo.och.common.module.wigets
+
+/*
+ * Copyright WeiLianYang
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+import android.content.Context
+import android.graphics.*
+import android.util.AttributeSet
+import android.util.Log
+import androidx.annotation.ColorInt
+import androidx.appcompat.widget.AppCompatImageView
+import com.mogo.och.common.module.R
+
+
+/**
+ * author : WilliamYang
+ * date : 2022/9/18 14:54
+ * description : 可设置 圆角、外边框 的 ImageView
+ *
+ * 使用方式:
+ *
+ * 1. 使用 riv_radius 设置4个角均为圆角,且圆角值一样
+ *
+ * 2. 使用 riv_roundAsCircle 设置图片为圆形,使用 riv_radius 设置半径,当 riv_radius 未设置时,默认取宽高最小值的一半
+ *
+ * 3. 使用 riv_topLeft_radius, riv_topRight_radius, riv_bottomLeft_radius, riv_bottomRight_radius 设置4个圆角
+ *
+ * 4. 使用 riv_borderColor, riv_borderWidth 设置外边框颜色和宽度
+ *
+ *
+ */
+class OchRoundImageView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : AppCompatImageView(context, attrs, defStyleAttr) {
+
+ /** 绘制路径 **/
+ private val path = Path()
+
+ /** 绘制坐标 **/
+ private val rectF = RectF()
+
+ /** 圆角大小 **/
+ private var radius = 0f
+
+ /** 顶部左侧圆角大小 **/
+ private var topLeftRadius = 0f
+
+ /** 顶部右侧圆角大小 **/
+ private var topRightRadius = 0f
+
+ /** 底部左侧圆角大小 **/
+ private var bottomLeftRadius = 0f
+
+ /** 底部右侧圆角大小 **/
+ private var bottomRightRadius = 0f
+
+ /** 作为圆形图片使用 **/
+ private var roundAsCircle = false
+
+ /** 外边框颜色、宽度、画笔、路径、坐标 */
+ private var borderColor = 0
+ private var borderWidth = 0f
+ private val borderPaint: Paint?
+ private val borderPath = Path()
+ private val borderRectF = RectF()
+
+ init {
+ val ta = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView)
+
+ roundAsCircle = ta.getBoolean(R.styleable.RoundImageView_riv_roundAsCircle, false)
+ borderColor = ta.getColor(R.styleable.RoundImageView_riv_borderColor, Color.TRANSPARENT)
+ borderWidth = ta.getDimension(R.styleable.RoundImageView_riv_borderWidth, 0f)
+
+ radius = ta.getDimension(R.styleable.RoundImageView_riv_radius, 0f)
+
+ topLeftRadius = ta.getDimension(R.styleable.RoundImageView_riv_topLeft_radius, 0f)
+ topRightRadius = ta.getDimension(R.styleable.RoundImageView_riv_topRight_radius, 0f)
+ bottomLeftRadius = ta.getDimension(R.styleable.RoundImageView_riv_bottomLeft_radius, 0f)
+ bottomRightRadius = ta.getDimension(R.styleable.RoundImageView_riv_bottomRight_radius, 0f)
+
+ ta.recycle()
+
+ borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ updateBorderPaint()
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+
+ // 当作为圆形图片使用,且半径未设置时,半径将取宽高最小值的一半
+ if (roundAsCircle && radius <= 0f) {
+ radius = w.coerceAtMost(h) / 2f
+ }
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ // 当作为圆形图片使用,宽高值不同,取宽高的最小值作为宽和高
+ val widthSize = MeasureSpec.getSize(widthMeasureSpec)
+ val heightSize = MeasureSpec.getSize(heightMeasureSpec)
+ if (roundAsCircle && widthSize > 0 && heightSize > 0 && widthSize != heightSize) {
+ val size = widthSize.coerceAtMost(heightSize)
+ setMeasuredDimension(size, size)
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ val halfBorderWidth = borderWidth / 2
+ if (radius > 0 || topLeftRadius > 0 || topRightRadius > 0 || bottomLeftRadius > 0 || bottomRightRadius > 0) {
+ // 如果设置了圆角值
+ path.reset()
+ borderPath.reset()
+ if (roundAsCircle) {
+ path.addCircle(radius, radius, radius, Path.Direction.CW)
+ } else {
+ if (topLeftRadius == 0f) topLeftRadius = radius
+ if (topRightRadius == 0f) topRightRadius = radius
+ if (bottomLeftRadius == 0f) bottomLeftRadius = radius
+ if (bottomRightRadius == 0f) bottomRightRadius = radius
+
+ Log.d("RoundImageView", "onDraw: topLeftRadius=$topLeftRadius, topRightRadius=$topRightRadius, bottomLeftRadius=$bottomLeftRadius, bottomRightRadius=$bottomRightRadius")
+
+ val radii = floatArrayOf(
+ topLeftRadius, topLeftRadius, topRightRadius, topRightRadius,
+ bottomRightRadius, bottomRightRadius, bottomLeftRadius, bottomLeftRadius
+ )
+
+
+
+ borderRectF.set(
+ paddingLeft.toFloat() + halfBorderWidth, paddingTop.toFloat() + halfBorderWidth,
+ measuredWidth.toFloat() - paddingRight - halfBorderWidth,
+ measuredHeight.toFloat() - paddingBottom - halfBorderWidth
+ )
+
+ borderPath.addRoundRect(borderRectF, radii, Path.Direction.CW)
+
+ if (halfBorderWidth > 0) {
+ radii.forEachIndexed { index, f ->
+ if (f > 0) {
+ radii[index] = f + halfBorderWidth
+ }
+ }
+ }
+ rectF.set(
+ paddingLeft.toFloat(),
+ paddingTop.toFloat(),
+ measuredWidth.toFloat() - paddingRight,
+ measuredHeight.toFloat() - paddingBottom
+ )
+ path.addRoundRect(rectF, radii, Path.Direction.CW)
+ }
+
+ // 裁剪画布
+ canvas.clipPath(path)
+ }
+
+ super.onDraw(canvas)
+
+ if (borderWidth > 0 && borderPaint != null) {
+ if (roundAsCircle) {
+ canvas.drawCircle(radius, radius, radius - borderWidth / 2, borderPaint)
+ } else {
+ canvas.drawPath(borderPath, borderPaint)
+ }
+ }
+ }
+
+ private fun updateBorderPaint() {
+ borderPaint?.apply {
+ color = borderColor
+ strokeWidth = borderWidth
+ style = Paint.Style.STROKE
+ }
+ }
+
+ /**
+ * @param radius 圆角大小,当 asCircle 为 true 时,值作为圆形图片的半径,如果为0,则将取宽高最小值的一半
+ * @param borderWidth 外边框宽度
+ * @param borderColor 外边框颜色
+ * @param asCircle 作为圆形图片使用,默认 false
+ */
+ fun setRadiusAndBorder(
+ radius: Float,
+ borderWidth: Float = 0f,
+ @ColorInt borderColor: Int = 0,
+ asCircle: Boolean = false,
+ ) {
+ this.radius = radius
+ this.borderWidth = borderWidth
+ this.borderColor = borderColor
+ this.roundAsCircle = asCircle
+
+ updateBorderPaint()
+ }
+
+ /**
+ * @param topLeftRadius 顶部左侧圆角大小
+ * @param topRightRadius 顶部右侧圆角大小
+ * @param bottomLeftRadius 底部左侧圆角大小
+ * @param bottomRightRadius 底部右侧圆角大小
+ * @param borderWidth 外边框宽度
+ * @param borderColor 外边框颜色
+ */
+ fun setRadiusAndBorder(
+ topLeftRadius: Float = 0f,
+ topRightRadius: Float = 0f,
+ bottomLeftRadius: Float = 0f,
+ bottomRightRadius: Float = 0f,
+ borderWidth: Float = 0f,
+ @ColorInt borderColor: Int = 0
+ ) {
+ this.topLeftRadius = topLeftRadius
+ this.topRightRadius = topRightRadius
+ this.bottomLeftRadius = bottomLeftRadius
+ this.bottomRightRadius = bottomRightRadius
+ this.borderWidth = borderWidth
+ this.borderColor = borderColor
+
+ updateBorderPaint()
+ }
+
+}
\ No newline at end of file
diff --git a/OCH/common/common/src/main/res/values/attrs.xml b/OCH/common/common/src/main/res/values/attrs.xml
index cb8a13ade6..5b122d83ed 100644
--- a/OCH/common/common/src/main/res/values/attrs.xml
+++ b/OCH/common/common/src/main/res/values/attrs.xml
@@ -114,4 +114,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/build.gradle b/OCH/taxi/unmanned-passenger/build.gradle
index 06bcf2d47a..98611c69ab 100644
--- a/OCH/taxi/unmanned-passenger/build.gradle
+++ b/OCH/taxi/unmanned-passenger/build.gradle
@@ -73,6 +73,10 @@ dependencies {
implementation project(":OCH:common:common")
compileOnly project(":libraries:mogo-map")
implementation project(':core:mogo-core-res')
+
+ implementation project(":libraries:mogo-speech")
+ implementation 'io.github.youth5201314:banner:2.2.3'
+
// implementation project(':OCH:taxi:pcommon')
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIMessageManager.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIMessageManager.kt
new file mode 100644
index 0000000000..51fea51056
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIMessageManager.kt
@@ -0,0 +1,56 @@
+package com.mogo.och.unmanned.passenger.ui.aiview
+
+import com.mogo.och.unmanned.passenger.ui.aiview.bean.AIMessage
+import java.util.concurrent.CopyOnWriteArrayList
+
+
+object AIMessageManager {
+
+ // 使用 CopyOnWriteArrayList 来存储消息回调列表,保证线程安全
+ private val messageListeners: MutableList = 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)
+ }
+ }
+
+ /**
+ * 消息监听器接口。
+ */
+ interface AIMessageListener {
+ /**
+ * 当接收到消息时,会调用此方法。
+ *
+ * @param msg 接收到的消息。
+ */
+ fun onReceive(msg: AIMessage)
+ }
+}
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIViewModel.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIViewModel.kt
new file mode 100644
index 0000000000..8d919a61c5
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AIViewModel.kt
@@ -0,0 +1,317 @@
+package com.mogo.och.unmanned.passenger.ui.aiview
+
+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.unmanned.passenger.ui.aiview.bean.AIMessage
+import com.mogo.mgintelligent.speech.AsrResult
+import com.mogo.mgintelligent.speech.AsrState
+import com.mogo.mgintelligent.speech.IWakeUpListener
+import com.mogo.mgintelligent.speech.MGSpeech
+import com.mogo.och.bridge.autopilot.location.OchLocationManager
+import com.mogo.och.data.taxi.BaseOrderBean
+import com.mogo.och.data.taxi.TaxiOrderStatusEnum
+import com.mogo.och.unmanned.taxi.utils.order.OrderListener
+import com.mogo.och.unmanned.taxi.utils.order.OrderModel
+import com.mogo.service.v2n.bean.MGLlmQueryBean
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+class AIViewModel : ViewModel(), AIMessageManager.AIMessageListener, IWakeUpListener {
+
+ private val msgList = mutableListOf()
+ 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>(emptyList())
+ val messagesFlow: SharedFlow> get() = _messagesFlow
+
+ private val _asrUIStateFlow = MutableStateFlow(AsrUIState.Idle)
+ val asrUIStateFlow: StateFlow get() = _asrUIStateFlow
+
+ private val mgSpeech = MGSpeech
+
+ private val tipsTimeOut = "小智还没反应过来,请稍后再试~"
+ private val tipsExit = "我先退下了"
+ val tipsQuestions = listOf(
+ "可以问我:前方路口有拥堵吗?",
+ "可以对我说:介绍一下蘑菇车联?",
+ "想要对话时可以点击:小智智能体",
+ "可以问我:今天天气怎么样?",
+ )
+ private val tipsWakeUpList = listOf(
+ "嗯,我在呢",
+ "在的",
+ "在呢",
+ "你说",
+ )
+
+ private val orderListener = object : OrderListener{
+ override fun onCurrentOrderStatusChanged(order: BaseOrderBean?) {
+ if(order?.orderStatus== TaxiOrderStatusEnum.ArriveAtEnd.code){
+ 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) {
+ OrderModel.setOrderStatusCallback(TAG,orderListener)
+ OchLocationManager.addGCJ02Listener(TAG,1,locationCallback)
+ AIMessageManager.registerListener(this)
+ mgSpeech.weakUpListener = this
+ mgSpeech.startWeakUp()
+
+ llmResultJob = viewModelScope.launch(Dispatchers.IO) {
+ V2XRepository.llmResultFlow.collect { result ->
+ Log.d(TAG, "llm result: $result")
+
+ val msg = msgList.findLast { it.id == result.queryId }
+
+ if (msg == null || msg !is AIMessage.QA) {
+ Log.w(TAG, "消息不匹配,${msg}")
+ return@collect
+ }
+
+ AIMessageManager.post(
+ AIMessage.QA(
+ id = result.queryId,
+ title = result.answer,
+ tts = result.answer,
+ answer = result.answer,
+ question = msg.question,
+ state = AIMessage.QA.QuestionState.FINISH,
+ pictureUrl = result.imgUrls.getOrNull(0) ?: "",
+ pictureUrlList = result.imgUrls
+ )
+ )
+ }
+ }
+ }
+
+
+ override fun onCleared() {
+ AIMessageManager.unregisterListener(this)
+ mgSpeech.weakUpListener = null
+ mgSpeech.stopWeakUp()
+ llmResultJob?.cancel()
+ OrderModel.setOrderStatusCallback(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)
+ }
+
+ 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]
+
+ if (shouldSkipUpdate(oldMessage, newMessage)) {
+ return
+ }
+
+ 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 shouldSkipUpdate(oldMessage: AIMessage, newMessage: AIMessage): Boolean {
+ if (oldMessage is AIMessage.QA
+ && (oldMessage.state == AIMessage.QA.QuestionState.FINISH ||
+ oldMessage.state == AIMessage.QA.QuestionState.ERROR)
+ ) {
+ Log.d(TAG, "handleMsg: 消息拦截")
+ return true
+ }
+ return false
+ }
+
+ 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()) {
+ mgSpeech.speak(newMessage.tts)
+ }
+ }
+
+ companion object {
+ private const val TAG = "AiViewModel"
+ }
+
+ override fun onWakeUp() {
+ mgSpeech.isAssistantShow(true)
+ val random = tipsWakeUpList.random()
+ _asrUIStateFlow.value = AsrUIState.Listening(random)
+ mgSpeech.speak(random, true) {
+ mgSpeech.startAsr()
+ _asrUIStateFlow.value = AsrUIState.Listening("")
+ }
+ }
+
+ override fun onListener(msg: AsrResult) {
+ when (msg.type) {
+ AsrState.STATE_PARTIAL -> {
+// binding.tvContent.text = msg.content
+
+ _asrUIStateFlow.value = AsrUIState.Listening(msg.content)
+ }
+
+ AsrState.STATE_FINAL -> {
+// assistantView?.showWaitingResult(msg.content)
+// binding.tvContent.text = msg.content
+ _asrUIStateFlow.value = AsrUIState.Recognized(msg.content)
+ }
+
+ AsrState.STATE_EXIT -> {
+ _asrUIStateFlow.value = AsrUIState.Idle
+ mgSpeech.speak(tipsExit)
+ mgSpeech.isAssistantShow(false)
+ val content = msg.content
+ if (content.isNotEmpty()) {
+ viewModelScope.launch(Dispatchers.IO) {
+ // 1.上报消息
+ val msgId = "${System.currentTimeMillis()}"
+ V2XRepository.uploadLlmQuery(MGLlmQueryBean().apply {
+ //user = sn
+ conversationId = ""
+ query = content
+ queryId = msgId
+ })
+ // 2.生成消息
+ val qaMsg = AIMessage.QA(
+ id = msgId,
+ title = "",
+ question = content,
+ state = AIMessage.QA.QuestionState.UNDERSTAND,
+ answer = "",
+ )
+ AIMessageManager.post(qaMsg)
+ // 3.延迟发送状态
+ delay(2000)
+ val analyzeMsg = qaMsg.copy(state = AIMessage.QA.QuestionState.ANALYZE)
+ AIMessageManager.post(analyzeMsg)
+ // 4.延迟发送状态
+ delay(2000)
+ val answerMsg = qaMsg.copy(state = AIMessage.QA.QuestionState.ANSWER)
+ AIMessageManager.post(answerMsg)
+
+ // test
+ delay(10000)
+ val finishMsg = qaMsg.copy(
+ state = AIMessage.QA.QuestionState.ERROR,
+ answer = tipsTimeOut,
+ )
+ AIMessageManager.post(finishMsg)
+ }
+ }
+ }
+
+ else -> {
+ _asrUIStateFlow.value = AsrUIState.Idle
+ mgSpeech.isAssistantShow(false)
+ }
+ }
+ }
+
+ 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()
+ }
+ }
+
+
+ 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()
+}
+
+
+
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AiView.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AiView.kt
new file mode 100644
index 0000000000..5a53fc1343
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/AiView.kt
@@ -0,0 +1,331 @@
+package com.mogo.och.unmanned.passenger.ui.aiview
+
+import android.animation.ObjectAnimator
+import android.content.Context
+import android.util.AttributeSet
+import androidx.lifecycle.lifecycleScope
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.TextView
+import androidx.constraintlayout.motion.widget.MotionLayout
+import androidx.core.animation.addListener
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.findViewTreeLifecycleOwner
+import androidx.lifecycle.findViewTreeViewModelStoreOwner
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.mogo.eagle.core.utilcode.kotlin.onClick
+import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
+import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
+import com.mogo.och.common.module.utils.BigFrameAnimatorContainer
+import com.mogo.och.common.module.utils.RxUtils
+import com.mogo.och.unmanned.passenger.ui.aiview.adapter.AIMessageAdapter
+import com.mogo.och.unmanned.passenger.ui.aiview.adapter.OnItemClickListener
+import com.mogo.och.unmanned.passenger.ui.aiview.adapter.PaddingItemDecoration
+import com.mogo.och.unmanned.passenger.ui.aiview.bean.AIMessage
+import com.mogo.och.unmanned.taxi.passenger.R
+import kotlinx.android.synthetic.main.taxt_p_ai.view.aiMotionLayout
+import kotlinx.android.synthetic.main.taxt_p_ai.view.ivIcon
+import kotlinx.android.synthetic.main.taxt_p_ai.view.ivListening
+import kotlinx.android.synthetic.main.taxt_p_ai.view.rvMessages
+import kotlinx.android.synthetic.main.taxt_p_ai.view.rvMessagesEmpty
+import kotlinx.android.synthetic.main.taxt_p_ai.view.tvContent
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+class AiView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : MotionLayout(context, attrs, defStyleAttr), AIViewModel.AiViewCallback {
+
+
+
+ private var viewModel:AIViewModel?=null
+
+ private var aiAnimator: BigFrameAnimatorContainer?=null
+ private var aiAnimatorBg: BigFrameAnimatorContainer?=null
+
+ private var tipLooperJob: Job? = null
+
+ @Volatile
+ private var isTipsLooperRunning = false
+
+ private var tipsIndex = 0
+
+ private var isUserScrollingTime = 0L
+
+
+ private val messageAdapter: AIMessageAdapter by lazy { AIMessageAdapter() }
+ private val messageLayoutManager: LinearLayoutManager by lazy {
+ LinearLayoutManager(context).apply {
+ stackFromEnd = true
+ }
+ }
+
+ companion object{
+ private const val TIPS_ROTATION_DELAY = 3000L
+ private const val TAG ="AiView"
+ private const val SCROLL_THRESHOLD = 5000L
+ }
+
+
+
+
+ init {
+ LayoutInflater.from(context).inflate(R.layout.taxt_p_ai, this, true)
+
+ rvMessages.layoutManager = messageLayoutManager
+ rvMessages.adapter = messageAdapter
+ rvMessages.addItemDecoration(PaddingItemDecoration(200, 300))
+ messageAdapter.onItemClickListener = OnItemClickListener { item, position ->
+ if (item is AIMessage.Event) {
+
+ }
+ }
+ rvMessages.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+ super.onScrollStateChanged(recyclerView, newState)
+ if (newState != RecyclerView.SCROLL_STATE_IDLE) {
+ isUserScrollingTime = System.currentTimeMillis()
+ }
+ }
+ })
+
+ if(aiAnimator==null) {
+ aiAnimator = BigFrameAnimatorContainer(R.array.ai_animator, 31, ivIcon)
+ }
+// aiAnimator?.start()
+
+ ivIcon.onClick {
+ viewModel?.onWakeUp()
+ }
+
+ ivListening.onClick {
+
+ }
+
+
+ }
+
+ private fun showListening(show: Boolean) {
+ if (aiAnimatorBg == null) {
+ aiAnimatorBg = BigFrameAnimatorContainer(R.array.ai_animator_listening, 31, ivListening)
+ }
+
+ CallerLogger.d(TAG,"showListening: $show")
+ if (show) {
+ ivListening.alpha = 1F
+ aiAnimatorBg?.start()
+ } else {
+ aiAnimatorBg?.stop()
+ ivListening.alpha = 0F
+ }
+ }
+
+ private fun startTips() {
+ if (isTipsLooperRunning) {
+ CallerLogger.d(TAG,"${tName()} startTips: Already running")
+ return
+ }
+ tipLooperJob?.cancel()
+
+ tipLooperJob = findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
+ isTipsLooperRunning = true
+ CallerLogger.d(TAG,"${tName()} startTipsLooper: start")
+ while (isTipsLooperRunning) {
+ CallerLogger.d(TAG,"${tName()} startTipsLooper: next")
+ viewModel?.let {
+ val tips = it.tipsQuestions
+ val index = tipsIndex++ % tips.size
+ tvContent.changeTextWithFade(tips[index])
+ }
+ delay(TIPS_ROTATION_DELAY)
+ }
+ }
+ }
+
+ private fun stopTips() {
+ Log.i(TAG, "${tName()} stopTips: stop")
+ tipLooperJob?.cancel()
+ tipLooperJob = null
+ isTipsLooperRunning = false
+ }
+
+ override fun onVisibilityAggregated(isVisible: Boolean) {
+ super.onVisibilityAggregated(isVisible)
+ CallerLogger.d(TAG,"是否展示中:${isVisible}")
+ try {
+ if(isVisible){
+ aiAnimator?.start()
+ RxUtils.createSubscribe(3_000) {
+ aiMotionLayout.setTransitionListener(object :TransitionListener{
+ override fun onTransitionStarted(
+ motionLayout: MotionLayout?,
+ startId: Int,
+ endId: Int
+ ) {
+ CallerLogger.d(TAG,"onTransitionStarted:${motionLayout?.id}_$startId $endId")
+ }
+
+ override fun onTransitionChange(
+ motionLayout: MotionLayout?,
+ startId: Int,
+ endId: Int,
+ progress: Float
+ ) {
+ CallerLogger.d(TAG,"onTransitionChange:${motionLayout?.id}_$startId $endId ————${progress}")
+ }
+
+ override fun onTransitionCompleted(
+ motionLayout: MotionLayout?,
+ currentId: Int
+ ) {
+ CallerLogger.d(TAG,"${tName()} onTransitionCompleted:${motionLayout?.id}_$currentId")
+ rvMessagesEmpty.visibility = View.VISIBLE
+ startContextInfo()
+ startListInfo()
+ }
+
+ override fun onTransitionTrigger(
+ motionLayout: MotionLayout?,
+ triggerId: Int,
+ positive: Boolean,
+ progress: Float
+ ) {
+ CallerLogger.d(TAG,"onTransitionTrigger:${motionLayout?.id}_$triggerId ${triggerId} $positive $progress")
+ }
+
+ })
+ aiMotionLayout.transitionToEnd()
+ }
+ }else{
+ aiAnimator?.stop()
+ }
+ }catch (e:Exception){
+ OchChainLogManager.writeChainLog("展示崩溃","${e.message}")
+ }
+
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ viewModel = findViewTreeViewModelStoreOwner()?.let {
+ ViewModelProvider(it).get(AIViewModel::class.java)
+ }
+ viewModel?.setViewCallback(this)
+ }
+
+ private fun startListInfo(){
+ tipsIndex = viewModel?.getQuestionsRandomIndex()?:0
+ findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
+ viewModel?.messagesFlow?.collect {
+ Log.d(TAG, "${tName()} onMessages update: ${it}")
+ if(it.isNotEmpty()){
+ rvMessagesEmpty.visibility = View.INVISIBLE
+ }else{
+ rvMessagesEmpty.visibility = View.VISIBLE
+ }
+ messageAdapter.submitList(it) {
+ Log.d(TAG, "${tName()} adapter submit: ")
+ scrollToBottom()
+ }
+ }
+ }
+ }
+
+ private fun startContextInfo(){
+ findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
+ viewModel?.asrUIStateFlow?.collect {
+ Log.d(TAG, "${tName()}asr ui state $it")
+
+ when (it) {
+ is AsrUIState.Idle -> {
+ startTips()
+ showListening(false)
+ }
+
+ is AsrUIState.Listening -> {
+ stopTips()
+ showListening(true)
+ tvContent.text = it.partialText
+ }
+
+ is AsrUIState.Recognized -> {
+ stopTips()
+ tvContent.text = it.finalText
+ showListening(false)
+ }
+ }
+ }
+ }
+ }
+
+ override fun onDetachedFromWindow() {
+ aiAnimator?.stop()
+ aiAnimator = null
+ aiAnimatorBg?.stop()
+ aiAnimatorBg = null
+ tipLooperJob?.cancel()
+ super.onDetachedFromWindow()
+ }
+
+ // 滚动到RecyclerView底部
+ private fun scrollToBottom() {
+ val delay = System.currentTimeMillis() - isUserScrollingTime
+ if (delay < SCROLL_THRESHOLD) {
+ return
+ }
+ val layoutManager = rvMessages.layoutManager as LinearLayoutManager
+ layoutManager.scrollToPositionWithOffset(messageAdapter.itemCount - 1, 0)
+ }
+
+ fun tName(): String {
+ return "【${Thread.currentThread().name}】"
+ }
+
+ private suspend fun TextView.changeTextWithFade(newText: String) {
+ fadeOutFlow()
+ text = newText
+ fadeInFlow()
+ }
+
+ private suspend fun View.fadeOutFlow(duration: Long = 100) = suspendCancellableCoroutine { continuation ->
+ val animator = ObjectAnimator.ofFloat(this@fadeOutFlow, "alpha", 1f, 0f).apply {
+ this.duration = duration
+ }
+ animator.addListener(onEnd = {
+ continuation.resume(Unit) {
+ animator.cancel()
+ }
+ })
+ animator.start()
+ continuation.invokeOnCancellation {
+ Log.i(TAG, "fadeOutFlow: cancel")
+ animator.cancel()
+ }
+ }
+
+ private suspend fun View.fadeInFlow(duration: Long = 100) =
+ suspendCancellableCoroutine { continuation ->
+ val animator = ObjectAnimator.ofFloat(this@fadeInFlow, "alpha", 0f, 1f).apply {
+ this.duration = duration
+ }
+ animator.addListener(onEnd = {
+ continuation.resume(Unit) {
+ animator.cancel()
+ }
+ })
+ animator.start()
+ continuation.invokeOnCancellation {
+ CallerLogger.d(TAG,"fadeInFlow: cancel")
+ animator.cancel()
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/AIMessageAdapter.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/AIMessageAdapter.kt
new file mode 100644
index 0000000000..f38bae4934
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/AIMessageAdapter.kt
@@ -0,0 +1,43 @@
+package com.mogo.och.unmanned.passenger.ui.aiview.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.ListAdapter
+import com.mogo.och.unmanned.passenger.ui.aiview.bean.AIMessage
+import com.mogo.och.unmanned.taxi.passenger.R
+
+class AIMessageAdapter : ListAdapter(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_QA -> QAViewHolder(inflater.inflate(R.layout.item_ai_msg_qa, 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.QA -> AIMessage.TYPE_QA
+ is AIMessage.Scan -> AIMessage.TYPE_SCAN
+ is AIMessage.Light -> AIMessage.TYPE_LIGHT
+ is AIMessage.Speed -> AIMessage.TYPE_SPEED
+ is AIMessage.Warning -> AIMessage.TYPE_WARNING
+ else -> AIMessage.TYPE_EVENT
+ }
+ }
+
+ override fun onViewRecycled(holder: MessageViewHolder) {
+ super.onViewRecycled(holder)
+ holder.viewRecycled(holder)
+ }
+}
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/AIMessageViewHolder.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/AIMessageViewHolder.kt
new file mode 100644
index 0000000000..475fac8976
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/AIMessageViewHolder.kt
@@ -0,0 +1,166 @@
+package com.mogo.och.unmanned.passenger.ui.aiview.adapter
+
+import android.animation.ObjectAnimator
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.core.graphics.toColorInt
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.mogo.och.common.module.wigets.OchRoundImageView
+import com.mogo.och.unmanned.passenger.ui.aiview.bean.AIMessage
+import com.mogo.och.unmanned.taxi.passenger.R
+import com.youth.banner.Banner
+import com.youth.banner.indicator.CircleIndicator
+import com.youth.banner.transformer.ScaleInTransformer
+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 QAViewHolder(val binding: View) : MessageViewHolder(binding) {
+ private var tvQuestion: TextView = binding.findViewById(R.id.tvQuestion)
+ private var tvAnswer: TextView = binding.findViewById(R.id.tvAnswer)
+ private var tvTimestamp: TextView = binding.findViewById(R.id.tvTimestamp)
+ private var tvStateContent: TextView = binding.findViewById(R.id.tvStateContent)
+// private var ivPicture: OchRoundImageView = binding.findViewById(R.id.ivPicture)
+ private var llState: LinearLayout = binding.findViewById(R.id.llState)
+ private var tvStateUnderstand: TextView = binding.findViewById(R.id.tvStateUnderstand)
+ private var tvStateAnalyze: TextView = binding.findViewById(R.id.tvStateAnalyze)
+ private var tvStateAnswer: TextView = binding.findViewById(R.id.tvStateAnswer)
+ private var picBanner = binding.findViewById>(R.id.picBanner)
+
+
+ override fun bind(item: AIMessage, onItemClickListener: OnItemClickListener?) {
+ if (item is AIMessage.QA) {
+ binding.apply {
+ handleState(item.state)
+ tvQuestion.text = item.question
+ Log.d(TAG, "bind: ${item}")
+
+ tvAnswer.setTextAndVisibility(item.answer)
+// ivPicture.showOrHideWithUrl(item.pictureUrl)
+
+ handleTimestamp(item, tvTimestamp)
+
+ val pictureUrlList = item.pictureUrlList
+ if (pictureUrlList.isEmpty()) {
+ picBanner.visibility = View.GONE
+ } else {
+ picBanner.visibility = View.VISIBLE
+ picBanner.setAdapter(BannerImageAdapter(pictureUrlList))
+// .addBannerLifecycleObserver(picBanner.context) //添加生命周期观察者
+ .setIndicator(CircleIndicator(picBanner.context))
+ .setIndicatorSelectedColor("#40B4FF".toColorInt())
+ .setIndicatorNormalColor("#FFFFFF".toColorInt())
+ .setPageTransformer(ScaleInTransformer())
+ }
+ }
+ }
+ }
+
+ private fun handleState(state: AIMessage.QA.QuestionState) {
+ when (state) {
+ AIMessage.QA.QuestionState.UNDERSTAND -> {
+ llState.visibility = View.VISIBLE
+ tvStateContent.text = "正在理解问题…"
+ tvStateUnderstand.visibility = View.GONE
+ tvStateAnalyze.visibility = View.GONE
+ tvStateAnswer.visibility = View.GONE
+ }
+
+ AIMessage.QA.QuestionState.ANALYZE -> {
+ tvStateUnderstand.visibility = View.VISIBLE
+ llState.visibility = View.VISIBLE
+ tvStateContent.text = "正在分析处理…"
+ tvStateAnalyze.visibility = View.GONE
+ tvStateAnswer.visibility = View.GONE
+ }
+
+ AIMessage.QA.QuestionState.ANSWER -> {
+ tvStateUnderstand.visibility = View.VISIBLE
+ tvStateAnalyze.visibility = View.VISIBLE
+ llState.visibility = View.VISIBLE
+ tvStateContent.text = "正在总结回答…"
+ tvStateAnswer.visibility = View.GONE
+ }
+
+
+ AIMessage.QA.QuestionState.FINISH -> {
+ tvStateUnderstand.visibility = View.VISIBLE
+ tvStateAnalyze.visibility = View.VISIBLE
+ tvStateAnswer.visibility = View.VISIBLE
+
+ llState.visibility = View.GONE
+ tvStateContent.text = ""
+ }
+
+ AIMessage.QA.QuestionState.ERROR -> {
+ tvStateUnderstand.visibility = View.VISIBLE
+ tvStateAnalyze.visibility = View.VISIBLE
+ tvStateAnswer.visibility = View.VISIBLE
+
+ llState.visibility = View.GONE
+ tvStateContent.text = ""
+ }
+ }
+ }
+}
+
+
+
+fun interface OnItemClickListener {
+ fun onItemClick(item: AIMessage, position: Int)
+}
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/BannerImageAdapter.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/BannerImageAdapter.kt
new file mode 100644
index 0000000000..58b00d0ffe
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/BannerImageAdapter.kt
@@ -0,0 +1,31 @@
+package com.mogo.och.unmanned.passenger.ui.aiview.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.mogo.och.unmanned.taxi.passenger.R
+import com.youth.banner.adapter.BannerAdapter
+
+open class BannerImageAdapter(imgList: List) :
+ BannerAdapter(imgList) {
+ override fun onBindView(holder: BannerHolder?, data: String?, position: Int, size: Int) {
+ //图片加载自己实现
+ holder ?: return
+ val imageView = holder.view
+ Glide.with(imageView)
+ .load(data)
+// .apply(RequestOptions.bitmapTransform(RoundedCorners(30)))
+ .into(imageView)
+ }
+
+ override fun onCreateHolder(parent: ViewGroup, viewType: Int): BannerHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ val view = inflater.inflate(R.layout.item_ai_banner_item, parent, false) as ImageView
+ return BannerHolder(view)
+ }
+}
+
+
+class BannerHolder(val view: ImageView) : RecyclerView.ViewHolder(view)
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/MessageDiffCallback.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/MessageDiffCallback.kt
new file mode 100644
index 0000000000..c5936542d9
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/MessageDiffCallback.kt
@@ -0,0 +1,15 @@
+package com.mogo.och.unmanned.passenger.ui.aiview.adapter
+
+import androidx.recyclerview.widget.DiffUtil
+import com.mogo.och.unmanned.passenger.ui.aiview.bean.AIMessage
+
+class MessageDiffCallback: DiffUtil.ItemCallback() {
+
+ override fun areContentsTheSame(oldItem: AIMessage, newItem: AIMessage): Boolean {
+ return oldItem == newItem
+ }
+
+ override fun areItemsTheSame(oldItem: AIMessage, newItem: AIMessage): Boolean {
+ return oldItem.id == newItem.id
+ }
+}
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/PaddingItemDecoration.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/PaddingItemDecoration.kt
new file mode 100644
index 0000000000..132afe2439
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/adapter/PaddingItemDecoration.kt
@@ -0,0 +1,26 @@
+package com.mogo.och.unmanned.passenger.ui.aiview.adapter
+
+import android.graphics.Rect
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+class PaddingItemDecoration(private val topPadding: Int, private val bottomPadding: Int) : RecyclerView.ItemDecoration() {
+ 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
+// }
+ }
+}
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/bean/AssistantMessage.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/bean/AssistantMessage.kt
new file mode 100644
index 0000000000..054e8ef6d3
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/bean/AssistantMessage.kt
@@ -0,0 +1,129 @@
+package com.mogo.och.unmanned.passenger.ui.aiview.bean
+
+import android.os.CountDownTimer
+import android.util.Log
+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_QA = 2
+ const val TYPE_LIGHT = 3
+ const val TYPE_SPEED = 4
+ const val TYPE_WARNING = 5
+ }
+
+ 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 = 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)
+
+}
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/bean/ListenUIState.kt b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/bean/ListenUIState.kt
new file mode 100644
index 0000000000..7fd872cf8c
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/java/com/mogo/och/unmanned/passenger/ui/aiview/bean/ListenUIState.kt
@@ -0,0 +1,3 @@
+package com.mogo.och.unmanned.passenger.ui.aiview.bean
+
+data class ListenUIState(val show: Boolean, val text: String, val showTips: Boolean = false)
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_01.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_01.webp
new file mode 100644
index 0000000000..6b4f4306ec
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_01.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_02.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_02.webp
new file mode 100644
index 0000000000..3dbff5f787
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_02.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_03.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_03.webp
new file mode 100644
index 0000000000..9752651d85
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_03.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_04.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_04.webp
new file mode 100644
index 0000000000..f3b092aa6d
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_04.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_05.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_05.webp
new file mode 100644
index 0000000000..e73389d1d9
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_05.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_06.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_06.webp
new file mode 100644
index 0000000000..bed96396bb
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_06.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_07.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_07.webp
new file mode 100644
index 0000000000..adc91a7958
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_07.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_08.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_08.webp
new file mode 100644
index 0000000000..9c65009417
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_08.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_09.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_09.webp
new file mode 100644
index 0000000000..15d9b8ae19
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_09.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_10.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_10.webp
new file mode 100644
index 0000000000..65b78a6fb3
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_10.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_100.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_100.webp
new file mode 100644
index 0000000000..fe6d63010d
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_100.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_11.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_11.webp
new file mode 100644
index 0000000000..79f4b9c34f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_11.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_12.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_12.webp
new file mode 100644
index 0000000000..1bae2d5c7a
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_12.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_13.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_13.webp
new file mode 100644
index 0000000000..373f770f0d
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_13.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_14.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_14.webp
new file mode 100644
index 0000000000..11c1ae98fa
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_14.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_15.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_15.webp
new file mode 100644
index 0000000000..65e1f0bfff
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_15.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_16.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_16.webp
new file mode 100644
index 0000000000..d75150e2f2
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_16.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_17.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_17.webp
new file mode 100644
index 0000000000..e2d13882d3
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_17.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_18.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_18.webp
new file mode 100644
index 0000000000..0e84b6fc9c
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_18.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_19.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_19.webp
new file mode 100644
index 0000000000..93a6218ce2
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_19.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_20.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_20.webp
new file mode 100644
index 0000000000..5b604c5576
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_20.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_21.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_21.webp
new file mode 100644
index 0000000000..a937518c16
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_21.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_22.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_22.webp
new file mode 100644
index 0000000000..39fb1fea8a
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_22.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_23.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_23.webp
new file mode 100644
index 0000000000..3df50a739b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_23.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_24.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_24.webp
new file mode 100644
index 0000000000..39820d1999
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_24.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_25.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_25.webp
new file mode 100644
index 0000000000..0bbea56fb9
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_25.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_26.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_26.webp
new file mode 100644
index 0000000000..75cda44ebf
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_26.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_27.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_27.webp
new file mode 100644
index 0000000000..8fe11311ac
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_27.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_28.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_28.webp
new file mode 100644
index 0000000000..5d3073a67d
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_28.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_29.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_29.webp
new file mode 100644
index 0000000000..a04b1e4ca9
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_29.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_30.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_30.webp
new file mode 100644
index 0000000000..fc9fcc5401
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_30.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_31.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_31.webp
new file mode 100644
index 0000000000..9e6ef15aa5
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_31.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_32.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_32.webp
new file mode 100644
index 0000000000..47e414d71e
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_32.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_33.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_33.webp
new file mode 100644
index 0000000000..b674719078
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_33.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_34.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_34.webp
new file mode 100644
index 0000000000..2286c059de
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_34.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_35.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_35.webp
new file mode 100644
index 0000000000..5cbf8d8e54
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_35.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_36.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_36.webp
new file mode 100644
index 0000000000..92af4bccb5
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_36.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_37.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_37.webp
new file mode 100644
index 0000000000..83e072bf44
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_37.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_38.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_38.webp
new file mode 100644
index 0000000000..b96b5b2ac8
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_38.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_39.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_39.webp
new file mode 100644
index 0000000000..629f61cdd5
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_39.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_40.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_40.webp
new file mode 100644
index 0000000000..9a5f07ff0b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_40.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_41.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_41.webp
new file mode 100644
index 0000000000..0f5d29acea
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_41.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_42.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_42.webp
new file mode 100644
index 0000000000..bed3ce67b3
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_42.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_43.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_43.webp
new file mode 100644
index 0000000000..404a075292
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_43.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_44.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_44.webp
new file mode 100644
index 0000000000..3578bb2f19
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_44.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_45.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_45.webp
new file mode 100644
index 0000000000..c5fb0d0a3b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_45.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_46.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_46.webp
new file mode 100644
index 0000000000..3b346373ee
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_46.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_47.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_47.webp
new file mode 100644
index 0000000000..7d3010f838
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_47.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_48.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_48.webp
new file mode 100644
index 0000000000..69bad18a38
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_48.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_49.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_49.webp
new file mode 100644
index 0000000000..d39950515f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_49.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_50.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_50.webp
new file mode 100644
index 0000000000..8a07ff89ca
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_50.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_51.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_51.webp
new file mode 100644
index 0000000000..413a290ff5
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_51.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_52.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_52.webp
new file mode 100644
index 0000000000..04d42b353d
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_52.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_53.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_53.webp
new file mode 100644
index 0000000000..5f181da3b2
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_53.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_54.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_54.webp
new file mode 100644
index 0000000000..82a77ad510
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_54.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_55.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_55.webp
new file mode 100644
index 0000000000..266bb0ea53
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_55.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_56.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_56.webp
new file mode 100644
index 0000000000..390c687b4b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_56.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_57.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_57.webp
new file mode 100644
index 0000000000..b89ff7a53f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_57.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_58.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_58.webp
new file mode 100644
index 0000000000..37be517e39
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_58.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_59.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_59.webp
new file mode 100644
index 0000000000..10500d1bd8
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_59.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_60.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_60.webp
new file mode 100644
index 0000000000..3a9ca2b135
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_60.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_61.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_61.webp
new file mode 100644
index 0000000000..f1f15b09b9
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_61.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_62.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_62.webp
new file mode 100644
index 0000000000..36ac568e29
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_62.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_63.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_63.webp
new file mode 100644
index 0000000000..92a4a699b7
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_63.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_64.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_64.webp
new file mode 100644
index 0000000000..c013e4e7a9
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_64.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_65.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_65.webp
new file mode 100644
index 0000000000..49aeb97d73
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_65.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_66.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_66.webp
new file mode 100644
index 0000000000..aab4c5b373
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_66.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_67.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_67.webp
new file mode 100644
index 0000000000..e89c0a8ce4
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_67.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_68.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_68.webp
new file mode 100644
index 0000000000..d1d24aaae4
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_68.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_69.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_69.webp
new file mode 100644
index 0000000000..9f74c1421b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_69.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_70.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_70.webp
new file mode 100644
index 0000000000..ff413a191a
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_70.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_71.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_71.webp
new file mode 100644
index 0000000000..ecbb0f0448
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_71.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_72.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_72.webp
new file mode 100644
index 0000000000..9776560e08
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_72.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_73.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_73.webp
new file mode 100644
index 0000000000..b7c5ee478a
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_73.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_74.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_74.webp
new file mode 100644
index 0000000000..77f04d978f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_74.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_75.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_75.webp
new file mode 100644
index 0000000000..4863c07dcd
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_75.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_76.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_76.webp
new file mode 100644
index 0000000000..2890b7c416
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_76.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_77.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_77.webp
new file mode 100644
index 0000000000..c46b150a45
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_77.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_78.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_78.webp
new file mode 100644
index 0000000000..560743d781
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_78.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_79.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_79.webp
new file mode 100644
index 0000000000..1b4f9de0f5
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_79.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_80.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_80.webp
new file mode 100644
index 0000000000..09e5baded4
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_80.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_81.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_81.webp
new file mode 100644
index 0000000000..7cbe07a874
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_81.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_82.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_82.webp
new file mode 100644
index 0000000000..8c764eed3a
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_82.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_83.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_83.webp
new file mode 100644
index 0000000000..4376a5b527
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_83.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_84.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_84.webp
new file mode 100644
index 0000000000..8d23a7306b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_84.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_85.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_85.webp
new file mode 100644
index 0000000000..25ab506111
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_85.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_86.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_86.webp
new file mode 100644
index 0000000000..e17fbe51a3
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_86.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_87.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_87.webp
new file mode 100644
index 0000000000..d4f66c8e97
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_87.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_88.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_88.webp
new file mode 100644
index 0000000000..6d5b509c01
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_88.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_89.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_89.webp
new file mode 100644
index 0000000000..f165990782
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_89.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_90.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_90.webp
new file mode 100644
index 0000000000..7dd83b3617
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_90.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_91.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_91.webp
new file mode 100644
index 0000000000..4762c95b6c
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_91.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_92.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_92.webp
new file mode 100644
index 0000000000..17a1e7ca85
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_92.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_93.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_93.webp
new file mode 100644
index 0000000000..ee8fd72f33
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_93.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_94.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_94.webp
new file mode 100644
index 0000000000..6865bf5d3e
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_94.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_95.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_95.webp
new file mode 100644
index 0000000000..89d61e30d9
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_95.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_96.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_96.webp
new file mode 100644
index 0000000000..213be04d2c
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_96.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_97.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_97.webp
new file mode 100644
index 0000000000..10921d599f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_97.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_98.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_98.webp
new file mode 100644
index 0000000000..8ea38d7ca1
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_98.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_99.webp b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_99.webp
new file mode 100644
index 0000000000..d8c07824a5
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_99.webp differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_00.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_00.png
new file mode 100644
index 0000000000..e6251b3e9b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_00.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_01.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_01.png
new file mode 100644
index 0000000000..2ebcb608df
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_01.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_02.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_02.png
new file mode 100644
index 0000000000..edb8b077ed
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_02.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_03.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_03.png
new file mode 100644
index 0000000000..867b0283ff
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_03.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_04.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_04.png
new file mode 100644
index 0000000000..d94af63ca0
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_04.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_05.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_05.png
new file mode 100644
index 0000000000..02275e7c44
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_05.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_06.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_06.png
new file mode 100644
index 0000000000..79172c0904
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_06.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_07.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_07.png
new file mode 100644
index 0000000000..648be20fe9
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_07.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_08.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_08.png
new file mode 100644
index 0000000000..8aa456573f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_08.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_09.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_09.png
new file mode 100644
index 0000000000..45e04b6df7
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_09.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_10.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_10.png
new file mode 100644
index 0000000000..a125fa2a11
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_10.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_11.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_11.png
new file mode 100644
index 0000000000..f025adaf45
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_11.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_12.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_12.png
new file mode 100644
index 0000000000..bade4e7fa0
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_12.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_13.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_13.png
new file mode 100644
index 0000000000..20415ad3d5
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_13.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_14.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_14.png
new file mode 100644
index 0000000000..9667980607
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_14.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_15.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_15.png
new file mode 100644
index 0000000000..f1b4aa07ec
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_15.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_16.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_16.png
new file mode 100644
index 0000000000..ade00f137f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_16.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_17.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_17.png
new file mode 100644
index 0000000000..19b2d879fe
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_17.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_18.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_18.png
new file mode 100644
index 0000000000..26462ef724
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_18.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_19.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_19.png
new file mode 100644
index 0000000000..7838464877
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_19.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_20.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_20.png
new file mode 100644
index 0000000000..3820cd4725
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_20.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_21.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_21.png
new file mode 100644
index 0000000000..3cece8dc68
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_21.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_22.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_22.png
new file mode 100644
index 0000000000..48966b0901
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_22.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_23.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_23.png
new file mode 100644
index 0000000000..d05bea95f8
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_23.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_24.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_24.png
new file mode 100644
index 0000000000..61a14d3d31
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_24.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_25.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_25.png
new file mode 100644
index 0000000000..42d3cb07bb
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_25.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_26.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_26.png
new file mode 100644
index 0000000000..f3ff9de974
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_26.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_27.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_27.png
new file mode 100644
index 0000000000..aad3e72a94
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_27.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_28.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_28.png
new file mode 100644
index 0000000000..939f057983
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_28.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_29.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_29.png
new file mode 100644
index 0000000000..d537a73dc1
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_29.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_30.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_30.png
new file mode 100644
index 0000000000..cbf447d296
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_30.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_31.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_31.png
new file mode 100644
index 0000000000..3cb32e80f0
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_31.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_32.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_32.png
new file mode 100644
index 0000000000..893b64936f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_32.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_33.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_33.png
new file mode 100644
index 0000000000..c6bd9cb25f
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_33.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_34.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_34.png
new file mode 100644
index 0000000000..c30c5e3da5
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_34.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_35.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_35.png
new file mode 100644
index 0000000000..216fc11d85
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_35.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_36.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_36.png
new file mode 100644
index 0000000000..d4b413ff24
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_36.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_37.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_37.png
new file mode 100644
index 0000000000..6f5f4d8475
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_37.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_38.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_38.png
new file mode 100644
index 0000000000..988fe6e0d0
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_38.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_39.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_39.png
new file mode 100644
index 0000000000..ac41b22f37
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_39.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_40.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_40.png
new file mode 100644
index 0000000000..639886dd88
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_40.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_41.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_41.png
new file mode 100644
index 0000000000..c9c45eda17
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_41.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_42.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_42.png
new file mode 100644
index 0000000000..9e2636712e
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_42.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_43.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_43.png
new file mode 100644
index 0000000000..06e4be3956
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_43.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_44.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_44.png
new file mode 100644
index 0000000000..66dc6001ca
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_44.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_45.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_45.png
new file mode 100644
index 0000000000..37f02d59e2
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_45.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_46.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_46.png
new file mode 100644
index 0000000000..311a25105b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_46.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_47.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_47.png
new file mode 100644
index 0000000000..8d0b4affaf
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_47.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_48.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_48.png
new file mode 100644
index 0000000000..124a146839
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_48.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_49.png b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_49.png
new file mode 100644
index 0000000000..e6251b3e9b
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/ani/drawable-nodpi/anim_ai_listening_49.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/ani/values/arrays.xml b/OCH/taxi/unmanned-passenger/src/main/res/ani/values/arrays.xml
index 1dfcf8e5e6..f17eebd89c 100644
--- a/OCH/taxi/unmanned-passenger/src/main/res/ani/values/arrays.xml
+++ b/OCH/taxi/unmanned-passenger/src/main/res/ani/values/arrays.xml
@@ -302,4 +302,179 @@
+
+ - @drawable/anim_ai_01
+ - @drawable/anim_ai_02
+ - @drawable/anim_ai_03
+ - @drawable/anim_ai_04
+ - @drawable/anim_ai_05
+ - @drawable/anim_ai_06
+ - @drawable/anim_ai_07
+ - @drawable/anim_ai_08
+ - @drawable/anim_ai_09
+
+ - @drawable/anim_ai_10
+ - @drawable/anim_ai_11
+ - @drawable/anim_ai_12
+ - @drawable/anim_ai_13
+ - @drawable/anim_ai_14
+ - @drawable/anim_ai_15
+ - @drawable/anim_ai_16
+ - @drawable/anim_ai_17
+ - @drawable/anim_ai_18
+ - @drawable/anim_ai_19
+
+ - @drawable/anim_ai_20
+ - @drawable/anim_ai_21
+ - @drawable/anim_ai_22
+ - @drawable/anim_ai_23
+ - @drawable/anim_ai_24
+ - @drawable/anim_ai_25
+ - @drawable/anim_ai_26
+ - @drawable/anim_ai_27
+ - @drawable/anim_ai_28
+ - @drawable/anim_ai_29
+
+
+ - @drawable/anim_ai_30
+ - @drawable/anim_ai_31
+ - @drawable/anim_ai_32
+ - @drawable/anim_ai_33
+ - @drawable/anim_ai_34
+ - @drawable/anim_ai_35
+ - @drawable/anim_ai_36
+ - @drawable/anim_ai_37
+ - @drawable/anim_ai_38
+ - @drawable/anim_ai_39
+
+ - @drawable/anim_ai_40
+ - @drawable/anim_ai_41
+ - @drawable/anim_ai_42
+ - @drawable/anim_ai_43
+ - @drawable/anim_ai_44
+ - @drawable/anim_ai_45
+ - @drawable/anim_ai_46
+ - @drawable/anim_ai_47
+ - @drawable/anim_ai_48
+ - @drawable/anim_ai_49
+
+
+ - @drawable/anim_ai_50
+ - @drawable/anim_ai_51
+ - @drawable/anim_ai_52
+ - @drawable/anim_ai_53
+ - @drawable/anim_ai_54
+ - @drawable/anim_ai_55
+ - @drawable/anim_ai_56
+ - @drawable/anim_ai_57
+ - @drawable/anim_ai_58
+ - @drawable/anim_ai_59
+
+ - @drawable/anim_ai_60
+ - @drawable/anim_ai_61
+ - @drawable/anim_ai_62
+ - @drawable/anim_ai_63
+ - @drawable/anim_ai_64
+ - @drawable/anim_ai_65
+ - @drawable/anim_ai_66
+ - @drawable/anim_ai_67
+ - @drawable/anim_ai_68
+ - @drawable/anim_ai_69
+
+ - @drawable/anim_ai_70
+ - @drawable/anim_ai_71
+ - @drawable/anim_ai_72
+ - @drawable/anim_ai_73
+ - @drawable/anim_ai_74
+ - @drawable/anim_ai_75
+ - @drawable/anim_ai_76
+ - @drawable/anim_ai_77
+ - @drawable/anim_ai_78
+ - @drawable/anim_ai_79
+
+ - @drawable/anim_ai_80
+ - @drawable/anim_ai_81
+ - @drawable/anim_ai_82
+ - @drawable/anim_ai_83
+ - @drawable/anim_ai_84
+ - @drawable/anim_ai_85
+ - @drawable/anim_ai_86
+ - @drawable/anim_ai_87
+ - @drawable/anim_ai_88
+ - @drawable/anim_ai_89
+
+ - @drawable/anim_ai_90
+ - @drawable/anim_ai_91
+ - @drawable/anim_ai_92
+ - @drawable/anim_ai_93
+ - @drawable/anim_ai_94
+ - @drawable/anim_ai_95
+ - @drawable/anim_ai_96
+ - @drawable/anim_ai_97
+ - @drawable/anim_ai_98
+ - @drawable/anim_ai_99
+ - @drawable/anim_ai_100
+
+
+
+
+ - @drawable/anim_ai_listening_00
+ - @drawable/anim_ai_listening_01
+ - @drawable/anim_ai_listening_02
+ - @drawable/anim_ai_listening_03
+ - @drawable/anim_ai_listening_04
+ - @drawable/anim_ai_listening_05
+ - @drawable/anim_ai_listening_06
+ - @drawable/anim_ai_listening_07
+ - @drawable/anim_ai_listening_08
+ - @drawable/anim_ai_listening_09
+
+ - @drawable/anim_ai_listening_10
+ - @drawable/anim_ai_listening_11
+ - @drawable/anim_ai_listening_12
+ - @drawable/anim_ai_listening_13
+ - @drawable/anim_ai_listening_14
+ - @drawable/anim_ai_listening_15
+ - @drawable/anim_ai_listening_16
+ - @drawable/anim_ai_listening_17
+ - @drawable/anim_ai_listening_18
+ - @drawable/anim_ai_listening_19
+
+ - @drawable/anim_ai_listening_20
+ - @drawable/anim_ai_listening_21
+ - @drawable/anim_ai_listening_22
+ - @drawable/anim_ai_listening_23
+ - @drawable/anim_ai_listening_24
+ - @drawable/anim_ai_listening_25
+ - @drawable/anim_ai_listening_26
+ - @drawable/anim_ai_listening_27
+ - @drawable/anim_ai_listening_28
+ - @drawable/anim_ai_listening_29
+
+
+ - @drawable/anim_ai_listening_30
+ - @drawable/anim_ai_listening_31
+ - @drawable/anim_ai_listening_32
+ - @drawable/anim_ai_listening_33
+ - @drawable/anim_ai_listening_34
+ - @drawable/anim_ai_listening_35
+ - @drawable/anim_ai_listening_36
+ - @drawable/anim_ai_listening_37
+ - @drawable/anim_ai_listening_38
+ - @drawable/anim_ai_listening_39
+
+ - @drawable/anim_ai_listening_40
+ - @drawable/anim_ai_listening_41
+ - @drawable/anim_ai_listening_42
+ - @drawable/anim_ai_listening_43
+ - @drawable/anim_ai_listening_44
+ - @drawable/anim_ai_listening_45
+ - @drawable/anim_ai_listening_46
+ - @drawable/anim_ai_listening_47
+ - @drawable/anim_ai_listening_48
+ - @drawable/anim_ai_listening_49
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/icon_image_error.png b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/icon_image_error.png
new file mode 100644
index 0000000000..4cadae7774
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/icon_image_error.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/icon_image_holder.png b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/icon_image_holder.png
new file mode 100644
index 0000000000..3febfc5587
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/icon_image_holder.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_ai_bg.png b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_ai_bg.png
new file mode 100644
index 0000000000..64323d5e89
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_ai_bg.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_ai_head.png b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_ai_head.png
new file mode 100644
index 0000000000..3c14d19549
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_ai_head.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_home_map_bg_1.9.png b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_home_map_bg_1.9.png
new file mode 100644
index 0000000000..af046bb4e0
Binary files /dev/null and b/OCH/taxi/unmanned-passenger/src/main/res/drawable-nodpi/taxi_p_home_map_bg_1.9.png differ
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/drawable/bg_assistant_item_point.xml b/OCH/taxi/unmanned-passenger/src/main/res/drawable/bg_assistant_item_point.xml
new file mode 100644
index 0000000000..cab7ec214e
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/res/drawable/bg_assistant_item_point.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/drawable/icon_pic_error.xml b/OCH/taxi/unmanned-passenger/src/main/res/drawable/icon_pic_error.xml
new file mode 100644
index 0000000000..5de9d5afff
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/res/drawable/icon_pic_error.xml
@@ -0,0 +1,15 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/drawable/icon_pic_holder.xml b/OCH/taxi/unmanned-passenger/src/main/res/drawable/icon_pic_holder.xml
new file mode 100644
index 0000000000..67373cd139
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/res/drawable/icon_pic_holder.xml
@@ -0,0 +1,14 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/layout/item_ai_banner_item.xml b/OCH/taxi/unmanned-passenger/src/main/res/layout/item_ai_banner_item.xml
new file mode 100644
index 0000000000..b74b2bfce4
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/res/layout/item_ai_banner_item.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/layout/item_ai_msg_qa.xml b/OCH/taxi/unmanned-passenger/src/main/res/layout/item_ai_msg_qa.xml
new file mode 100644
index 0000000000..44171a642c
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/res/layout/item_ai_msg_qa.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/layout/taxi_p_home.xml b/OCH/taxi/unmanned-passenger/src/main/res/layout/taxi_p_home.xml
index e2a538eeb8..321c4e74a1 100644
--- a/OCH/taxi/unmanned-passenger/src/main/res/layout/taxi_p_home.xml
+++ b/OCH/taxi/unmanned-passenger/src/main/res/layout/taxi_p_home.xml
@@ -4,15 +4,17 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:parentTag="RelativeLayout">
+ tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
-
@@ -24,7 +26,7 @@
app:isWeatherEnable="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />
-
+
-
-
+
+
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/layout/taxt_p_ai.xml b/OCH/taxi/unmanned-passenger/src/main/res/layout/taxt_p_ai.xml
new file mode 100644
index 0000000000..3260d49bf2
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/res/layout/taxt_p_ai.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/layout/taxt_u_p_base_fragment.xml b/OCH/taxi/unmanned-passenger/src/main/res/layout/taxt_u_p_base_fragment.xml
index 485a16f27e..b4c353d2f4 100644
--- a/OCH/taxi/unmanned-passenger/src/main/res/layout/taxt_u_p_base_fragment.xml
+++ b/OCH/taxi/unmanned-passenger/src/main/res/layout/taxt_u_p_base_fragment.xml
@@ -22,13 +22,13 @@
#80000000
- 11
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OCH/taxi/unmanned-passenger/src/main/res/xml/fragment_ai_scene.xml b/OCH/taxi/unmanned-passenger/src/main/res/xml/fragment_ai_scene.xml
new file mode 100644
index 0000000000..31dd7b1a27
--- /dev/null
+++ b/OCH/taxi/unmanned-passenger/src/main/res/xml/fragment_ai_scene.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/view/MapBizView.kt b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/view/MapBizView.kt
index 7dbeb7c5bf..e7fb87bc64 100644
--- a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/view/MapBizView.kt
+++ b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/view/MapBizView.kt
@@ -12,6 +12,7 @@ import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisStatesListener
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationWGS84ListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisStatesListenerManager
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager
+import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
import com.mogo.map.MogoMap
import com.mogo.map.MogoMapView
import com.mogo.map.overlay.line.Polyline
@@ -19,6 +20,7 @@ import com.mogo.map.overlay.point.Point
import com.mogo.map.overlay.proxy.line.IMapPolylineOverlay
import com.mogo.map.overlay.proxy.point.IMapPointOverlay
import com.mogo.map.uicontroller.IMogoMapUIController
+import me.jessyan.autosize.utils.AutoSizeUtils
import kotlin.properties.Delegates
@@ -141,4 +143,10 @@ class MapBizView(context: Context?, attrs: AttributeSet?) : MogoMapView(context,
super.onDestroy()
}
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ this.outlineProvider = TextureVideoViewOutlineProvider(AutoSizeUtils.dp2px(context,36f).toFloat())
+ this.clipToOutline = true
+ }
+
}
\ No newline at end of file
diff --git a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/LocationExt.kt b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/LocationExt.kt
index 24a6c25820..a9885427b7 100644
--- a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/LocationExt.kt
+++ b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/LocationExt.kt
@@ -1,7 +1,7 @@
package com.mogo.eagle.core.data.ai
import android.location.Location
-import com.amap.api.location.AMapLocation
+import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.service.v2n.service.socket.bean.MGLocationBean
/**
@@ -36,15 +36,15 @@ fun MGLocationBean.toCG02Location(): Location {
* @receiver MGLocationModel
* @return MGLocationBean
*/
-fun AMapLocation.toMogoLocation(sourceType:Int = 0): MGLocationBean {
+fun MogoLocation.toMogoLocation(sourceType:Int = 0): MGLocationBean {
val bean = MGLocationBean()
- bean.lockType = locationType
+ bean.lockType = locType
bean.coordType = MGLocationBean.COORD_TYPE_GCJ02
bean.accuracy = accuracy
bean.altitude = altitude
- bean.bearing = bearing
- bean.speed = speed
- bean.satellites = satellites
+ bean.bearing = heading.toFloat()
+ bean.speed = gnssSpeed
+ bean.satellites = satellite
bean.latitude = latitude
bean.longitude = longitude
bean.adCode = adCode
diff --git a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/LocationRepository.kt b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/LocationRepository.kt
index 79442308d2..e9d997c221 100644
--- a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/LocationRepository.kt
+++ b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/LocationRepository.kt
@@ -80,48 +80,48 @@ class LocationRepository : IRepository, AMapLocationListener {
override fun onLocationChanged(loc: AMapLocation) {
// Log.d(TAG, "onLocationChanged:${location.toStr()}")
- var location = loc
- Log.d(
- TAG,
- "onLocationChanged:$this , provider=${location.provider}#${location}#speed=${location.speed}#bearing=${location.bearing}"
- )
- if (location.latitude <= 0 && location.longitude <= 0) return
-
- location.cityCode.takeIf { !it.isNullOrEmpty() }?.let {
- lastCityCode = it
- }
-
- location.adCode.takeIf { !it.isNullOrEmpty() }?.let {
- lastAdCode = it
- }
- if (location.bearing > 0) {
- bear = location.bearing
- }
- location.bearing = bear
-
- // 设置固定坐标
- if (DataConfigs.fixedPoint != null) {
- location = DataConfigs.fixedPoint!!
- }
-
- lastLocationGcj02 = location
- _cg02Flow.tryEmit(location)
-
-
- if (!DataConfigs.useCorrectLocation){
- Log.e(TAG, "CorrectLoc 本地坐标应用为纠偏坐标")
- location.toMogoLocation().let {
- val latLng = CoordinateUtil.gcj02LngLatToWGS84(it.longitude, it.latitude)
- it.latitude = latLng[1];
- it.longitude = latLng[0]
- if (!stopLocation) {
- instance.lastLastCorrLoc84 = instance.lastCorrLoc84
- instance.lastCorrLoc84 = it
- V2XRepository._correctLocFlow.tryEmit(it)
-
- }
- }
- return
- }
+// var location = loc
+// Log.d(
+// TAG,
+// "onLocationChanged:$this , provider=${location.provider}#${location}#speed=${location.speed}#bearing=${location.bearing}"
+// )
+// if (location.latitude <= 0 && location.longitude <= 0) return
+//
+// location.cityCode.takeIf { !it.isNullOrEmpty() }?.let {
+// lastCityCode = it
+// }
+//
+// location.adCode.takeIf { !it.isNullOrEmpty() }?.let {
+// lastAdCode = it
+// }
+// if (location.bearing > 0) {
+// bear = location.bearing
+// }
+// location.bearing = bear
+//
+// // 设置固定坐标
+// if (DataConfigs.fixedPoint != null) {
+// location = DataConfigs.fixedPoint!!
+// }
+//
+// lastLocationGcj02 = location
+// _cg02Flow.tryEmit(location)
+//
+//
+// if (!DataConfigs.useCorrectLocation){
+// Log.e(TAG, "CorrectLoc 本地坐标应用为纠偏坐标")
+// location.toMogoLocation().let {
+// val latLng = CoordinateUtil.gcj02LngLatToWGS84(it.longitude, it.latitude)
+// it.latitude = latLng[1];
+// it.longitude = latLng[0]
+// if (!stopLocation) {
+// instance.lastLastCorrLoc84 = instance.lastCorrLoc84
+// instance.lastCorrLoc84 = it
+// V2XRepository._correctLocFlow.tryEmit(it)
+//
+// }
+// }
+// return
+// }
}
}
\ No newline at end of file
diff --git a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/V2XRepository.kt b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/V2XRepository.kt
index 0cde0cf44b..6920c2a572 100644
--- a/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/V2XRepository.kt
+++ b/core/mogo-core-data/src/main/java/com/mogo/eagle/core/data/ai/V2XRepository.kt
@@ -2,7 +2,7 @@ package com.mogo.eagle.core.data.ai
import android.content.Context
import android.util.Log
-import com.amap.api.location.AMapLocation
+import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.service.v2n.MGV2N
import com.mogo.service.v2n.MGV2NConst
import com.mogo.service.v2n.bean.MGAccidentBean
@@ -205,7 +205,7 @@ object V2XRepository : IRepository, IMGV2NListener {
/**
* 提供v2x坐标
*/
- fun provideLocation(loc: AMapLocation, sourceType: Int = 0) {
+ fun provideLocation(loc: MogoLocation, sourceType: Int = 0) {
loc.toMogoLocation(sourceType).also {
MGV2N.getInstance().uploadLocation(it)
}
diff --git a/libraries/mogo-speech/.gitignore b/libraries/mogo-speech/.gitignore
new file mode 100644
index 0000000000..f11e27d274
--- /dev/null
+++ b/libraries/mogo-speech/.gitignore
@@ -0,0 +1,11 @@
+*.iml
+.gradle
+/local.properties
+.idea/
+.DS_Store
+._.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+.gitlab-ci.yml
\ No newline at end of file
diff --git a/libraries/mogo-speech/build.gradle b/libraries/mogo-speech/build.gradle
new file mode 100644
index 0000000000..449ad99f39
--- /dev/null
+++ b/libraries/mogo-speech/build.gradle
@@ -0,0 +1,60 @@
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+ id 'kotlin-android-extensions'
+ id 'kotlin-kapt'
+}
+
+android {
+ compileSdkVersion rootProject.ext.android.compileSdkVersion
+ // buildToolsVersion rootProject.ext.android.buildToolsVersion
+ defaultConfig {
+ minSdkVersion rootProject.ext.android.minSdkVersion
+ targetSdkVersion rootProject.ext.android.targetSdkVersion
+ versionCode Integer.valueOf(VERSION_CODE)
+ versionName getValueFromRootProperties("${project.name.replace("-", "_").toUpperCase()}_VERSION")
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles 'consumer-rules.pro'
+
+ buildConfigField("String", "BAIDU_APP_ID", "\"${project.property("baiduAppId")}\"")
+ buildConfigField("String", "BAIDU_API_KEY", "\"${project.property("baiduApiKey")}\"")
+ buildConfigField("String", "BAIDU_SECRET_KEY", "\"${project.property("baiduSecretKey")}\"")
+
+ //ARouter apt 参数
+ kapt {
+ useBuildCache = false
+ arguments {
+ arg("AROUTER_MODULE_NAME", project.getName())
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation rootProject.ext.dependencies.kotlinstdlib
+ implementation rootProject.ext.dependencies.androidxccorektx
+ implementation rootProject.ext.dependencies.androidxappcompat
+ implementation rootProject.ext.dependencies.material
+
+ implementation(files("libs/bdasr_V3_20210628_cfe8c44.jar"))
+ implementation(files("libs/com.baidu.tts_2.6.3.c2aaa9f_20220922113422.jar"))
+}
diff --git a/libraries/mogo-speech/consumer-rules.pro b/libraries/mogo-speech/consumer-rules.pro
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/libraries/mogo-speech/gradle.properties b/libraries/mogo-speech/gradle.properties
new file mode 100644
index 0000000000..220e0a97c6
--- /dev/null
+++ b/libraries/mogo-speech/gradle.properties
@@ -0,0 +1,8 @@
+GROUP=com.mogo.speech
+POM_ARTIFACT_ID=mogo-speech
+VERSION_CODE=1
+
+#百度语音
+baiduAppId=117782292
+baiduApiKey=N4zeGxC1quVgyEmlmcvrteHC
+baiduSecretKey=xNuc1xVgmmW6NWDXYBq6z5e3qxSU5nUa
\ No newline at end of file
diff --git a/libraries/mogo-speech/libs/bdasr_V3_20210628_cfe8c44.jar b/libraries/mogo-speech/libs/bdasr_V3_20210628_cfe8c44.jar
new file mode 100644
index 0000000000..927c2813b5
Binary files /dev/null and b/libraries/mogo-speech/libs/bdasr_V3_20210628_cfe8c44.jar differ
diff --git a/libraries/mogo-speech/libs/com.baidu.tts_2.6.3.c2aaa9f_20220922113422.jar b/libraries/mogo-speech/libs/com.baidu.tts_2.6.3.c2aaa9f_20220922113422.jar
new file mode 100644
index 0000000000..7a6e87d7f1
Binary files /dev/null and b/libraries/mogo-speech/libs/com.baidu.tts_2.6.3.c2aaa9f_20220922113422.jar differ
diff --git a/libraries/mogo-speech/proguard-rules.pro b/libraries/mogo-speech/proguard-rules.pro
new file mode 100644
index 0000000000..481bb43481
--- /dev/null
+++ b/libraries/mogo-speech/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/AndroidManifest.xml b/libraries/mogo-speech/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..a253f68ad2
--- /dev/null
+++ b/libraries/mogo-speech/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/assets/WakeUp.bin b/libraries/mogo-speech/src/main/assets/WakeUp.bin
new file mode 100644
index 0000000000..44e9bbac5d
--- /dev/null
+++ b/libraries/mogo-speech/src/main/assets/WakeUp.bin
@@ -0,0 +1 @@
+B_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfEB_ZBOpfEOpfEOpfE
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/AsrResult.kt b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/AsrResult.kt
new file mode 100644
index 0000000000..79db05a68c
--- /dev/null
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/AsrResult.kt
@@ -0,0 +1,50 @@
+package com.mogo.mgintelligent.speech
+
+import android.util.Log
+import org.json.JSONObject
+
+data class AsrResult(val type: AsrState, val content: String)
+
+enum class AsrState {
+ /**
+ * 识别中
+ */
+ STATE_PARTIAL,
+
+ /**
+ * 识别结束
+ */
+ STATE_FINAL,
+
+ /**
+ * 退出识别
+ */
+ STATE_EXIT,
+
+ /**
+ * 识别错误
+ */
+ STATE_ERROR,
+}
+
+fun parseAsrResult(asrResult: String): AsrResult {
+ Log.d("ASR", "parseAsrResult: ")
+ val json = JSONObject(asrResult)
+ val error = json.optInt("error")
+ val resultType = json.optString("result_type")
+ val bestResult = json.optString("best_result")
+
+ if (error != 0) {
+ return AsrResult(AsrState.STATE_ERROR, "识别失败")
+ }
+
+ val state = when (resultType) {
+ "partial_result" -> AsrState.STATE_PARTIAL
+ "final_result" -> AsrState.STATE_FINAL
+ else -> AsrState.STATE_ERROR
+ }
+ val content = if (state == AsrState.STATE_ERROR) {
+ "识别错误"
+ } else bestResult
+ return AsrResult(state, content)
+}
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/DefaultSpeechListener.kt b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/DefaultSpeechListener.kt
new file mode 100644
index 0000000000..d94c36296c
--- /dev/null
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/DefaultSpeechListener.kt
@@ -0,0 +1,22 @@
+package com.mogo.mgintelligent.speech
+
+import com.baidu.tts.client.SpeechError
+import com.baidu.tts.client.SpeechSynthesizerListener
+
+interface DefaultSpeechListener : SpeechSynthesizerListener {
+
+ override fun onSynthesizeStart(utteranceId: String?) {}
+
+ override fun onSynthesizeDataArrived(utteranceId: String?, var2: ByteArray?, var3: Int, var4: Int) {}
+
+ override fun onSynthesizeFinish(utteranceId: String?) {}
+
+ override fun onSpeechStart(utteranceId: String?) {}
+
+ override fun onSpeechProgressChanged(utteranceId: String?, var2: Int) {}
+
+ override fun onSpeechFinish(utteranceId: String?) {}
+
+ override fun onError(utteranceId: String?, error: SpeechError?) {}
+
+}
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/IWakeUpListener.kt b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/IWakeUpListener.kt
new file mode 100644
index 0000000000..9b0311883d
--- /dev/null
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/IWakeUpListener.kt
@@ -0,0 +1,14 @@
+package com.mogo.mgintelligent.speech
+
+interface IWakeUpListener {
+
+ fun onWakeUp()
+
+ fun onListener(msg: AsrResult)
+
+}
+
+
+fun interface SpeakCallback {
+ fun finish()
+}
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/MGSpeech.kt b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/MGSpeech.kt
new file mode 100644
index 0000000000..88c9829bea
--- /dev/null
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/MGSpeech.kt
@@ -0,0 +1,289 @@
+package com.mogo.mgintelligent.speech
+
+import android.content.Context
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import com.baidu.speech.EventListener
+import com.baidu.speech.EventManager
+import com.baidu.speech.EventManagerFactory
+import com.baidu.speech.asr.SpeechConstant
+import com.baidu.tts.client.SpeechError
+import com.baidu.tts.client.SpeechSynthesizeBag
+import com.baidu.tts.client.SpeechSynthesizer
+import com.baidu.tts.client.TtsMode
+import org.json.JSONObject
+
+object MGSpeech {
+ private val appId = BuildConfig.BAIDU_APP_ID
+ private val apiKey = BuildConfig.BAIDU_API_KEY
+ private val secretKey = BuildConfig.BAIDU_SECRET_KEY
+ private val mSpeechSynthesizer: SpeechSynthesizer = SpeechSynthesizer.getInstance()
+
+ private var asr: EventManager? = null
+ private var wp: EventManager? = null
+
+ var weakUpListener: IWakeUpListener? = null
+
+ private val mainHandler = Handler(Looper.getMainLooper())
+
+ private var isListening = false
+
+ private var currAsrContent = ""
+
+ private var isAssistantShow = false
+
+ var mFinishedCallback: SpeakCallback? = null
+
+ val eventListener = EventListener { name, params, data, offset, length ->
+ Log.d(
+ TAG,
+ "[${Thread.currentThread().name}] onEvent: name=$name, param=$params, data=$data, offset=$offset, length=$length"
+ )
+ postMain {
+ when (name) {
+ SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS -> weakUpListener?.onWakeUp()
+ SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL ->
+ weakUpListener?.onListener(
+ parseAsrResult(params).apply {
+ currAsrContent = content
+ }
+ )
+
+ SpeechConstant.CALLBACK_EVENT_ASR_EXIT -> {
+ isListening = false
+ weakUpListener?.onListener(AsrResult(AsrState.STATE_EXIT, currAsrContent))
+ }
+
+ SpeechConstant.CALLBACK_EVENT_ASR_FINISH,
+ SpeechConstant.CALLBACK_EVENT_ASR_CANCEL -> {
+ isListening = false
+ }
+ }
+ }
+ }
+
+ private const val TAG = "MGSpeech"
+ fun init(context: Context) {
+
+ initTts(context)
+
+ initAsr(context)
+ }
+
+ private fun initAsr(context: Context) {
+ asr = EventManagerFactory.create(context, "asr")
+ wp = EventManagerFactory.create(context, "wp")
+ }
+
+ fun startAsr() {
+ Log.d(TAG, "startAsr: ")
+ isListening = true
+ currAsrContent = ""
+ val params = mapOf(
+ SpeechConstant.APP_ID to appId,
+ SpeechConstant.APP_KEY to apiKey,
+ SpeechConstant.SECRET to secretKey,
+ SpeechConstant.ACCEPT_AUDIO_VOLUME to false,
+ SpeechConstant.VAD to SpeechConstant.VAD_DNN,
+ SpeechConstant.PID to 1537,
+ SpeechConstant.LMID to 21867,
+ SpeechConstant.VAD_ENDPOINT_TIMEOUT to 10000
+ )
+
+ asr?.registerListener(eventListener)
+ val json = JSONObject(params).toString()
+ asr?.send(SpeechConstant.ASR_START, json, null, 0, 0)
+ }
+
+ fun stopAsr() {
+ Log.d(TAG, "stopAsr: ")
+ isListening = false
+ asr?.unregisterListener(eventListener)
+ asr?.send(SpeechConstant.ASR_STOP, "{}", null, 0, 0)
+ }
+
+
+ fun startWeakUp() {
+ Log.d(TAG, "startWeakUp: ")
+ val params = mapOf(
+ SpeechConstant.APP_ID to appId,
+ SpeechConstant.APP_KEY to apiKey,
+ SpeechConstant.SECRET to secretKey,
+ SpeechConstant.WP_WORDS_FILE to "assets:///WakeUp.bin"
+ )
+ wp?.registerListener(eventListener)
+
+
+// // 复制此段可以自动检测常规错误
+// AutoCheckAsr(context, object : Handler() {
+// override fun handleMessage(msg: Message) {
+// if (msg.what == 100) {
+// val autoCheck = msg.obj as AutoCheckAsr
+// synchronized(autoCheck) {
+// val message =
+// autoCheck.obtainErrorMessage() // autoCheck.obtainAllMessage();
+//// txtLog.append(message + "\n")
+// // 可以用下面一行替代,在logcat中查看代码
+// Log.d(TAG, "AutoCheckAsr: $message")
+// }
+// }
+// }
+// }, false).checkAsr(params)
+
+
+// val json = JSONObject(params).toString()
+// wp?.send(SpeechConstant.WAKEUP_START, json, null, 0, 0)
+ }
+
+ fun stopWeakUp() {
+ Log.d(TAG, "stopWeakUp: ")
+ wp?.unregisterListener(eventListener)
+ wp?.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0)
+ }
+
+ private fun initTts(context: Context) {
+ mSpeechSynthesizer.setContext(context)
+ mSpeechSynthesizer.setAppId(appId)
+ mSpeechSynthesizer.setApiKey(apiKey, secretKey)
+ mSpeechSynthesizer.initTts(TtsMode.ONLINE)
+ // 设置在线发声音人: 0 普通女声(默认) 1 普通男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫>, 其它发音人见文档
+ mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0")
+ // 设置合成的音量,0-15 ,默认 5
+ mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "15")
+ // 设置合成的语速,0-15 ,默认 5
+ mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "7")
+ // 设置合成的语调,0-15 ,默认 5
+ mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5")
+
+// mSpeechSynthesizer.setParam(
+// SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE,
+// "assets:///bd_etts_common_text_txt_all_mand_eng_middle_big_v4.1.0_20230423.dat"
+// )
+// mSpeechSynthesizer.setParam(
+// SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE,
+// "assets:///bd_etts_common_speech_duyaya_mand_eng_high_am-style24k_v4.6.0_20210721_20220822104311.dat"
+// )
+
+// val params: MutableMap = HashMap()
+// // 以下参数均为选填
+// // 设置在线发声音人: 0 普通女声(默认) 1 普通男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫>, 其它发音人见文档
+// params[SpeechSynthesizer.PARAM_SPEAKER] = "5118"
+// // 设置合成的音量,0-15 ,默认 5
+// params[SpeechSynthesizer.PARAM_VOLUME] = "15"
+// // 设置合成的语速,0-15 ,默认 5
+// params[SpeechSynthesizer.PARAM_SPEED] = "5"
+// // 设置合成的语调,0-15 ,默认 5
+// params[SpeechSynthesizer.PARAM_PITCH] = "5"
+ //
+ // val initConfig = InitConfig(appId, apiKey, secretKey, TtsMode.ONLINE, params, null)
+// // 如果您集成中出错,请将下面一段代码放在和demo中相同的位置,并复制InitConfig 和 AutoCheck到您的项目中
+// // 上线时请删除AutoCheck的调用
+// AutoCheck.getInstance(context.getApplicationContext())
+// .check(initConfig, object : Handler() {
+// override fun handleMessage(msg: Message) {
+// if (msg.what == 100) {
+// val autoCheck: AutoCheck = msg.obj as AutoCheck
+// synchronized(autoCheck) {
+// val message: String = autoCheck.obtainDebugMessage()
+//
+// Log.e(TAG, "handleMessage: $message")
+// }
+// }
+// }
+// })
+
+ mSpeechSynthesizer.setSpeechSynthesizerListener(object : DefaultSpeechListener {
+ override fun onSpeechStart(utteranceId: String?) {
+ Log.d(TAG, "[${Thread.currentThread().name}] onSpeechStart: $utteranceId")
+ }
+
+ override fun onSpeechFinish(utteranceId: String?) {
+ Log.d(TAG, "[${Thread.currentThread().name}] onSpeechFinish: $utteranceId")
+ if (utteranceId == null) {
+ return
+ }
+ val progress = utteranceId.toFloatOrNull() ?: return
+ if (progress == 1F) {
+ postMain {
+ mFinishedCallback?.finish()
+ }
+ }
+ }
+
+ override fun onError(utteranceId: String?, error: SpeechError?) {
+ Log.d(TAG, "[${Thread.currentThread().name}] onError: $utteranceId, $error")
+ if (utteranceId == null) {
+ return
+ }
+
+ val progress = utteranceId.toFloatOrNull() ?: return
+ if (progress == 1F) {
+ postMain {
+ mFinishedCallback?.finish()
+ }
+ }
+ }
+ })
+ }
+
+ fun speak(text: String, isAssistant:Boolean = false, callback: SpeakCallback? = null) {
+ if(text.isEmpty()){
+ Log.d(TAG, "speak: 空文本")
+ mFinishedCallback = callback
+ callback?.finish()
+ return
+ }
+
+ if (isListening) {
+ Log.d(TAG, "speak: 当前正在语音识别,本次tts取消,$text")
+ return
+ }
+
+ if (isAssistantShow && !isAssistant) {
+ Log.d(TAG, "speak: 当前语音助手正在展示,本次tts取消,$text")
+ return
+ }
+ Log.d(TAG, "speak: $text")
+ mSpeechSynthesizer.stop()
+ mFinishedCallback = callback
+
+ if (text.length <= 40) {
+ mSpeechSynthesizer.speak(text, "1")
+ return
+ }
+
+ val subTxtList = text.split("[。,;,.;]".toRegex()).filter { it.isNotEmpty() }
+ val subTxtSize = subTxtList.size
+ Log.d(TAG, "speak bags: $subTxtSize")
+ if (subTxtSize == 1) {
+ mSpeechSynthesizer.speak(text, "1")
+ return
+ }
+
+ val bags = subTxtList.mapIndexed { index, txt ->
+// //需要合成的文本text的长度不能超过120个GBK字节。
+ val id = "${(index + 1F) / subTxtSize}"
+ Log.d(TAG, "speak bags: utteranceId=$id, text=$txt ")
+ SpeechSynthesizeBag().apply {
+ setText(txt)
+ utteranceId = id
+ }
+ }
+
+ mSpeechSynthesizer.batchSpeak(bags)
+ }
+
+ fun isAssistantShow(isAssistantShow: Boolean) {
+ this.isAssistantShow = isAssistantShow
+ }
+
+ fun stopSpeak() {
+ mSpeechSynthesizer.stop()
+ }
+
+ fun postMain(runnable: Runnable) {
+ mainHandler.post(runnable)
+ }
+
+}
\ No newline at end of file
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/AutoCheck.java b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/AutoCheck.java
new file mode 100644
index 0000000000..8897d96c51
--- /dev/null
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/AutoCheck.java
@@ -0,0 +1,460 @@
+package com.mogo.mgintelligent.speech.debug;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Message;
+
+import androidx.core.content.ContextCompat;
+
+import com.baidu.tts.client.SpeechSynthesizer;
+import com.baidu.tts.client.SynthesizerTool;
+import com.baidu.tts.client.TtsMode;
+
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeSet;
+
+/**
+ * 自动排查工具,用于集成后发现错误。
+ *
+ * 可以检测如下错误:
+ * 1. PermissionCheck : AndroidManifest,xml 需要的部分权限
+ * 2. JniCheck: 检测so文件是否安装在指定目录
+ * 3. AppInfoCheck: 联网情况下 , 检测appId appKey secretKey是否正确
+ * 4. ApplicationIdCheck: 显示包名applicationId, 提示用户手动去官网检查
+ * 5. ParamKeyExistCheck: 检查key是否存在,目前检查 SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE
+ * 和PARAM_TTS_SPEECH_MODEL_FILE
+ * 6. OfflineResourceFileCheck 检查离线资源文件(需要从assets目录下复制),是否存在
+ *
+ *
+ * 示例使用代码:
+ * AutoCheck.getInstance(getApplicationContext()).check(initConfig, new Handler() {
+ *
+ * @Override public void handleMessage(Message msg) {
+ * if (msg.what == 100) {
+ * AutoCheck autoCheck = (AutoCheck) msg.obj;
+ * synchronized (autoCheck) {
+ * String message = autoCheck.obtainDebugMessage();
+ * toPrint(message); // 可以用下面一行替代,在logcat中查看代码
+ * //Log.w("AutoCheckMessage",message);
+ * }
+ * }
+ * }
+ *
+ * });
+ */
+public class AutoCheck {
+
+ private static AutoCheck instance;
+
+ private LinkedHashMap checks;
+
+ private static Context context;
+
+ private boolean hasError = false;
+
+ volatile boolean isFinished = false;
+
+ /**
+ * 获取实例,非线程安全
+ *
+ * @return
+ */
+ public static AutoCheck getInstance(Context context) {
+ if (instance == null || AutoCheck.context != context) {
+ instance = new AutoCheck(context);
+ }
+ return instance;
+ }
+
+ public void check(final InitConfig initConfig, final Handler handler) {
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ AutoCheck obj = innerCheck(initConfig);
+ isFinished = true;
+ synchronized (obj) { // 偶发,同步线程信息
+ Message msg = handler.obtainMessage(100, obj);
+ handler.sendMessage(msg);
+ }
+ }
+ });
+ t.start();
+
+ }
+
+ private AutoCheck innerCheck(InitConfig config) {
+ boolean isOnlineSdk = TtsMode.ONLINE.equals(config.getTtsMode());
+ checks.put("检查申请的Android权限", new PermissionCheck(context));
+ checks.put("检查4个so文件是否存在", new JniCheck(context, isOnlineSdk));
+ checks.put("检查AppId AppKey SecretKey",
+ new AppInfoCheck(config.getAppId(), config.getAppKey(), config.getSecretKey()));
+ checks.put("检查包名", new ApplicationIdCheck(context, config.getAppId()));
+
+ if (!isOnlineSdk) {
+ Map params = config.getParams();
+ String fileKey = SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE;
+ checks.put("检查离线资TEXT文件参数", new ParamKeyExistCheck(params, fileKey,
+ "SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE未设置 ,"));
+ checks.put("检查离线资源TEXT文件", new OfflineResourceFileCheck(params.get(fileKey)));
+ fileKey = SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE;
+ checks.put("检查离线资Speech文件参数", new ParamKeyExistCheck(params, fileKey,
+ "SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE未设置 ,"));
+ checks.put("检查离线资源Speech文件", new OfflineResourceFileCheck(params.get(fileKey)));
+ }
+
+ for (Map.Entry e : checks.entrySet()) {
+ Check check = e.getValue();
+ check.check();
+ if (check.hasError()) {
+ break;
+ }
+ }
+ return this;
+ }
+
+ public String obtainErrorMessage() {
+ PrintConfig config = new PrintConfig();
+ return formatString(config);
+ }
+
+ public String obtainDebugMessage() {
+ PrintConfig config = new PrintConfig();
+ config.withInfo = true;
+ return formatString(config);
+ }
+
+ public String obtainAllMessage() {
+ PrintConfig config = new PrintConfig();
+ config.withLog = true;
+ config.withInfo = true;
+ return formatString(config);
+ }
+
+ public String formatString(PrintConfig config) {
+ StringBuilder sb = new StringBuilder();
+ hasError = false;
+
+ for (HashMap.Entry entry : checks.entrySet()) {
+ Check check = entry.getValue();
+ String testName = entry.getKey();
+ if (check.hasError()) {
+ if (!hasError) {
+ hasError = true;
+ }
+
+ sb.append("【错误】【").append(testName).append(" 】 ").append(check.getErrorMessage()).append("\n");
+ if (check.hasFix()) {
+ sb.append("【修复方法】【").append(testName).append(" 】 ").append(check.getFixMessage()).append("\n");
+ }
+ }
+ if (config.withInfo && check.hasInfo()) {
+ sb.append("【请手动检查】【").append(testName).append("】 ").append(check.getInfoMessage()).append("\n");
+ }
+ if (config.withLog && (config.withLogOnSuccess || hasError) && check.hasLog()) {
+ sb.append("【log】:" + check.getLogMessage()).append("\n");
+ }
+ }
+ if (!hasError) {
+ sb.append("集成自动排查工具: 恭喜没有检测到任何问题\n");
+ }
+ return sb.toString();
+ }
+
+ public void clear() {
+ checks.clear();
+ hasError = false;
+ }
+
+ private AutoCheck(Context context) {
+ this.context = context;
+ checks = new LinkedHashMap<>();
+ }
+
+ private static class PrintConfig {
+ public boolean withFix = true;
+ public boolean withInfo = false;
+ public boolean withLog = false;
+ public boolean withLogOnSuccess = false;
+ }
+
+ private static class PermissionCheck extends Check {
+ private Context context;
+
+ public PermissionCheck(Context context) {
+ this.context = context;
+ }
+
+ @Override
+ public void check() {
+ String[] permissions = {
+ Manifest.permission.INTERNET,
+ Manifest.permission.ACCESS_NETWORK_STATE,
+ Manifest.permission.MODIFY_AUDIO_SETTINGS,
+ // Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ // Manifest.permission.WRITE_SETTINGS,
+ Manifest.permission.ACCESS_WIFI_STATE,
+ // Manifest.permission.CHANGE_WIFI_STATE
+ };
+
+ ArrayList toApplyList = new ArrayList();
+
+ for (String perm : permissions) {
+ if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(context, perm)) {
+ toApplyList.add(perm);
+ // 进入到这里代表没有权限.
+ }
+ }
+ if (!toApplyList.isEmpty()) {
+ errorMessage = "缺少权限:" + toApplyList;
+ fixMessage = "请从AndroidManifest.xml复制相关权限";
+ }
+ }
+ }
+
+ private static class JniCheck extends Check {
+ private Context context;
+
+ private String[] soNames;
+
+ public JniCheck(Context context, boolean isOnlineSdk) {
+ this.context = context;
+ if (isOnlineSdk) {
+ soNames = new String[]{"libBDSpeechDecoder_V1.so"};
+ } else {
+ soNames = new String[]{"libbd_etts.so", "libBDSpeechDecoder_V1.so", "libgnustl_shared.so"};
+ }
+ }
+
+ @Override
+ public void check() {
+ String path = context.getApplicationInfo().nativeLibraryDir;
+ appendLogMessage("Jni so文件目录 " + path);
+ File[] files = new File(path).listFiles();
+ TreeSet set = new TreeSet<>();
+ if (files != null) {
+ for (File file : files) {
+ if (file.canRead()) {
+ set.add(file.getName());
+ }
+ }
+ }
+ appendLogMessage("Jni目录内文件: " + set.toString());
+ for (String name : soNames) {
+ if (!set.contains(name)) {
+ errorMessage = "Jni目录" + path + " 缺少可读的so文件:" + name + ", 该目录文件列表: " + set.toString();
+ fixMessage = "如果您的app内没有其它so文件,请复制demo里的src/main/jniLibs至同名目录。"
+ + " 如果app内有so文件,请合并目录放一起(注意目录取交集,多余的目录删除)。";
+ break;
+ }
+ }
+ }
+ }
+
+ private static class ParamKeyExistCheck extends Check {
+ private Map params;
+ private String key;
+ private String prefixErrorMessage;
+
+ public ParamKeyExistCheck(Map params, String key, String prefixErrorMessage) {
+ this.params = params;
+ this.key = key;
+ this.prefixErrorMessage = prefixErrorMessage;
+ }
+
+ @Override
+ public void check() {
+ if (params == null || !params.containsKey(key)) {
+ errorMessage = prefixErrorMessage + " 参数中没有设置:" + key;
+ fixMessage = "请参照demo在设置 " + key + "参数";
+ }
+ }
+ }
+
+ private static class OfflineResourceFileCheck extends Check {
+ private String filename;
+
+ public OfflineResourceFileCheck(String filename) {
+ this.filename = filename;
+ }
+
+ @Override
+ public void check() {
+ File file = new File(filename);
+ if (!file.exists()) {
+ errorMessage = "资源文件不存在:" + filename;
+ } else if (!file.canRead()) {
+ errorMessage = "资源文件不可读:" + filename;
+ } else if (!SynthesizerTool.verifyModelFile(filename)) {
+ errorMessage = "SDK verifyModelFile方法判断模型文件不是有效的,请重新复制:" + filename;
+ }
+ if (hasError()) {
+ fixMessage = "请将demo中src/main/assets目录下同名文件复制到 " + filename;
+ }
+ }
+ }
+
+ private static class ApplicationIdCheck extends Check {
+
+ private String appId;
+ private Context context;
+
+ public ApplicationIdCheck(Context context, String appId) {
+ this.appId = appId;
+ this.context = context;
+ }
+
+ @Override
+ public void check() {
+ infoMessage = "如果您集成过程中遇见离线合成初始化问题,请检查网页上appId:" + appId
+ + " 应用是否开通了合成服务,并且网页上的应用填写了Android包名:"
+ + getApplicationId();
+ }
+
+ private String getApplicationId() {
+ return context.getPackageName();
+ }
+ }
+
+
+ private static class AppInfoCheck extends Check {
+ private String appId;
+ private String appKey;
+ private String secretKey;
+
+ public AppInfoCheck(String appId, String appKey, String secretKey) {
+ this.appId = appId;
+ this.appKey = appKey;
+ this.secretKey = secretKey;
+ }
+
+
+ public void check() {
+ do {
+ appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
+ if (appId == null || appId.isEmpty()) {
+ errorMessage = "appId 为空";
+ fixMessage = "填写appID";
+ break;
+ }
+ if (appKey == null || appKey.isEmpty()) {
+ errorMessage = "appKey 为空";
+ fixMessage = "填写appID";
+ break;
+ }
+ if (secretKey == null || secretKey.isEmpty()) {
+ errorMessage = "secretKey 为空";
+ fixMessage = "secretKey";
+ break;
+ }
+ } while (false);
+ try {
+ checkOnline();
+ } catch (UnknownHostException e) {
+ infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
+ } catch (Exception e) {
+ errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
+ fixMessage = " 重新检测appId, appKey, appSecret是否正确";
+ }
+ }
+
+ public void checkOnline() throws Exception {
+ String urlpath = "https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id="
+ + appKey + "&client_secret=" + secretKey;
+ URL url = new URL(urlpath);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.setConnectTimeout(1000);
+ InputStream is = conn.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder result = new StringBuilder();
+ String line = "";
+ do {
+ line = reader.readLine();
+ if (line != null) {
+ result.append(line);
+ }
+ } while (line != null);
+ String res = result.toString();
+ appendLogMessage("openapi return " + res);
+ JSONObject jsonObject = new JSONObject(res);
+ String error = jsonObject.optString("error");
+ if (error != null && !error.isEmpty()) {
+ throw new Exception("appkey secretKey 错误" + ", error:" + error + ", json is" + result);
+ }
+ String token = jsonObject.getString("access_token");
+ if (token == null || !token.endsWith("-" + appId)) {
+ throw new Exception("appId 与 appkey及 appSecret 不一致。appId = " + appId + " ,token = " + token);
+ }
+ }
+
+
+ }
+
+ private abstract static class Check {
+ protected String errorMessage = null;
+
+ protected String fixMessage = null;
+
+ protected String infoMessage = null;
+
+ protected StringBuilder logMessage;
+
+ public Check() {
+ logMessage = new StringBuilder();
+ }
+
+ public abstract void check();
+
+ public boolean hasError() {
+ return errorMessage != null;
+ }
+
+ public boolean hasFix() {
+ return fixMessage != null;
+ }
+
+ public boolean hasInfo() {
+ return infoMessage != null;
+ }
+
+ public boolean hasLog() {
+ return !logMessage.toString().isEmpty();
+ }
+
+ public void appendLogMessage(String message) {
+ logMessage.append(message + "\n");
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public String getFixMessage() {
+ return fixMessage;
+ }
+
+ public String getInfoMessage() {
+ return infoMessage;
+ }
+
+ public String getLogMessage() {
+ return logMessage.toString();
+ }
+
+
+ }
+}
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/AutoCheckAsr.java b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/AutoCheckAsr.java
new file mode 100644
index 0000000000..396c0cb941
--- /dev/null
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/AutoCheckAsr.java
@@ -0,0 +1,472 @@
+package com.mogo.mgintelligent.speech.debug;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import androidx.core.content.ContextCompat;
+
+import com.baidu.speech.asr.SpeechConstant;
+
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeSet;
+
+import javax.net.ssl.HttpsURLConnection;
+
+public class AutoCheckAsr {
+ public static final boolean isOnlineLited = false; // 是否只需要是纯在线识别功能
+ private LinkedHashMap checks;
+
+ private Context context;
+ private Handler handler;
+
+ private boolean hasError;
+ private boolean enableOffline;
+ private boolean isFinished = false;
+
+ private String name;
+
+ private static final String TAG = "AutoCheck";
+
+ public AutoCheckAsr(Context context, final Handler handler, boolean enableOffline) {
+ this.context = context;
+ checks = new LinkedHashMap<>();
+ this.handler = handler;
+ this.enableOffline = enableOffline;
+ }
+
+ public void checkAsr(final Map params) {
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ AutoCheckAsr obj = checkAsrInternal(params);
+ name = "识别";
+ synchronized (obj) { // 偶发,同步线程信息
+ isFinished = true;
+ Message msg = handler.obtainMessage(100, obj);
+ handler.sendMessage(msg);
+ }
+ }
+ });
+ t.start();
+ }
+
+ public String obtainErrorMessage() {
+ PrintConfig config = new PrintConfig();
+ return formatString(config);
+ }
+
+ public String obtainDebugMessage() {
+ PrintConfig config = new PrintConfig();
+ config.withInfo = true;
+ return formatString(config);
+ }
+
+ public String obtainAllMessage() {
+ PrintConfig config = new PrintConfig();
+ config.withLog = true;
+ config.withInfo = true;
+ config.withLogOnSuccess = true;
+ return formatString(config);
+ }
+
+ private String formatString(PrintConfig config) {
+ StringBuilder sb = new StringBuilder();
+ hasError = false;
+
+ for (HashMap.Entry entry : checks.entrySet()) {
+ Check check = entry.getValue();
+ String testName = entry.getKey();
+ if (check.hasError()) {
+ if (!hasError) {
+ hasError = true;
+ }
+
+ sb.append("【错误】【").append(testName).append(" 】 ").append(check.getErrorMessage()).append("\n");
+ Log.e("AutoCheck", sb.toString());
+ if (check.hasFix()) {
+ sb.append("【修复方法】【").append(testName).append(" 】 ").append(check.getFixMessage()).append("\n");
+ }
+ } else if (config.withEachCheckInfo) {
+ sb.append("【无报错】【").append(testName).append(" 】 ").append("\n");
+ }
+ if (config.withInfo && check.hasInfo()) {
+ sb.append("【请手动检查】【").append(testName).append("】 ").append(check.getInfoMessage()).append("\n");
+ }
+ if (config.withLog && (config.withLogOnSuccess || hasError) && check.hasLog()) {
+ sb.append("【log】:" + check.getLogMessage()).append("\n");
+ }
+ }
+ if (!hasError) {
+ sb.append("【" + name + "】集成自动排查工具: 恭喜没有检测到任何问题\n");
+ }
+ return sb.toString();
+ }
+
+ private AutoCheckAsr checkAsrInternal(Map params) {
+ commonSetting(params);
+ checks.put("外部音频文件存在校验", new FileCheck(context, params, SpeechConstant.IN_FILE));
+ checks.put("离线命令词及本地语义bsg文件存在校验",
+ new FileCheck(context, params, SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH));
+ for (Map.Entry e : checks.entrySet()) {
+ Check check = e.getValue();
+ check.check();
+ if (check.hasError()) {
+ break;
+ }
+ }
+ return this;
+ }
+
+ private void commonSetting(Map params) {
+ checks.put("检查申请的Android权限", new PermissionCheck(context));
+ checks.put("检查so文件是否存在", new JniCheck(context));
+ AppInfoCheck infoCheck = null;
+ try {
+ infoCheck = new AppInfoCheck(context, params);
+ checks.put("检查AppId AppKey SecretKey", infoCheck);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ Log.e(TAG, "检查AppId AppKey SecretKey 错误", e);
+ return;
+ }
+ if (enableOffline) {
+ checks.put("检查包名", new ApplicationIdCheck(context, infoCheck.appId));
+ }
+
+ }
+
+ private static class PrintConfig {
+ public boolean withEachCheckInfo = false;
+ public boolean withInfo = false;
+ public boolean withLog = false;
+ public boolean withLogOnSuccess = false;
+ }
+
+
+ private static class PermissionCheck extends Check {
+ private Context context;
+
+ public PermissionCheck(Context context) {
+ this.context = context;
+ }
+
+ @Override
+ public void check() {
+ String[] permissions = {
+ Manifest.permission.RECORD_AUDIO,
+ Manifest.permission.ACCESS_NETWORK_STATE,
+ Manifest.permission.INTERNET,
+ // Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ };
+
+ ArrayList toApplyList = new ArrayList();
+ for (String perm : permissions) {
+ if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(context, perm)) {
+ toApplyList.add(perm);
+ // 进入到这里代表没有权限.
+ }
+ }
+ if (!toApplyList.isEmpty()) {
+ errorMessage = "缺少权限:" + toApplyList;
+ fixMessage = "请从AndroidManifest.xml复制相关权限";
+ }
+ }
+ }
+
+ private static class JniCheck extends Check {
+ private Context context;
+
+ private String[] soNames;
+
+ public JniCheck(Context context) {
+ this.context = context;
+ if (isOnlineLited) {
+ soNames = new String[]{"libBaiduSpeechSDK.so", "libvad.dnn.so"};
+ } else {
+ soNames = new String[]{"libBaiduSpeechSDK.so", "libvad.dnn.so",
+ "libbd_easr_s1_merge_normal_20151216.dat.so", "libbdEASRAndroid.so",
+ "libbdSpilWakeup.so"};
+ }
+ }
+
+ @Override
+ public void check() {
+ String path = context.getApplicationInfo().nativeLibraryDir;
+ appendLogMessage("Jni so文件目录 " + path);
+ File[] files = new File(path).listFiles();
+ TreeSet set = new TreeSet<>();
+ if (files != null) {
+ for (File file : files) {
+ set.add(file.getName());
+ }
+ }
+ // String debugMessage = "Jni目录内文件: " + set.toString();
+ // boolean isSuccess = true;
+ for (String name : soNames) {
+ if (!set.contains(name)) {
+ errorMessage = "Jni目录" + path + " 缺少so文件:" + name + ", 该目录文件列表: " + set.toString();
+ fixMessage = "如果您的app内没有其它so文件,请复制demo里的src/main/jniLibs至同名目录。"
+ + " 如果app内有so文件,请合并目录放一起(注意目录取交集,多余的目录删除)。";
+ break;
+ }
+ }
+ }
+ }
+
+ private static class AppInfoCheck extends Check {
+ private String appId;
+ private String appKey;
+ private String secretKey;
+
+ public AppInfoCheck(Context context, Map params) throws PackageManager.NameNotFoundException {
+
+ if (params.get(SpeechConstant.APP_ID) != null) {
+ appId = params.get(SpeechConstant.APP_ID).toString();
+ }
+ if (params.get(SpeechConstant.APP_KEY) != null) {
+ appKey = params.get(SpeechConstant.APP_KEY).toString();
+ }
+
+ if (params.get(SpeechConstant.SECRET) != null) {
+ secretKey = params.get(SpeechConstant.SECRET).toString();
+ }
+ }
+
+
+ public void check() {
+ do {
+ appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
+ if (appId == null || appId.isEmpty()) {
+ errorMessage = "appId 为空";
+ fixMessage = "填写appID";
+ break;
+ }
+ if (appKey == null || appKey.isEmpty()) {
+ errorMessage = "appKey 为空";
+ fixMessage = "填写appID";
+ break;
+ }
+ if (secretKey == null || secretKey.isEmpty()) {
+ errorMessage = "secretKey 为空";
+ fixMessage = "secretKey";
+ break;
+ }
+
+
+ try {
+ checkOnline();
+ } catch (UnknownHostException e) {
+ infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
+ } catch (Exception e) {
+ errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
+ fixMessage = " 重新检测appId, appKey, appSecret是否正确";
+ }
+ } while (false);
+ }
+
+ public void checkOnline() throws Exception {
+ String urlpath = "https://openapi.baidu.com/oauth/2.0/token?client_id="
+ + appKey + "&client_secret=" + secretKey + "&grant_type=client_credentials";
+ Log.i("AutoCheck", "Url is " + urlpath);
+ URL url = new URL(urlpath);
+ HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.setConnectTimeout(1000);
+ InputStream is = conn.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder result = new StringBuilder();
+ String line = "";
+ do {
+ line = reader.readLine();
+ if (line != null) {
+ result.append(line);
+ }
+ } while (line != null);
+ String res = result.toString();
+ if (!res.contains("audio_voice_assistant_get")) {
+ errorMessage = "appid:" + appId + ",没有audio_voice_assistant_get 权限,请在网页上开通\"语音识别\"能力";
+ fixMessage = "secretKey";
+ return;
+ }
+ appendLogMessage("openapi return " + res);
+ JSONObject jsonObject = new JSONObject(res);
+ String error = jsonObject.optString("error");
+ if (error != null && !error.isEmpty()) {
+ errorMessage = "appkey secretKey 错误" + ", error:" + error + ", json is" + result;
+ fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
+ return;
+ }
+ String token = jsonObject.getString("access_token");
+ if (token == null || !token.endsWith("-" + appId)) {
+ errorMessage = "appId 与 appkey及 appSecret 不一致。appId = " + appId + " ,token = " + token;
+ fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
+ }
+ }
+ }
+
+ private static class ApplicationIdCheck extends Check {
+
+ private String appId;
+ private Context context;
+
+ public ApplicationIdCheck(Context context, String appId) {
+ this.appId = appId;
+ this.context = context;
+ }
+
+ @Override
+ public void check() {
+ infoMessage = "如果您集成过程中遇见离线命令词或者唤醒初始化问题,请检查网页上appId:" + appId
+ + " 应用填写了Android包名:"
+ + getApplicationId();
+ }
+
+ private String getApplicationId() {
+ return context.getPackageName();
+ }
+ }
+
+ private static class FileCheck extends Check {
+ private Map params;
+ private String key;
+ private Context context;
+ private boolean allowRes = false;
+ private boolean allowAssets = true;
+
+ public FileCheck(Context context, Map params, String key) {
+ this.context = context;
+ this.params = params;
+ this.key = key;
+ if (key.equals(SpeechConstant.IN_FILE)) {
+ allowRes = true;
+ allowAssets = false;
+ }
+ }
+
+ @Override
+ public void check() {
+ if (!params.containsKey(key)) {
+ return;
+ }
+ String value = params.get(key).toString();
+ if (allowAssets) {
+ int len = "assets".length();
+ int totalLen = len + ":///".length();
+ if (value.startsWith("assets")) {
+ String filename = value.substring(totalLen);
+ if (!":///".equals(value.substring(len, totalLen)) || filename.isEmpty()) {
+ errorMessage = "参数:" + key + "格式错误:" + value;
+ fixMessage = "修改成" + "assets:///sdcard/xxxx.yyy";
+ }
+ try {
+ context.getAssets().open(filename);
+ } catch (IOException e) {
+ errorMessage = "assets 目录下,文件不存在:" + filename;
+ fixMessage = "demo的assets目录是:src/main/assets";
+ e.printStackTrace();
+ }
+ appendLogMessage("assets 检验完毕:" + filename);
+ }
+ }
+ if (allowRes) {
+ int len = "res".length();
+ int totalLen = len + ":///".length();
+ if (value.startsWith("res")) {
+ String filename = value.substring(totalLen);
+ if (!":///".equals(value.substring(len, totalLen)) || filename.isEmpty()) {
+ errorMessage = "参数:" + key + "格式错误:" + value;
+ fixMessage = "修改成" + "res:///com/baidu/android/voicedemo/16k_test.pcm";
+ }
+ InputStream is = getClass().getClassLoader().getResourceAsStream(filename);
+ if (is == null) {
+ errorMessage = "res,文件不存在:" + filename;
+ fixMessage = "demo的res目录是:app/src/main/resources";
+ } else {
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ appendLogMessage("res 检验完毕:" + filename);
+ }
+ }
+ if (value.startsWith("/")) {
+ if (!new File(value).canRead()) {
+ errorMessage = "文件不存在:" + value;
+ fixMessage = "请查看文件是否存在";
+ }
+ appendLogMessage("文件路径 检验完毕:" + value);
+ }
+ }
+ }
+
+ private abstract static class Check {
+ protected String errorMessage = null;
+
+ protected String fixMessage = null;
+
+ protected String infoMessage = null;
+
+ protected StringBuilder logMessage;
+
+ public Check() {
+ logMessage = new StringBuilder();
+ }
+
+ public abstract void check();
+
+ public boolean hasError() {
+ return errorMessage != null;
+ }
+
+ public boolean hasFix() {
+ return fixMessage != null;
+ }
+
+ public boolean hasInfo() {
+ return infoMessage != null;
+ }
+
+ public boolean hasLog() {
+ return !logMessage.toString().isEmpty();
+ }
+
+ public void appendLogMessage(String message) {
+ logMessage.append(message + "\n");
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public String getFixMessage() {
+ return fixMessage;
+ }
+
+ public String getInfoMessage() {
+ return infoMessage;
+ }
+
+ public String getLogMessage() {
+ return logMessage.toString();
+ }
+ }
+}
+
diff --git a/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/InitConfig.java b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/InitConfig.java
new file mode 100644
index 0000000000..83f7a587ef
--- /dev/null
+++ b/libraries/mogo-speech/src/main/java/com/mogo/mgintelligent/speech/debug/InitConfig.java
@@ -0,0 +1,98 @@
+package com.mogo.mgintelligent.speech.debug;
+
+import com.baidu.tts.client.SpeechSynthesizerListener;
+import com.baidu.tts.client.TtsMode;
+
+import java.util.Map;
+
+/**
+ * 合成引擎的初始化参数
+ *
+ * Created by fujiayi on 2017/9/13.
+ */
+
+public class InitConfig {
+ /**
+ * appId appKey 和 secretKey。注意如果需要离线合成功能,请在您申请的应用中填写包名。
+ * 本demo的包名是com.baidu.tts.sample,定义在build.gradle中。
+ */
+ private String appId;
+
+ private String appKey;
+
+ private String secretKey;
+
+ private String sn;
+
+ /**
+ * 纯在线或者离在线融合、纯离线
+ */
+ private TtsMode ttsMode;
+
+
+ /**
+ * 初始化的其它参数,用于setParam
+ */
+ private Map params;
+
+ /**
+ * 合成引擎的回调
+ */
+ private SpeechSynthesizerListener listener;
+
+ private InitConfig() {
+
+ }
+
+ // 在线SDK用
+ public InitConfig(String appId, String appKey, String secretKey, TtsMode ttsMode,
+ Map params, SpeechSynthesizerListener listener) {
+ this.appId = appId;
+ this.appKey = appKey;
+ this.secretKey = secretKey;
+ this.ttsMode = ttsMode;
+ this.params = params;
+ this.listener = listener;
+ }
+
+
+ // 纯离线SDK用
+ public InitConfig(String appId, String appKey, String secretKey, String sn, TtsMode ttsMode,
+ Map params, SpeechSynthesizerListener listener) {
+ this(appId, appKey, secretKey, ttsMode, params, listener);
+ this.sn = sn;
+ if (sn != null) {
+ // 纯离线sdk 才有的参数;在线版本没有
+// params.put(IOfflineResourceConst.PARAM_SN_NAME, sn);
+ }
+ }
+
+ public SpeechSynthesizerListener getListener() {
+ return listener;
+ }
+
+ public Map getParams() {
+ return params;
+ }
+
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public String getAppKey() {
+ return appKey;
+ }
+
+ public String getSecretKey() {
+ return secretKey;
+ }
+
+ public TtsMode getTtsMode() {
+ return ttsMode;
+ }
+
+ public String getSn() {
+ return sn;
+ }
+}
diff --git a/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libBDSpeechDecoder_V1.so b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libBDSpeechDecoder_V1.so
new file mode 100644
index 0000000000..a8107b157e
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libBDSpeechDecoder_V1.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libBaiduSpeechSDK.so b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libBaiduSpeechSDK.so
new file mode 100644
index 0000000000..2f16021408
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libBaiduSpeechSDK.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbdEASRAndroid.so b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbdEASRAndroid.so
new file mode 100644
index 0000000000..c824ce9e50
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbdEASRAndroid.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbdSpilWakeup.so b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbdSpilWakeup.so
new file mode 100644
index 0000000000..a4816337b9
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbdSpilWakeup.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbd_easr_s1_merge_normal_20151216.dat.so b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbd_easr_s1_merge_normal_20151216.dat.so
new file mode 100644
index 0000000000..826c81fc5d
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbd_easr_s1_merge_normal_20151216.dat.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbd_etts.so b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbd_etts.so
new file mode 100644
index 0000000000..f313d921bb
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libbd_etts.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libgnustl_shared.so b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libgnustl_shared.so
new file mode 100644
index 0000000000..be99b75ae5
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libgnustl_shared.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libvad.dnn.so b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libvad.dnn.so
new file mode 100644
index 0000000000..0550061c2e
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/arm64-v8a/libvad.dnn.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libBDSpeechDecoder_V1.so b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libBDSpeechDecoder_V1.so
new file mode 100644
index 0000000000..b08c4aa686
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libBDSpeechDecoder_V1.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libBaiduSpeechSDK.so b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libBaiduSpeechSDK.so
new file mode 100644
index 0000000000..5538ab647a
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libBaiduSpeechSDK.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbdEASRAndroid.so b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbdEASRAndroid.so
new file mode 100644
index 0000000000..0fef52af53
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbdEASRAndroid.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbdSpilWakeup.so b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbdSpilWakeup.so
new file mode 100644
index 0000000000..f7809e2dd3
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbdSpilWakeup.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbd_easr_s1_merge_normal_20151216.dat.so b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbd_easr_s1_merge_normal_20151216.dat.so
new file mode 100644
index 0000000000..826c81fc5d
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbd_easr_s1_merge_normal_20151216.dat.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbd_etts.so b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbd_etts.so
new file mode 100644
index 0000000000..66e2b2f1af
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libbd_etts.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libgnustl_shared.so b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libgnustl_shared.so
new file mode 100644
index 0000000000..96c22a2586
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libgnustl_shared.so differ
diff --git a/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libvad.dnn.so b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libvad.dnn.so
new file mode 100644
index 0000000000..0550061c2e
Binary files /dev/null and b/libraries/mogo-speech/src/main/jniLibs/armeabi-v7a/libvad.dnn.so differ
diff --git a/settings.gradle b/settings.gradle
index 0d12a00a55..3560a3f445 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -45,6 +45,7 @@ include ':libraries:mogo-adas'
include ':libraries:mogo-adas-data'
include ':libraries:mogo-obu'
include ':libraries:mogo-hardware-devices'
+include ':libraries:mogo-speech'
//include ':libraries:mapmodule'
// 语音