[V2X]优化V2X弹窗语音加载逻辑

This commit is contained in:
renwj
2022-07-01 19:57:29 +08:00
parent 82af6dda9c
commit 914cafaef6
12 changed files with 172 additions and 118 deletions

View File

@@ -1,18 +1,26 @@
package com.mogo.eagle.core.function.hmi.ui
import android.animation.Animator
import android.content.*
import android.graphics.*
import android.graphics.drawable.*
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.text.TextUtils
import android.transition.*
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.view.WindowManager.LayoutParams
import android.view.animation.*
import android.widget.*
import androidx.core.view.*
import androidx.lifecycle.lifecycleScope
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.commons.mvp.MvpFragment
import com.mogo.commons.voice.AIAssist
import com.mogo.commons.voice.*
import com.mogo.eagle.core.data.bindingcar.AdUpgradeStateHelper
import com.mogo.eagle.core.data.bindingcar.IPCUpgradeStateInfo
import com.mogo.eagle.core.data.camera.CameraEntity
@@ -27,6 +35,7 @@ import com.mogo.eagle.core.data.report.ReportEntity
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotRecordListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.api.hmi.IMoGoHmiViewProxy
import com.mogo.eagle.core.function.api.hmi.IMoGoHmiViewProxy.IViewNotificationProvider
import com.mogo.eagle.core.function.api.hmi.view.IViewLimitingVelocity
import com.mogo.eagle.core.function.api.hmi.view.IViewNotification
import com.mogo.eagle.core.function.api.hmi.view.IViewTrafficLight
@@ -55,14 +64,19 @@ import com.mogo.eagle.core.function.hmi.ui.setting.ReportListFloatWindow
import com.mogo.eagle.core.function.hmi.ui.tools.AdUpgradeDialog
import com.mogo.eagle.core.function.hmi.ui.tools.AutoPilotAndCheckView
import com.mogo.eagle.core.function.hmi.ui.widget.V2XNotificationView
import com.mogo.eagle.core.utilcode.kotlin.*
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.*
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_HMI
import com.mogo.eagle.core.utilcode.reminder.*
import com.mogo.eagle.core.utilcode.reminder.api.*
import com.mogo.eagle.core.utilcode.reminder.api.IReminder.IStateChangeListener
import com.mogo.eagle.core.utilcode.reminder.api.impl.*
import com.mogo.eagle.core.utilcode.util.SoundUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.TimeUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.module.common.enums.EventTypeEnum
import com.mogo.module.common.enums.*
import kotlinx.android.synthetic.main.fragment_hmi.*
import kotlinx.coroutines.*
import mogo_msg.MogoReportMsg
@@ -82,15 +96,13 @@ import kotlin.collections.ArrayList
IMoGoHmiViewProxy,
MoGoHmiContract.View,
IMoGoAutopilotRecordListener,
IMoGoAutopilotStatusListener {
IMoGoAutopilotStatusListener, IViewNotificationProvider {
private val TAG = "MoGoHmiFragment"
// DebugSettingView
private var mDebugSettingViewFloat: WarningFloat.Builder? = null
private var mDebugSettingView: DebugSettingView? = null
// V2X、OBU、云端推送预警弹窗
private var mWarningFloat: WarningFloat.Builder? = null
private var mNoticeFloat: WarningFloat.Builder? = null
@@ -113,7 +125,7 @@ import kotlin.collections.ArrayList
private var mViewLimitingVelocity: IViewLimitingVelocity? = null
// V2X预警弹窗 View 代理
private var mViewNotification: IViewNotification? = null
private var mViewNotificationProvider: IViewNotificationProvider? = null
//工控机节点上报列表
private var reportList = arrayListOf<ReportEntity>()
@@ -123,6 +135,8 @@ import kotlin.collections.ArrayList
private var adUpgradeDialog: AdUpgradeDialog?=null
private var speakJob: Job? = null
override fun vipIdentification(visible: Boolean) {
ThreadUtils.runOnUiThread {
if (visible) {
@@ -174,7 +188,7 @@ import kotlin.collections.ArrayList
// 首次初始化使用默认视图
setProxyTrafficLightView(viewTrafficLightVr)
setProxyLimitingSpeedView(viewLimitingVelocity)
setProxyNotificationView(V2XNotificationView(view.context))
setViewNotificationProvider(this)
context?.also {
if (!AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)) {
@@ -183,7 +197,8 @@ import kotlin.collections.ArrayList
}
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun getNotificationView(): IViewNotification? = context?.let { V2XNotificationView(it) }
override fun onAutopilotRecordResult(recordPanel: RecordPanelOuterClass.RecordPanel) {
if (HmiBuildConfig.isShowBadCaseView && recordPanel.type == 1 && recordPanel.stat == 100) {
CallerDevaToolsManager.onReceiveBadCaseRecord(recordPanel)
@@ -256,11 +271,8 @@ import kotlin.collections.ArrayList
}
}
/**
* 设置 V2X 通知 代理View
*/
override fun setProxyNotificationView(view: IViewNotification) {
mViewNotification = view
override fun setViewNotificationProvider(provider: IViewNotificationProvider) {
mViewNotificationProvider = provider
}
/**
@@ -525,88 +537,97 @@ import kotlin.collections.ArrayList
alertContent: CharSequence?,
ttsContent: String?,
tag: String?,
listenerIMoGo: IMoGoWarningStatusListener?,
listener: IMoGoWarningStatusListener?,
playTts: Boolean,
expireTime: Long
) {
val playTTS = playTts && !AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)
lifecycleScope.launchWhenResumed {
activity?.let {
val floatWindow = mWarningFloat
val showTag = floatWindow?.config?.floatTag
if (floatWindow == null || TextUtils.isEmpty(showTag) || !floatWindow.isShow() || floatWindow.config.floatTag != tag) {
// 代理View初始化了才可以弹窗
mViewNotification?.let { notificationView ->
notificationView.setWarningIcon(EventTypeEnum.getWarningIcon(v2xType))
val warningContent = alertContent
?: EventTypeEnum.getWarningContent(v2xType)
if (warningContent.isEmpty()) {
CallerLogger.e("$M_HMI$TAG", "Show warningContent is null or empty!")
return@launchWhenResumed
} else {
notificationView.setWarningContent(warningContent)
}
if (floatWindow != null && floatWindow.isShow()) {
showWarning(WarningDirectionEnum.ALERT_WARNING_NON)
WarningFloat.dismiss(floatWindow.config.floatTag, true)
}
mWarningFloat = WarningFloat.with(it)
.setTag(tag)
.setLayout(notificationView)
.setSidePattern(notificationView.sidePattern)
.setCountDownTime(expireTime)
.setGravity(notificationView.layoutGravity, offsetX = notificationView.offsetX, offsetY = notificationView.offsetY)
.setImmersionStatusBar(true)
.isEnqueue(true)
.addWarningStatusListener(listenerIMoGo)
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onShow() {
// 创建弹窗成功才进行TTS播报
CallerLogger.d(
"$M_HMI$TAG",
"mWarningFloat = $mWarningFloat---ttsContent = $ttsContent"
)
if (mWarningFloat != null && !TextUtils.isEmpty(ttsContent) && playTTS) {
CallerLogger.d("$M_HMI$TAG", "---> ttsContent = $ttsContent")
AIAssist.getInstance(activity)
.speakTTSVoice(ttsContent)
}
}
override fun onDismiss() {
showWarning(WarningDirectionEnum.ALERT_WARNING_NON)
}
})
.setAnimator(object : DefaultAnimator() {
override fun enterAnim(
view: View,
params: LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.enterAnim(view, params, windowManager, sidePattern)?.apply {
interpolator = OvershootInterpolator()
}
override fun exitAnim(
view: View,
params: LayoutParams,
windowManager: WindowManager,
sidePattern: SidePattern
): Animator? =
super.exitAnim(view, params, windowManager, sidePattern)
?.setDuration(200)
})
.show()
}
} else {
val notification = floatWindow.config.layoutView as? V2XNotificationView
if (alertContent?.isNotEmpty() == true) {
notification?.setWarningContent(alertContent)
floatWindow.resetExpireTime(expireTime)
activity?.let {
val warningContent = alertContent
?: EventTypeEnum.getWarningContent(v2xType)
if (warningContent.isEmpty()) {
CallerLogger.e("$M_HMI$TAG", "Show warningContent is null or empty!")
return
}
speakJob?.safeCancel()
val content = mViewNotificationProvider?.getNotificationView() ?: return
content.setWarningIcon(EventTypeEnum.getWarningIcon(v2xType))
content.setWarningContent(warningContent)
var reminder: IReminder? = null
Reminder.enqueue(this@MoGoHmiFragment, object : PopupWindowReminder(PopupWindow(content, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).also { itx ->
itx.isTouchable = false
itx.isFocusable = false
itx.isClippingEnabled = false
itx.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
val transition = Slide(Gravity.TOP).also { t ->
t.interpolator = AccelerateDecelerateInterpolator()
t.duration = 200
}
if (VERSION.SDK_INT >= VERSION_CODES.M) {
itx.enterTransition = transition
itx.exitTransition = transition
}
}) {
override fun show() {
val parent = it.window.decorView
parent.doOnAttach {
popupWindow.showAtLocation(parent, Gravity.TOP, 0, 0)
}
}
override fun isOverride(): Boolean {
return true
}
}.also { itx -> reminder = itx }, object : IStateChangeListener {
override fun onShow(reminder: IReminder) {
listener?.onShow()
if (ttsContent != null && !TextUtils.isEmpty(ttsContent) && playTTS) {
CallerLogger.d("$M_HMI$TAG", "---> ttsContent = $ttsContent")
lifecycleScope.launch {
speak(it, ttsContent)
}.also {
speakJob = it
}
}
}
override fun onHide(reminder: IReminder) {
listener?.onDismiss()
showWarning(WarningDirectionEnum.ALERT_WARNING_NON)
}
})
if (reminder == null) {
return
}
lifecycleScope.launch {
delay(expireTime)
reminder?.hide()
}
}
}
private suspend fun speak(ctx: Context, text: String) = suspendCancellableCoroutine<Unit> {
try {
val voiceCallback = object : IMogoVoiceCmdCallBack {
override fun onSpeakEnd(speakText: String?) {
super.onSpeakEnd(speakText)
it.resumeWith(Result.success(Unit))
}
override fun onSpeakError(speakText: String?, errorMsg: String?) {
super.onSpeakError(speakText, errorMsg)
it.resumeWith(Result.success(Unit))
}
}
it.invokeOnCancellation {
AIAssist.getInstance(ctx).stopSpeakTts(text)
}
AIAssist.getInstance(ctx).speakTTSVoice(text, voiceCallback)
} catch (t: Throwable) {
it.resumeWith(Result.success(Unit))
Logger.e(TAG, t.message)
}
}

View File

@@ -32,8 +32,7 @@ class V2XNotificationView @JvmOverloads constructor(
sidePattern = SidePattern.RESULT_TOP
layoutGravity = Gravity.CENTER_HORIZONTAL
// 设置View的停留位置
offsetX = 0
offsetY = 110
setPadding(0, 110, 0, 0)
}
override fun setWarningIcon(@DrawableRes warningIcon: Int) {

View File

@@ -63,6 +63,16 @@ public class TestPanelBroadcastReceiver extends BroadcastReceiver {
intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
// 存储本地,出行动态作展示
saveLocalStory(V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING,
v2XMessageEntity.getContent().getNoveltyInfo());
} if (sceneType == 2) {// 触发AI道路施工事件
V2XMessageEntity<V2XRoadEventEntity> v2XMessageEntity =
TestOnLineCarUtils.getV2XScenarioAIRoadEventData();
Intent intent = new Intent(V2XConst.BROADCAST_SCENE_HANDLER_ACTION);
intent.putExtra(V2XConst.BROADCAST_SCENE_EXTRA_KEY, v2XMessageEntity);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
// 存储本地,出行动态作展示
saveLocalStory(V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING,
v2XMessageEntity.getContent().getNoveltyInfo());

View File

@@ -1,5 +1,7 @@
package com.mogo.eagle.core.function.v2x.events.utils;
import static com.mogo.module.common.entity.V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING;
import com.mogo.eagle.core.data.map.MogoLatLng;
import com.mogo.eagle.core.function.v2x.R;
import com.mogo.eagle.core.function.v2x.events.entity.net.V2XOptimalRouteDataRes;
@@ -7,10 +9,12 @@ import com.mogo.eagle.core.function.v2x.events.entity.net.V2XSpecialCarRes;
import com.mogo.eagle.core.network.utils.GsonUtil;
import com.mogo.eagle.core.utilcode.util.Utils;
import com.mogo.module.common.entity.MarkerExploreWay;
import com.mogo.module.common.entity.MarkerLocation;
import com.mogo.module.common.entity.MarkerResponse;
import com.mogo.module.common.entity.V2XMessageEntity;
import com.mogo.module.common.entity.V2XRoadEventEntity;
import com.mogo.module.common.entity.V2XWarningEntity;
import com.mogo.v2x.event.V2XEvent;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
@@ -80,7 +84,7 @@ public class TestOnLineCarUtils {
V2XMessageEntity<V2XRoadEventEntity> v2xMessageEntity = new V2XMessageEntity<>();
// 控制类型
v2xMessageEntity.setType(V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING);
v2xMessageEntity.setType(ALERT_ROAD_WARNING);
// 设置数据
v2xMessageEntity.setContent(v2xRoadEventEntity);
// 控制展示状态
@@ -92,7 +96,22 @@ public class TestOnLineCarUtils {
return null;
}
/**
* 模拟道路事件测试数据
*/
public static V2XMessageEntity<V2XRoadEventEntity> getV2XScenarioAIRoadEventData() {
V2XRoadEventEntity entity = new V2XRoadEventEntity();
entity.setLocation(new MarkerLocation());
entity.setShowEventButton(false);
entity.setPoiType("100061");
entity.setExpireTime(20000);
V2XMessageEntity<V2XRoadEventEntity> body = new V2XMessageEntity<>();
body.setOnlyShow(false);
body.setShowState(true);
body.setContent(entity);
body.setType(ALERT_ROAD_WARNING);
return body;
}
/**
* 模拟道路事件UGC测试数据
*/