Merge branch 'dev_robotaxi-d-app-module_290_220715_2.9.0' into dev_robotaxi-d-app-module-videoprocess
# Conflicts: # OCH/mogo-och-taxi-passenger/src/main/res/values/strings.xml
@@ -315,6 +315,10 @@ class MoGoAutopilotProvider :
|
||||
return AdasManager.getInstance().startRecordPackage(id, duration, type)
|
||||
}
|
||||
|
||||
override fun recordPackage(type: Int, id: Int, duration: Int, bduration: Int): Boolean {
|
||||
return AdasManager.getInstance().startRecordPackage(id,duration, type, bduration)
|
||||
}
|
||||
|
||||
override fun stopRecord(type: Int, id: Int): Boolean {
|
||||
return AdasManager.getInstance().stopRecordPackage(id, type)
|
||||
}
|
||||
@@ -353,17 +357,14 @@ class MoGoAutopilotProvider :
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 演示模式(美化模式)
|
||||
* 演示模式(美化模式)设置只限定于鹰眼
|
||||
* isEnable = true 开启
|
||||
* isEnable = false 关闭
|
||||
*/
|
||||
override fun setDemoMode(isEnable: Boolean) {
|
||||
if (isEnable) {
|
||||
AdasManager.getInstance().sendDemoModeReq(1)
|
||||
} else {
|
||||
AdasManager.getInstance().sendDemoModeReq(0)
|
||||
}
|
||||
// 同步给乘客端
|
||||
if (AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
|
||||
var byteArray = if (isEnable) byteArrayOf(1) else byteArrayOf(0)
|
||||
@@ -382,6 +383,19 @@ class MoGoAutopilotProvider :
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置工控机演示模式(美化模式)开启、关闭
|
||||
* isEnable = true 开启
|
||||
* isEnable = false 关闭
|
||||
*/
|
||||
override fun setIPCDemoMode(isEnable: Boolean) {
|
||||
if (isEnable) {
|
||||
AdasManager.getInstance().sendDemoModeReq(1)
|
||||
} else {
|
||||
AdasManager.getInstance().sendDemoModeReq(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 雨天模式
|
||||
* isEnable = true 开启
|
||||
@@ -395,6 +409,13 @@ class MoGoAutopilotProvider :
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据采集录制模式配置列表
|
||||
*/
|
||||
override fun getBadCaseConfig() {
|
||||
AdasManager.getInstance().sendRecordDataConfigReq()
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送工控机所有节点重启命令
|
||||
*/
|
||||
@@ -508,4 +529,9 @@ class MoGoAutopilotProvider :
|
||||
override fun getTeleTimeStamp(): Long {
|
||||
return msgHandler.getTeleTimeStamp()
|
||||
}
|
||||
|
||||
override fun sendStatusQueryReq() {
|
||||
Log.d(TAG, "---- sendStatusQueryReq ----")
|
||||
AdasManager.getInstance().sendStatusQueryReq()
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager.invokeAutoPilotStatus
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager.invokeAutopilotGuardian
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager.invokeAutopilotSNRequest
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager.invokeAutopilotStatusRespByQuery
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotCarConfigListenerManager.invokeAutopilotCarConfigData
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotCarStatusListenerManager.invokeAutopilotCarStateData
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
|
||||
@@ -43,6 +44,7 @@ import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotPointCloudList
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotRecordListenerManager.invokeAutopilotRecordResult
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotVehicleStateListenerManager
|
||||
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager
|
||||
import com.mogo.eagle.core.network.utils.*
|
||||
import com.zhidao.support.adas.high.AdasManager
|
||||
import com.zhidao.support.adas.high.OnAdasListener
|
||||
import com.zhidao.support.adas.high.common.ProtocolStatus
|
||||
@@ -244,10 +246,11 @@ class MoGoAdasListenerImpl : OnAdasListener {
|
||||
//他车轨迹预测
|
||||
}
|
||||
|
||||
override fun onPointCloud(header: MessagePad.Header?, pointCloud: MogoPointCloudOuterClass.MogoPointCloud?) {
|
||||
//点云数据透传
|
||||
//Logger.d("pointCloud","pointCloud"+pointCloud);
|
||||
}
|
||||
// override fun onPointCloud(header: MessagePad.Header?, pointCloud: MogoPointCloudOuterClass.MogoPointCloud?) {
|
||||
// //点云数据透传
|
||||
// //Logger.d("pointCloud","pointCloud"+pointCloud);
|
||||
// CallerAutopilotPointCloudListenerManager.invokeAutopilotPointCloudDataUpdate(header, pointCloud)
|
||||
// }
|
||||
|
||||
override fun onPointCloud(pointCloud: ByteArray?) {
|
||||
//点云数据透传
|
||||
@@ -369,6 +372,20 @@ class MoGoAdasListenerImpl : OnAdasListener {
|
||||
statusInfo: SystemStatusInfo.StatusInfo?
|
||||
) {
|
||||
//状态查询应答
|
||||
statusInfo?.let {
|
||||
Log.d(TAG, GsonUtil.jsonFromObject(it))
|
||||
}
|
||||
invokeAutopilotStatusRespByQuery(statusInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据采集配置应答
|
||||
*/
|
||||
override fun onRecordDataConfigResp(
|
||||
header: MessagePad.Header?,
|
||||
config: MessagePad.RecordDataConfig?
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ class MoGoAdasMsgConnectStatusListenerImpl : OnAdasConnectStatusListener,
|
||||
updateDriveStatusTask()
|
||||
//每次工控机连接成功后,需同步当前设置的美化模式状态
|
||||
CallerAutoPilotManager.setDemoMode(FunctionBuildConfig.isDemoMode)
|
||||
//当连接状态是关闭美化模式时,同步给工控机
|
||||
if(!FunctionBuildConfig.isDemoMode){
|
||||
CallerAutoPilotManager.setIPCDemoMode(FunctionBuildConfig.isDemoMode)
|
||||
}
|
||||
//每次工控机连接成功后,需同步当前设置的雨天模式状态
|
||||
CallerAutoPilotManager.setRainMode(FunctionBuildConfig.isRainMode)
|
||||
} else if (ipcConnectionStatus == Constants.IPC_CONNECTION_STATUS.DISCONNECTED) {
|
||||
|
||||
@@ -90,6 +90,7 @@ dependencies {
|
||||
implementation rootProject.ext.dependencies.mogo_core_function_api
|
||||
implementation rootProject.ext.dependencies.mogo_core_function_call
|
||||
implementation rootProject.ext.dependencies.mogo_core_data
|
||||
implementation rootProject.ext.dependencies.mogo_core_res
|
||||
}else {
|
||||
implementation project(':services:mogo-service-api')
|
||||
implementation project(':modules:mogo-module-common')
|
||||
@@ -98,6 +99,7 @@ dependencies {
|
||||
implementation project(':core:mogo-core-function-api')
|
||||
implementation project(':core:mogo-core-function-call')
|
||||
implementation project(':core:mogo-core-data')
|
||||
implementation project(':core:mogo-core-res')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,12 @@ class DevaToolsProvider : IDevaToolsProvider {
|
||||
sceneManager.updateSceneTAG(sceneTag)
|
||||
}
|
||||
|
||||
override fun initBadCase(view: View, onShow: (() -> Unit)?, onHide: (() -> Unit)?) {
|
||||
BadCaseManager.init(view, onShow, onHide)
|
||||
override fun initBadCase(view: View) {
|
||||
BadCaseManager.initBadCase(view)
|
||||
}
|
||||
|
||||
override fun initAiCollect(view: View) {
|
||||
BadCaseManager.initAiCollect(view)
|
||||
}
|
||||
|
||||
override fun onReceiveBadCaseRecord(record: RecordPanelOuterClass.RecordPanel) {
|
||||
@@ -87,7 +91,7 @@ class DevaToolsProvider : IDevaToolsProvider {
|
||||
}
|
||||
|
||||
override fun showFeedbackWindow(ctx: Context) {
|
||||
FeedbackManager.showFeedbackWindow(ctx)
|
||||
BadCaseManager.showBadCaseConfigWindow(ctx)
|
||||
}
|
||||
|
||||
override fun getUpgradeVersionUrls(versionName: String) {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase
|
||||
|
||||
import android.transition.AutoTransition
|
||||
import android.transition.TransitionManager
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.Lifecycle.Event
|
||||
@@ -15,24 +14,24 @@ import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotVehicleStateListenerManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_DEVA
|
||||
import com.mogo.eagle.core.utilcode.kotlin.PX
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.kotlin.lifecycleOwner
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_DEVA
|
||||
import com.mogo.eagle.core.utilcode.reminder.Reminder
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.IReminder
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.IReminder.IGlobalStateChangeListener
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.repository.net.api.entity.BadCaseResponse.Reason
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.biz.BadCasePresenter
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.biz.BadCaseView
|
||||
import com.mogo.eagle.core.utilcode.util.ClickUtils
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.biz.*
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.repository.net.api.entity.BadCaseResponse.Reason
|
||||
import com.zhjt.mogo_core_function_devatools.ext.enqueuePop
|
||||
import com.zhjt.mogo_core_function_devatools.ext.toast
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import record_cache.RecordPanelOuterClass
|
||||
import java.lang.IllegalStateException
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@@ -46,9 +45,6 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
|
||||
private val CASE_EXPIRE_DURATION: Long = TimeUnit.HOURS.toMillis(4)/* TimeUnit.SECONDS.toMillis(10) */
|
||||
|
||||
private var onShow: (() -> Unit)? = null
|
||||
private var onHide: (() -> Unit)? = null
|
||||
|
||||
private var hideFloat: (() -> Unit)? = null
|
||||
|
||||
@Volatile
|
||||
@@ -85,19 +81,69 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
field
|
||||
}
|
||||
|
||||
fun init(view: View, onShow: (() -> Unit)?, onHide: (() -> Unit)?) {
|
||||
this.viewHolder = WeakReference(view)
|
||||
view.lifecycleOwner.lifecycle.addObserver(this)
|
||||
this.onShow = onShow
|
||||
this.onHide = onHide
|
||||
register()
|
||||
recoverBadCase()
|
||||
/**
|
||||
* 展示BadCase配置页面
|
||||
*/
|
||||
fun showBadCaseConfigWindow(context: Context){
|
||||
val badCaseConfigView = BadCaseConfigView(context)
|
||||
badCaseConfigView.setClickListener(object: BadCaseConfigView.ClickListener{
|
||||
override fun onClose() {
|
||||
hideFloat?.invoke()
|
||||
hideFloat = null
|
||||
}
|
||||
})
|
||||
context.enqueuePop(badCaseConfigView,960.PX, WindowManager.LayoutParams.MATCH_PARENT, key = "BadCaseConfigView").also {
|
||||
hideFloat = it
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动采集BadCase
|
||||
*/
|
||||
fun initBadCase(view: View){
|
||||
val activity = view.context as? FragmentActivity ?: throw IllegalStateException("please ensure context is FragmentActivity.")
|
||||
view.setOnClickListener {
|
||||
if(ClickUtils.isFastClick()){
|
||||
val initiativeBadCaseWindow = InitiativeBadCaseWindow(activity)
|
||||
initiativeBadCaseWindow.setClickListener(object: InitiativeBadCaseWindow.ClickListener{
|
||||
override fun closeWindow() {
|
||||
initiativeBadCaseWindow.hideFloatWindow()
|
||||
}
|
||||
|
||||
})
|
||||
initiativeBadCaseWindow.showFloatWindow()
|
||||
}else{
|
||||
ToastUtils.showShort("请勿连续点击,稍后再试")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AI数据采集
|
||||
*/
|
||||
fun initAiCollect(view: View){
|
||||
val activity = view.context as? FragmentActivity ?: throw IllegalStateException("please ensure context is FragmentActivity.")
|
||||
view.setOnClickListener {
|
||||
if(ClickUtils.isFastClick()){
|
||||
val aiDataCollectWindow = AIDataCollectWindow(activity)
|
||||
aiDataCollectWindow.setClickListener(object: AIDataCollectWindow.ClickListener{
|
||||
override fun closeWindow() {
|
||||
aiDataCollectWindow.hideFloatWindow()
|
||||
}
|
||||
})
|
||||
aiDataCollectWindow.showFloatWindow()
|
||||
}else{
|
||||
ToastUtils.showShort("请勿连续点击,稍后再试")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun register() {
|
||||
scope?.launch(Dispatchers.Default) {
|
||||
while (true) {
|
||||
showBadCaseInternal(AutoPilotRecord())
|
||||
CallerLogger.d("$M_DEVA$TAG", "---- 开始监听BadCase事件 ----")
|
||||
val old = record
|
||||
if (old == null || old.consumed) {
|
||||
@@ -131,12 +177,6 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
showBadCaseInternal(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CallerLogger.d("$M_DEVA$TAG", "record: [$old] hasn't been consumed~~~~")
|
||||
withContext(Dispatchers.Main) {
|
||||
showEntry()
|
||||
}
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +235,7 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
private fun CoroutineScope.showBadCaseInternal(record: AutoPilotRecord) = launch {
|
||||
viewHolder?.get()?.also { itx ->
|
||||
presenter.updateLastModified(CallerAutopilotVehicleStateListenerManager.getAutopilotTimeStamp())
|
||||
showEntry()
|
||||
|
||||
itx.onClick {
|
||||
showBadCaseFloat(
|
||||
onDismiss = {
|
||||
@@ -221,7 +261,7 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
withContext(Dispatchers.IO) {
|
||||
presenter.deleteRecord(record)
|
||||
}
|
||||
hideEntry()
|
||||
|
||||
hideFloat?.invoke()
|
||||
hideFloat = null
|
||||
}
|
||||
@@ -235,7 +275,6 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
dismissJob?.takeIf { it.isActive }?.cancel()
|
||||
return scope?.launch {
|
||||
delay(CASE_EXPIRE_DURATION)
|
||||
hideEntry()
|
||||
record?.also {
|
||||
it.consumed = true
|
||||
withContext(Dispatchers.IO) {
|
||||
@@ -245,20 +284,6 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
}
|
||||
}
|
||||
|
||||
private fun showEntry() {
|
||||
viewHolder?.get()?.takeIf { it.visibility != View.VISIBLE }?.also {
|
||||
it.toggle(true)
|
||||
onShow?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideEntry() {
|
||||
viewHolder?.get()?.takeIf { it.visibility == View.VISIBLE }?.also {
|
||||
it.toggle(false)
|
||||
onHide?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showBadCaseFloat(onDismiss: () -> Unit, onSelect:suspend (reason: Reason) -> Unit) {
|
||||
val activity = viewHolder?.get()?.context as? FragmentActivity ?: throw IllegalStateException("please ensure context is FragmentActivity.")
|
||||
BadCaseView(activity).also { itx ->
|
||||
@@ -291,21 +316,12 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
if (event == ON_DESTROY) {
|
||||
Reminder.unRegisterGlobalStateChangeListener(listener)
|
||||
dismissJob?.takeIf { it.isActive }?.cancel()
|
||||
onHide = null
|
||||
onShow = null
|
||||
hideFloat = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : View> T.toggle(show: Boolean) {
|
||||
val group = (parent as? ViewGroup) ?: return
|
||||
val target = if (show) View.VISIBLE else View.GONE
|
||||
takeIf { it.visibility != target }?.also {
|
||||
TransitionManager.beginDelayedTransition(group, AutoTransition())
|
||||
it.visibility = target
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun RecordPanelOuterClass.RecordPanel.toRecord(): AutoPilotRecord =
|
||||
AutoPilotRecord().also {
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.biz
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.graphics.PixelFormat
|
||||
import android.os.SystemClock
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.*
|
||||
import android.widget.RadioButton
|
||||
import android.widget.TextView
|
||||
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotRecordListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_DEVA
|
||||
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast
|
||||
import com.mogo.eagle.core.utilcode.util.TimeUtils
|
||||
import com.mogo.eagle.core.utilcode.util.TimeUtils.millis2String
|
||||
import com.zhjt.mogo_core_function_devatools.R
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.consts.BadCaseConfig
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.toRecord
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import record_cache.RecordPanelOuterClass
|
||||
import java.lang.reflect.Field
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author XuXinChao
|
||||
* @description AI数据采集弹窗
|
||||
* @since: 2022/7/12
|
||||
*/
|
||||
class AIDataCollectWindow constructor(activity: Activity) : View.OnTouchListener,
|
||||
IMoGoAutopilotRecordListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "AIDataCollectWindow"
|
||||
}
|
||||
|
||||
private var mActivity: Activity = activity
|
||||
private var mWindowParams: WindowManager.LayoutParams? = null
|
||||
private var mWindowManager: WindowManager? = null
|
||||
|
||||
private lateinit var tvCollectNum: TextView //采集弹窗数量
|
||||
private lateinit var tvCollectTime: TextView //采集时间
|
||||
|
||||
private lateinit var rbLargeCar: RadioButton //大型车
|
||||
private lateinit var rbTrafficLight: RadioButton //交通灯
|
||||
private lateinit var rbWater: RadioButton //积水
|
||||
private lateinit var rbConstruction: RadioButton //施工
|
||||
private lateinit var rbAccident: RadioButton //车祸路段
|
||||
private lateinit var rbRain: RadioButton //中雨交通流
|
||||
private lateinit var rbNightTraffic: RadioButton //夜间交通流
|
||||
|
||||
private lateinit var tvCollectReport: TextView //上报按钮
|
||||
private lateinit var tvCollectCancel: TextView //取消按钮
|
||||
|
||||
private var collectReason: String = "大型车:大货、大巴、特种车辆"
|
||||
|
||||
private lateinit var mFloatLayout: View
|
||||
private var mInViewX = 0f
|
||||
private var mInViewY = 0f
|
||||
private var mDownInScreenX = 0f
|
||||
private var mDownInScreenY = 0f
|
||||
private var mInScreenX = 0f
|
||||
private var mInScreenY = 0f
|
||||
|
||||
private var clickListener: ClickListener? = null
|
||||
|
||||
init {
|
||||
initFloatWindow();
|
||||
}
|
||||
|
||||
private val presenter by lazy {
|
||||
BadCasePresenter()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun initFloatWindow(){
|
||||
mFloatLayout = LayoutInflater.from(mActivity).inflate(R.layout.view_ai_data_collect, null) as View
|
||||
mFloatLayout.setOnTouchListener(this)
|
||||
tvCollectNum = mFloatLayout.findViewById(R.id.tvCollectNum)
|
||||
tvCollectTime = mFloatLayout.findViewById(R.id.tvCollectTime)
|
||||
|
||||
rbLargeCar = mFloatLayout.findViewById(R.id.rbLargeCar)
|
||||
rbTrafficLight = mFloatLayout.findViewById(R.id.rbTrafficLight)
|
||||
rbWater = mFloatLayout.findViewById(R.id.rbWater)
|
||||
rbConstruction = mFloatLayout.findViewById(R.id.rbConstruction)
|
||||
rbAccident = mFloatLayout.findViewById(R.id.rbAccident)
|
||||
rbRain = mFloatLayout.findViewById(R.id.rbRain)
|
||||
rbNightTraffic = mFloatLayout.findViewById(R.id.rbNightTraffic)
|
||||
|
||||
tvCollectReport = mFloatLayout.findViewById(R.id.tvCollectReport)
|
||||
tvCollectCancel = mFloatLayout.findViewById(R.id.tvCollectCancel)
|
||||
|
||||
tvCollectNum.text = BadCaseConfig.windowNum.toString()
|
||||
BadCaseConfig.windowNum++
|
||||
tvCollectTime.text ="时间:${millis2String(System.currentTimeMillis(),TimeUtils.getHourMinSecondFormat())}"
|
||||
|
||||
CallerAutoPilotManager.recordPackage(
|
||||
99,
|
||||
Random(SystemClock.elapsedRealtime()).nextInt(),
|
||||
20,
|
||||
12
|
||||
)
|
||||
|
||||
//大型车
|
||||
rbLargeCar.setOnClickListener{
|
||||
setRadioButtonStatus(
|
||||
largeCarStatus = true,
|
||||
trafficLightStatus = false,
|
||||
waterStatus = false,
|
||||
constructionStatus = false,
|
||||
accidentStatus = false,
|
||||
rainStatus = false,
|
||||
nightTrafficStatus = false
|
||||
)
|
||||
collectReason = "大型车:大货、大巴、特种车辆"
|
||||
}
|
||||
//交通灯
|
||||
rbTrafficLight.setOnClickListener {
|
||||
setRadioButtonStatus(
|
||||
largeCarStatus = false,
|
||||
trafficLightStatus = true,
|
||||
waterStatus = false,
|
||||
constructionStatus = false,
|
||||
accidentStatus = false,
|
||||
rainStatus = false,
|
||||
nightTrafficStatus = false
|
||||
)
|
||||
collectReason = "交通灯:水平、箭头、雨天交通灯"
|
||||
}
|
||||
//积水
|
||||
rbWater.setOnClickListener {
|
||||
setRadioButtonStatus(
|
||||
largeCarStatus = false,
|
||||
trafficLightStatus = false,
|
||||
waterStatus = true,
|
||||
constructionStatus = false,
|
||||
accidentStatus = false,
|
||||
rainStatus = false,
|
||||
nightTrafficStatus = false
|
||||
)
|
||||
collectReason = "积水:距离10米内,面积大于1平米"
|
||||
}
|
||||
//施工
|
||||
rbConstruction.setOnClickListener {
|
||||
setRadioButtonStatus(
|
||||
largeCarStatus = false,
|
||||
trafficLightStatus = false,
|
||||
waterStatus = false,
|
||||
constructionStatus = true,
|
||||
accidentStatus = false,
|
||||
rainStatus = false,
|
||||
nightTrafficStatus = false
|
||||
)
|
||||
collectReason = "施工:锥桶、路障"
|
||||
}
|
||||
//车祸路段
|
||||
rbAccident.setOnClickListener {
|
||||
setRadioButtonStatus(
|
||||
largeCarStatus = false,
|
||||
trafficLightStatus = false,
|
||||
waterStatus = false,
|
||||
constructionStatus = false,
|
||||
accidentStatus = true,
|
||||
rainStatus = false,
|
||||
nightTrafficStatus = false
|
||||
)
|
||||
collectReason = "车祸路段:有三角板"
|
||||
}
|
||||
//中雨交通流
|
||||
rbRain.setOnClickListener {
|
||||
setRadioButtonStatus(
|
||||
largeCarStatus = false,
|
||||
trafficLightStatus = false,
|
||||
waterStatus = false,
|
||||
constructionStatus = false,
|
||||
accidentStatus = false,
|
||||
rainStatus = true,
|
||||
nightTrafficStatus = false
|
||||
)
|
||||
collectReason = "中雨交通流"
|
||||
}
|
||||
//夜间交通流
|
||||
rbNightTraffic.setOnClickListener {
|
||||
setRadioButtonStatus(
|
||||
largeCarStatus = false,
|
||||
trafficLightStatus = false,
|
||||
waterStatus = false,
|
||||
constructionStatus = false,
|
||||
accidentStatus = false,
|
||||
rainStatus = false,
|
||||
nightTrafficStatus = true
|
||||
)
|
||||
collectReason = "夜间交通流"
|
||||
}
|
||||
|
||||
//上报
|
||||
tvCollectReport.setOnClickListener {
|
||||
GlobalScope.launch{
|
||||
val uploadResult = presenter.upload(mutableMapOf<String, String>().also { itx ->
|
||||
itx["carLicense"] = "DFD02313"
|
||||
itx["filename"] = "/home/mogo/data/bags/badcase/20220706145143/20220706145143-265939904-rosmaster-XXXX000000.bag"
|
||||
itx["filesize"] = "0"
|
||||
itx["key"] = "265939904"
|
||||
itx["reason"] = collectReason
|
||||
itx["duration"] = "16"
|
||||
itx["startTime"] = "20220706145203"
|
||||
itx["channel"] = "AI"
|
||||
itx["carSn"] = MoGoAiCloudClientConfig.getInstance().sn
|
||||
itx["userRole"] = "安全员"
|
||||
itx["audioUrl"] = "http://petchfile-1255510688.cos.ap-beijing.myqcloud.com/CarPad/mogopadlog/deviceId/2022-07-07/AUDIO_myTest.wav"
|
||||
itx["mapVersion"] = "MAP-taxi-hq_RoboTaxi_hq_H9_2.3.0.5_20220410"
|
||||
itx["eyeVersion"] = "2.8.0"
|
||||
itx["coordinate"] = ""
|
||||
|
||||
})
|
||||
if (uploadResult == null || uploadResult.code != 200) {
|
||||
TipToast.shortTip("上报失败")
|
||||
} else {
|
||||
TipToast.shortTip("上报成功")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//取消
|
||||
tvCollectCancel.setOnClickListener {
|
||||
BadCaseConfig.windowNum--
|
||||
clickListener?.closeWindow()
|
||||
}
|
||||
|
||||
|
||||
mWindowParams = WindowManager.LayoutParams()
|
||||
mWindowManager = mActivity.windowManager
|
||||
mWindowParams?.let {
|
||||
it.format = PixelFormat.RGBA_8888
|
||||
it.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
it.gravity = Gravity.START or Gravity.TOP
|
||||
it.width = 924
|
||||
it.height = 668
|
||||
it.alpha = 0.9f
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAutopilotRecordResult(recordPanel: RecordPanelOuterClass.RecordPanel) {
|
||||
CallerLogger.d("${M_DEVA}${TAG}", "-- 收到工控机录制任务回调 -- $recordPanel")
|
||||
when(recordPanel.toRecord().stat){
|
||||
100, 101 ->{
|
||||
//成功结束录制
|
||||
TipToast.shortTip("bag录制成功")
|
||||
|
||||
}
|
||||
300 ->{
|
||||
//开始录制
|
||||
|
||||
}
|
||||
200 ->{
|
||||
//录制失败
|
||||
TipToast.shortTip("bag录制失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setRadioButtonStatus(largeCarStatus: Boolean,trafficLightStatus: Boolean,waterStatus: Boolean,
|
||||
constructionStatus: Boolean,accidentStatus: Boolean,rainStatus: Boolean,nightTrafficStatus: Boolean){
|
||||
rbLargeCar.isChecked = largeCarStatus
|
||||
rbTrafficLight.isChecked = trafficLightStatus
|
||||
rbWater.isChecked = waterStatus
|
||||
rbConstruction.isChecked = constructionStatus
|
||||
rbAccident.isChecked = accidentStatus
|
||||
rbRain.isChecked = rainStatus
|
||||
rbNightTraffic.isChecked = nightTrafficStatus
|
||||
}
|
||||
|
||||
override fun onTouch(v: View?, motionEvent: MotionEvent?): Boolean {
|
||||
when (motionEvent?.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
// 获取相对View的坐标,即以此View左上角为原点
|
||||
mInViewX = motionEvent.x
|
||||
mInViewY = motionEvent.y
|
||||
// 获取相对屏幕的坐标,即以屏幕左上角为原点
|
||||
mDownInScreenX = motionEvent.rawX
|
||||
mDownInScreenY = motionEvent.rawY - getSysBarHeight(mActivity)
|
||||
mInScreenX = motionEvent.rawX
|
||||
mInScreenY = motionEvent.rawY - getSysBarHeight(mActivity)
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
// 更新浮动窗口位置参数
|
||||
mInScreenX = motionEvent.rawX
|
||||
mInScreenY = motionEvent.rawY - getSysBarHeight(mActivity)
|
||||
mWindowParams!!.x = (mInScreenX - mInViewX).toInt()
|
||||
mWindowParams!!.y = (mInScreenY - mInViewY).toInt()
|
||||
// 手指移动的时候更新小悬浮窗的位置
|
||||
mWindowManager!!.updateViewLayout(mFloatLayout, mWindowParams)
|
||||
}
|
||||
// MotionEvent.ACTION_UP -> // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
|
||||
// if (mDownInScreenX === mInScreenX && mDownInScreenY === mInScreenY) {
|
||||
// }
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun showFloatWindow() {
|
||||
if (mFloatLayout.parent == null) {
|
||||
val metrics = DisplayMetrics()
|
||||
// 默认固定位置,靠屏幕右边缘的中间
|
||||
mWindowManager!!.defaultDisplay.getMetrics(metrics)
|
||||
mWindowParams!!.x = metrics.widthPixels
|
||||
mWindowParams!!.y = metrics.heightPixels / 2 - getSysBarHeight(mActivity)
|
||||
mWindowManager!!.addView(mFloatLayout, mWindowParams)
|
||||
}
|
||||
}
|
||||
|
||||
fun hideFloatWindow() {
|
||||
if (mFloatLayout.parent != null) mWindowManager!!.removeView(mFloatLayout)
|
||||
}
|
||||
|
||||
// 获取系统状态栏高度
|
||||
private fun getSysBarHeight(activity: Activity): Int {
|
||||
val c: Class<*>
|
||||
val obj: Any
|
||||
val field: Field
|
||||
val x: Int
|
||||
var sbar = 0
|
||||
try {
|
||||
c = Class.forName("com.android.internal.R\$dimen")
|
||||
obj = c.newInstance()
|
||||
field = c.getField("status_bar_height")
|
||||
x = field.get(obj).toString().toInt()
|
||||
sbar = activity.resources.getDimensionPixelSize(x)
|
||||
} catch (e1: Exception) {
|
||||
e1.printStackTrace()
|
||||
}
|
||||
return sbar
|
||||
}
|
||||
|
||||
fun setClickListener(clickListener: ClickListener) {
|
||||
this.clickListener = clickListener
|
||||
}
|
||||
|
||||
interface ClickListener {
|
||||
fun closeWindow()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.biz
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.RadioButton
|
||||
import android.widget.RadioGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotRecordListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotRecordListenerManager
|
||||
import com.mogo.eagle.core.utilcode.util.SizeUtils
|
||||
import com.mogo.eagle.core.utilcode.util.ThreadUtils
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import com.zhjt.mogo_core_function_devatools.R
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.consts.BadCaseConfig
|
||||
import kotlinx.android.synthetic.main.layout_badcase_config.view.*
|
||||
import mogo.telematics.pad.MessagePad
|
||||
import java.lang.Exception
|
||||
|
||||
|
||||
/**
|
||||
* @author XuXinChao
|
||||
* @description BadCase上报信息配置页面
|
||||
* @since: 2022/7/5
|
||||
*/
|
||||
internal class BadCaseConfigView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoAutopilotRecordListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "BadCaseConfigView"
|
||||
}
|
||||
|
||||
private var clickListener: ClickListener? = null
|
||||
private var mIdentity = "安全员"
|
||||
private var mPreviousDuration = 12
|
||||
private var mBackDuration = 8
|
||||
private var mType = 1
|
||||
|
||||
init{
|
||||
LayoutInflater.from(context).inflate(R.layout.layout_badcase_config, this, true)
|
||||
background = ColorDrawable(Color.parseColor("#F0151D41"))
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initView(){
|
||||
//关闭BadCase配置窗口
|
||||
ivConfigClose.setOnClickListener {
|
||||
clickListener?.onClose()
|
||||
}
|
||||
//安全员选项
|
||||
rbSafetyOfficer.setOnClickListener {
|
||||
mIdentity = "安全员"
|
||||
rbDeveloper.isChecked = false
|
||||
rbProduct.isChecked = false
|
||||
}
|
||||
//QA、研发选项
|
||||
rbDeveloper.setOnClickListener {
|
||||
mIdentity = "QA、研发"
|
||||
rbSafetyOfficer.isChecked = false
|
||||
rbProduct.isChecked = false
|
||||
}
|
||||
//产品、运营、演示选项
|
||||
rbProduct.setOnClickListener {
|
||||
mIdentity = "产品、运营、演示"
|
||||
rbSafetyOfficer.isChecked = false
|
||||
rbDeveloper.isChecked = false
|
||||
}
|
||||
|
||||
//保存配置按钮
|
||||
tvConfigSave.setOnClickListener {
|
||||
//判断、保存录制时间信息
|
||||
val preTimeStr=etInitiativePreTime.text.toString()
|
||||
val afterTimeStr=etInitiativeAfterTime.text.toString()
|
||||
|
||||
try {
|
||||
if(preTimeStr.isEmpty()){
|
||||
mPreviousDuration = 12
|
||||
}else{
|
||||
mPreviousDuration = preTimeStr.toInt()
|
||||
}
|
||||
if(afterTimeStr.isEmpty()){
|
||||
mBackDuration = 8
|
||||
}else{
|
||||
mBackDuration = afterTimeStr.toInt()
|
||||
}
|
||||
|
||||
if(mPreviousDuration<0 || mPreviousDuration>300){
|
||||
ToastUtils.showLong("采集时长最长300S")
|
||||
return@setOnClickListener
|
||||
}
|
||||
if(mBackDuration<0 || mBackDuration>300){
|
||||
ToastUtils.showLong("采集时长最长300S")
|
||||
return@setOnClickListener
|
||||
}
|
||||
if((mPreviousDuration+mBackDuration)<5){
|
||||
ToastUtils.showLong("采集时长最短5S")
|
||||
return@setOnClickListener
|
||||
}
|
||||
if((mPreviousDuration+mBackDuration)>300){
|
||||
ToastUtils.showLong("采集时长最长300S")
|
||||
return@setOnClickListener
|
||||
}
|
||||
BadCaseConfig.previousDuration = mPreviousDuration
|
||||
BadCaseConfig.backDuration = mBackDuration
|
||||
BadCaseConfig.totalDuration = BadCaseConfig.previousDuration + BadCaseConfig.backDuration
|
||||
}catch (e: Exception){
|
||||
ToastUtils.showLong("输入时间格式不合法,请重新输入")
|
||||
etInitiativePreTime.text = null
|
||||
etInitiativeAfterTime.text = null
|
||||
return@setOnClickListener
|
||||
}
|
||||
//保存身份信息
|
||||
BadCaseConfig.identity = mIdentity
|
||||
//保存录制模板采集类型
|
||||
BadCaseConfig.type = mType
|
||||
//吐司提示保存成功
|
||||
ToastUtils.showLong("保存成功")
|
||||
//关闭配置窗口
|
||||
clickListener?.onClose()
|
||||
}
|
||||
|
||||
// val test1 = TestBean(1,"人工接管自动录制")
|
||||
// val test2 = TestBean(2,"地图采集")
|
||||
// val test3 = TestBean(3,"画龙问题排查")
|
||||
// val test4 = TestBean(4,"误识别问题排查")
|
||||
// val test5 = TestBean(5,"lidar+planning")
|
||||
// val test6 = TestBean(6,"camera+planning")
|
||||
// val test7 = TestBean(7,"bus lidar+planning")
|
||||
// val test8 = TestBean(8,"bus camera+planning")
|
||||
// val test99 = TestBean(99,"ai data")
|
||||
//
|
||||
// val list = ArrayList<TestBean>()
|
||||
// list.add(test1)
|
||||
// list.add(test2)
|
||||
// list.add(test3)
|
||||
// list.add(test4)
|
||||
// list.add(test5)
|
||||
// list.add(test6)
|
||||
// list.add(test7)
|
||||
// list.add(test8)
|
||||
// list.add(test99)
|
||||
// list.iterator().forEach {
|
||||
// if(it.id!=99){
|
||||
// val radioButton = RadioButton(context)
|
||||
// val lp = RadioGroup.LayoutParams(
|
||||
// RadioGroup.LayoutParams.WRAP_CONTENT,
|
||||
// RadioGroup.LayoutParams.WRAP_CONTENT
|
||||
// )
|
||||
// //设置RadioButton边距 (int left, int top, int right, int bottom)
|
||||
// lp.setMargins(
|
||||
// SizeUtils.dp2px(0f),
|
||||
// SizeUtils.dp2px(8f),
|
||||
// SizeUtils.dp2px(10f),
|
||||
// SizeUtils.dp2px(8f)
|
||||
// )
|
||||
// //设置RadioButton背景
|
||||
// radioButton.setTextColor(Color.WHITE)
|
||||
//
|
||||
// radioButton.buttonDrawable = resources.getDrawable(R.drawable.badcase_radio_button_style)
|
||||
// //设置文字距离四周的距离
|
||||
// radioButton.setPadding(
|
||||
// SizeUtils.dp2px(12f),
|
||||
// SizeUtils.dp2px(5f),
|
||||
// SizeUtils.dp2px(10f),
|
||||
// SizeUtils.dp2px(5f)
|
||||
// )
|
||||
// radioButton.textSize = SizeUtils.sp2px(9f).toFloat()
|
||||
// radioButton.id = it.id
|
||||
// radioButton.isChecked = it.id == 1
|
||||
// //设置文字
|
||||
// radioButton.text = it.src
|
||||
// //将radioButton添加到radioGroup中
|
||||
// rgRecordConfig.addView(radioButton, lp)
|
||||
// rgRecordConfig.setOnCheckedChangeListener { _, checkedId ->
|
||||
// mType = checkedId
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fun setClickListener(clickListener: ClickListener) {
|
||||
this.clickListener = clickListener
|
||||
}
|
||||
|
||||
override fun onAutopilotRecordConfig(config: MessagePad.RecordDataConfig) {
|
||||
super.onAutopilotRecordConfig(config)
|
||||
ThreadUtils.runOnUiThread{
|
||||
config.recordTypesList.iterator().forEach {
|
||||
if(it.id!=99){
|
||||
val radioButton = RadioButton(context)
|
||||
val lp = RadioGroup.LayoutParams(
|
||||
RadioGroup.LayoutParams.WRAP_CONTENT,
|
||||
RadioGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
//设置RadioButton边距 (int left, int top, int right, int bottom)
|
||||
lp.setMargins(
|
||||
SizeUtils.dp2px(0f),
|
||||
SizeUtils.dp2px(8f),
|
||||
SizeUtils.dp2px(10f),
|
||||
SizeUtils.dp2px(8f)
|
||||
)
|
||||
//设置RadioButton背景
|
||||
radioButton.setTextColor(Color.WHITE)
|
||||
|
||||
radioButton.buttonDrawable = resources.getDrawable(R.drawable.badcase_radio_button_style)
|
||||
//设置文字距离四周的距离
|
||||
radioButton.setPadding(
|
||||
SizeUtils.dp2px(12f),
|
||||
SizeUtils.dp2px(5f),
|
||||
SizeUtils.dp2px(10f),
|
||||
SizeUtils.dp2px(5f)
|
||||
)
|
||||
radioButton.textSize = SizeUtils.sp2px(9f).toFloat()
|
||||
radioButton.id = it.id
|
||||
radioButton.isChecked = it.id == 1
|
||||
//设置文字
|
||||
radioButton.text = it.desc
|
||||
//将radioButton添加到radioGroup中
|
||||
rgRecordConfig.addView(radioButton, lp)
|
||||
rgRecordConfig.setOnCheckedChangeListener { _, checkedId ->
|
||||
mType = checkedId
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
CallerAutopilotRecordListenerManager.addListener(TAG, this)
|
||||
//获取数据采集录制模式配置列表
|
||||
CallerAutoPilotManager.getBadCaseConfig()
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
CallerAutopilotRecordListenerManager.removeListener(TAG)
|
||||
}
|
||||
|
||||
interface ClickListener{
|
||||
fun onClose()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.biz
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.graphics.PixelFormat
|
||||
import android.os.SystemClock
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.*
|
||||
import android.widget.TextView
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
|
||||
import com.mogo.eagle.core.utilcode.util.TimeUtils
|
||||
import com.mogo.eagle.core.utilcode.util.TimeUtils.millis2String
|
||||
import com.zhjt.mogo_core_function_devatools.R
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.consts.BadCaseConfig
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.RecordManager
|
||||
import java.lang.reflect.Field
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author XuXinChao
|
||||
* @description BadCase主动录包
|
||||
* @since: 2022/7/13
|
||||
*/
|
||||
class InitiativeBadCaseWindow constructor(activity: Activity) : View.OnTouchListener{
|
||||
|
||||
private var mActivity: Activity = activity
|
||||
private var mWindowParams: WindowManager.LayoutParams? = null
|
||||
private var mWindowManager: WindowManager? = null
|
||||
|
||||
private lateinit var mFloatLayout: View
|
||||
|
||||
private lateinit var tvInitiativeNum: TextView
|
||||
private lateinit var tvInitiativeTime: TextView
|
||||
private lateinit var tvInitiativeIdentity: TextView
|
||||
|
||||
private lateinit var viewAudioButton: View
|
||||
|
||||
private lateinit var tvInitiativeReport: TextView
|
||||
private lateinit var tvInitiativeCancel: TextView
|
||||
|
||||
private var audioStatus = false
|
||||
|
||||
private var mInViewX = 0f
|
||||
private var mInViewY = 0f
|
||||
private var mDownInScreenX = 0f
|
||||
private var mDownInScreenY = 0f
|
||||
private var mInScreenX = 0f
|
||||
private var mInScreenY = 0f
|
||||
|
||||
private var clickListener: ClickListener? = null
|
||||
|
||||
init {
|
||||
initFloatWindow();
|
||||
}
|
||||
|
||||
private val presenter by lazy {
|
||||
BadCasePresenter()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun initFloatWindow(){
|
||||
mFloatLayout = LayoutInflater.from(mActivity).inflate(R.layout.view_initiative_bad_case, null) as View
|
||||
mFloatLayout.setOnTouchListener(this)
|
||||
|
||||
tvInitiativeNum = mFloatLayout.findViewById(R.id.tvInitiativeNum)
|
||||
tvInitiativeTime = mFloatLayout.findViewById(R.id.tvInitiativeTime)
|
||||
tvInitiativeIdentity = mFloatLayout.findViewById(R.id.tvInitiativeIdentity)
|
||||
|
||||
viewAudioButton = mFloatLayout.findViewById(R.id.viewAudioButton)
|
||||
|
||||
tvInitiativeReport = mFloatLayout.findViewById(R.id.tvInitiativeReport)
|
||||
tvInitiativeCancel = mFloatLayout.findViewById(R.id.tvInitiativeCancel)
|
||||
|
||||
tvInitiativeNum.text = BadCaseConfig.windowNum.toString()
|
||||
BadCaseConfig.windowNum++
|
||||
tvInitiativeTime.text = "时间:${millis2String(System.currentTimeMillis(),TimeUtils.getHourMinSecondFormat())}"
|
||||
tvInitiativeIdentity.text = "身份:${BadCaseConfig.identity}"
|
||||
|
||||
viewAudioButton.setOnClickListener {
|
||||
audioStatus = !audioStatus
|
||||
if(audioStatus){
|
||||
viewAudioButton.background = mActivity.getDrawable(R.drawable.bad_case_audio_select)
|
||||
//开始录音
|
||||
RecordManager.getInstance().start("audio_test")
|
||||
}else{
|
||||
viewAudioButton.background = mActivity.getDrawable(R.drawable.bad_case_audio_normal)
|
||||
//结束录音
|
||||
RecordManager.getInstance().stop()
|
||||
}
|
||||
}
|
||||
|
||||
tvInitiativeReport.setOnClickListener {
|
||||
CallerAutoPilotManager.recordPackage(BadCaseConfig.type,
|
||||
Random(SystemClock.elapsedRealtime()).nextInt(),
|
||||
BadCaseConfig.totalDuration,
|
||||
BadCaseConfig.previousDuration
|
||||
)
|
||||
}
|
||||
tvInitiativeCancel.setOnClickListener {
|
||||
BadCaseConfig.windowNum--
|
||||
clickListener?.closeWindow()
|
||||
}
|
||||
|
||||
|
||||
mWindowParams = WindowManager.LayoutParams()
|
||||
mWindowManager = mActivity.windowManager
|
||||
mWindowParams?.let {
|
||||
it.format = PixelFormat.RGBA_8888
|
||||
it.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
it.gravity = Gravity.START or Gravity.TOP
|
||||
it.width = 924
|
||||
it.height = 668
|
||||
it.alpha = 0.9f
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouch(v: View?, motionEvent: MotionEvent?): Boolean {
|
||||
when (motionEvent?.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
// 获取相对View的坐标,即以此View左上角为原点
|
||||
mInViewX = motionEvent.x
|
||||
mInViewY = motionEvent.y
|
||||
// 获取相对屏幕的坐标,即以屏幕左上角为原点
|
||||
mDownInScreenX = motionEvent.rawX
|
||||
mDownInScreenY = motionEvent.rawY - getSysBarHeight(mActivity)
|
||||
mInScreenX = motionEvent.rawX
|
||||
mInScreenY = motionEvent.rawY - getSysBarHeight(mActivity)
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
// 更新浮动窗口位置参数
|
||||
mInScreenX = motionEvent.rawX
|
||||
mInScreenY = motionEvent.rawY - getSysBarHeight(mActivity)
|
||||
mWindowParams!!.x = (mInScreenX - mInViewX).toInt()
|
||||
mWindowParams!!.y = (mInScreenY - mInViewY).toInt()
|
||||
// 手指移动的时候更新小悬浮窗的位置
|
||||
mWindowManager!!.updateViewLayout(mFloatLayout, mWindowParams)
|
||||
}
|
||||
// MotionEvent.ACTION_UP -> // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
|
||||
// if (mDownInScreenX === mInScreenX && mDownInScreenY === mInScreenY) {
|
||||
// }
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun showFloatWindow() {
|
||||
if (mFloatLayout.parent == null) {
|
||||
val metrics = DisplayMetrics()
|
||||
// 默认固定位置,靠屏幕右边缘的中间
|
||||
mWindowManager!!.defaultDisplay.getMetrics(metrics)
|
||||
mWindowParams!!.x = metrics.widthPixels
|
||||
mWindowParams!!.y = metrics.heightPixels / 2 - getSysBarHeight(mActivity)
|
||||
mWindowManager!!.addView(mFloatLayout, mWindowParams)
|
||||
}
|
||||
}
|
||||
|
||||
fun hideFloatWindow() {
|
||||
if (mFloatLayout.parent != null) mWindowManager!!.removeView(mFloatLayout)
|
||||
}
|
||||
|
||||
// 获取系统状态栏高度
|
||||
private fun getSysBarHeight(activity: Activity): Int {
|
||||
val c: Class<*>
|
||||
val obj: Any
|
||||
val field: Field
|
||||
val x: Int
|
||||
var sbar = 0
|
||||
try {
|
||||
c = Class.forName("com.android.internal.R\$dimen")
|
||||
obj = c.newInstance()
|
||||
field = c.getField("status_bar_height")
|
||||
x = field.get(obj).toString().toInt()
|
||||
sbar = activity.resources.getDimensionPixelSize(x)
|
||||
} catch (e1: Exception) {
|
||||
e1.printStackTrace()
|
||||
}
|
||||
return sbar
|
||||
}
|
||||
|
||||
fun setClickListener(clickListener: ClickListener) {
|
||||
this.clickListener = clickListener
|
||||
}
|
||||
|
||||
interface ClickListener {
|
||||
fun closeWindow()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.biz
|
||||
|
||||
/**
|
||||
* @author xuxinchao
|
||||
* @description
|
||||
* @since: 2022/7/11
|
||||
*/
|
||||
data class TestBean(
|
||||
var id: Int,
|
||||
var src: String
|
||||
) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.consts
|
||||
|
||||
/**
|
||||
* @author XuXinChao
|
||||
* @description 录包配置参数
|
||||
* @since: 2022/7/11
|
||||
*/
|
||||
object BadCaseConfig {
|
||||
//身份:安全员;QA、研发;产品、运营、演示,默认身份是:安全员
|
||||
@JvmField
|
||||
var identity: String = "安全员"
|
||||
//录制前溯时长
|
||||
@JvmField
|
||||
var previousDuration: Int = 12
|
||||
//录制后溯时长
|
||||
@JvmField
|
||||
var backDuration: Int = 8
|
||||
//BadCase录制时长,默认时长为20秒,时长区间为大于等于5秒,小于等于300秒
|
||||
@JvmField
|
||||
var totalDuration: Int = 20
|
||||
//采集类型
|
||||
@JvmField
|
||||
var type: Int = 1
|
||||
//BadCase采集和AI数据采集弹窗数量
|
||||
@JvmField
|
||||
var windowNum = 1
|
||||
|
||||
}
|
||||
@@ -4,5 +4,17 @@ import com.mogo.commons.debug.DebugConfig
|
||||
|
||||
internal object BadCaseHost {
|
||||
|
||||
fun getHost(): String = "http://dzt.zhidaozhixing.com"/*if (DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE) "http://dzt.zhidaozhixing.com" else "http://front.zdjs-private-test.myghost.zhidaoauto.com"*/
|
||||
private const val HOST_DEV = "http://dzt.zhidaozhixing.com/"
|
||||
private const val HOST_RELEASE = "https://dzt-test.zhidaozhixing.com/"
|
||||
|
||||
fun getHost(): String{
|
||||
return when (DebugConfig.getNetMode()) {
|
||||
DebugConfig.NET_MODE_DEV -> HOST_DEV
|
||||
DebugConfig.NET_MODE_QA -> HOST_DEV
|
||||
DebugConfig.NET_MODE_DEMO -> HOST_RELEASE
|
||||
DebugConfig.NET_MODE_RELEASE -> HOST_RELEASE
|
||||
else -> HOST_RELEASE
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
public class RecordConfig implements Serializable {
|
||||
/**
|
||||
* 录音格式 默认WAV格式
|
||||
*/
|
||||
private RecordFormat format = RecordFormat.WAV;
|
||||
/**
|
||||
* 通道数:默认双通道
|
||||
*/
|
||||
private int channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||
|
||||
/**
|
||||
* 位宽
|
||||
*/
|
||||
private int encodingConfig = AudioFormat.ENCODING_PCM_16BIT;
|
||||
|
||||
/**
|
||||
* 采样率
|
||||
*/
|
||||
private int sampleRate = 16000;
|
||||
|
||||
|
||||
|
||||
public RecordConfig() {
|
||||
}
|
||||
|
||||
public RecordConfig(RecordFormat format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param format 录音文件的格式
|
||||
* @param channelConfig 声道配置
|
||||
* 单声道:See {@link AudioFormat#CHANNEL_IN_MONO}
|
||||
* 双声道:See {@link AudioFormat#CHANNEL_IN_STEREO}
|
||||
* @param encodingConfig 位宽配置
|
||||
* 8Bit: See {@link AudioFormat#ENCODING_PCM_8BIT}
|
||||
* 16Bit: See {@link AudioFormat#ENCODING_PCM_16BIT},
|
||||
* @param sampleRate 采样率 hz: 8000/16000/44100
|
||||
*/
|
||||
public RecordConfig(RecordFormat format, int channelConfig, int encodingConfig, int sampleRate) {
|
||||
this.format = format;
|
||||
this.channelConfig = channelConfig;
|
||||
this.encodingConfig = encodingConfig;
|
||||
this.sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前录音的采样位宽 单位bit
|
||||
*
|
||||
* @return 采样位宽 0: error
|
||||
*/
|
||||
public int getEncoding() {
|
||||
if (format == RecordFormat.MP3) {//mp3后期转换
|
||||
return 16;
|
||||
}
|
||||
|
||||
if (encodingConfig == AudioFormat.ENCODING_PCM_8BIT) {
|
||||
return 8;
|
||||
} else if (encodingConfig == AudioFormat.ENCODING_PCM_16BIT) {
|
||||
return 16;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前录音的采样位宽 单位bit
|
||||
*
|
||||
* @return 采样位宽 0: error
|
||||
*/
|
||||
public int getRealEncoding() {
|
||||
if (encodingConfig == AudioFormat.ENCODING_PCM_8BIT) {
|
||||
return 8;
|
||||
} else if (encodingConfig == AudioFormat.ENCODING_PCM_16BIT) {
|
||||
return 16;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前的声道数
|
||||
*
|
||||
* @return 声道数: 0:error
|
||||
*/
|
||||
public int getChannelCount() {
|
||||
if (channelConfig == AudioFormat.CHANNEL_IN_MONO) {
|
||||
return 1;
|
||||
} else if (channelConfig == AudioFormat.CHANNEL_IN_STEREO) {
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//get&set
|
||||
|
||||
public RecordFormat getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public RecordConfig setFormat(RecordFormat format) {
|
||||
this.format = format;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getChannelConfig() {
|
||||
return channelConfig;
|
||||
}
|
||||
|
||||
public RecordConfig setChannelConfig(int channelConfig) {
|
||||
this.channelConfig = channelConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getEncodingConfig() {
|
||||
if (format == RecordFormat.MP3) {//mp3后期转换
|
||||
return AudioFormat.ENCODING_PCM_16BIT;
|
||||
}
|
||||
return encodingConfig;
|
||||
}
|
||||
|
||||
public RecordConfig setEncodingConfig(int encodingConfig) {
|
||||
this.encodingConfig = encodingConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getSampleRate() {
|
||||
return sampleRate;
|
||||
}
|
||||
|
||||
public RecordConfig setSampleRate(int sampleRate) {
|
||||
this.sampleRate = sampleRate;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.getDefault(), "录制格式: %s,采样率:%sHz,位宽:%s bit,声道数:%s", format, sampleRate, getEncoding(), getChannelCount());
|
||||
}
|
||||
|
||||
public enum RecordFormat {
|
||||
/**
|
||||
* mp3格式
|
||||
*/
|
||||
MP3(".mp3"),
|
||||
/**
|
||||
* wav格式
|
||||
*/
|
||||
WAV(".wav"),
|
||||
/**
|
||||
* pcm格式
|
||||
*/
|
||||
PCM(".pcm");
|
||||
|
||||
private String extension;
|
||||
|
||||
public String getExtension() {
|
||||
return extension;
|
||||
}
|
||||
|
||||
RecordFormat(String extension) {
|
||||
this.extension = extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record;
|
||||
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
|
||||
import com.mogo.eagle.core.utilcode.util.FileUtils;
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.fft.FftFactory;
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.listener.RecordListener;
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.mp3.Mp3EncodeThread;
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.utils.ByteUtils;
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.utils.WavUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
public class RecordHelper {
|
||||
private static final String TAG = RecordHelper.class.getSimpleName();
|
||||
private volatile RecordState state = RecordState.IDLE;
|
||||
private static final int RECORD_AUDIO_BUFFER_TIMES = 1;
|
||||
/*
|
||||
* 录音文件存放路径
|
||||
*/
|
||||
public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Mogo" + File.separator + "DataCollection" + File.separator;//程序外部存储跟目录
|
||||
private static final String TEMP_PATH = ROOT_PATH + "temp" + File.separator;
|
||||
private RecordListener listener;
|
||||
private final RecordConfig currentConfig;
|
||||
private AudioRecordThread audioRecordThread;
|
||||
|
||||
private File resultFile = null;
|
||||
private File tmpFile = null;
|
||||
private List<File> files = new ArrayList<>();
|
||||
private Mp3EncodeThread mp3EncodeThread;
|
||||
|
||||
public RecordHelper(RecordConfig config) {
|
||||
this.currentConfig = config;
|
||||
}
|
||||
|
||||
|
||||
public RecordState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
public void registerRecordListener(RecordListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void unregisterRecordListener() {
|
||||
this.listener = null;
|
||||
}
|
||||
|
||||
public void start(String fileName) {
|
||||
|
||||
if (state != RecordState.IDLE && state != RecordState.STOP) {
|
||||
return;
|
||||
}
|
||||
String path = getFilePath(fileName);
|
||||
resultFile = new File(path);
|
||||
String tempFilePath = getTempFilePath();
|
||||
tmpFile = new File(tempFilePath);
|
||||
audioRecordThread = new AudioRecordThread();
|
||||
audioRecordThread.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (state == RecordState.IDLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == RecordState.PAUSE) {
|
||||
makeFile();
|
||||
state = RecordState.IDLE;
|
||||
notifyState();
|
||||
stopMp3Encoded();
|
||||
} else {
|
||||
state = RecordState.STOP;
|
||||
notifyState();
|
||||
}
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if (state != RecordState.RECORDING) {
|
||||
return;
|
||||
}
|
||||
state = RecordState.PAUSE;
|
||||
notifyState();
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
if (state != RecordState.PAUSE) {
|
||||
return;
|
||||
}
|
||||
String tempFilePath = getTempFilePath();
|
||||
tmpFile = new File(tempFilePath);
|
||||
audioRecordThread = new AudioRecordThread();
|
||||
audioRecordThread.start();
|
||||
}
|
||||
|
||||
private void notifyState() {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
listener.onStateChange(state);
|
||||
if (state == RecordState.STOP || state == RecordState.PAUSE) {
|
||||
listener.onSoundSize(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyFinish() {
|
||||
if (listener != null) {
|
||||
listener.onStateChange(RecordState.FINISH);
|
||||
listener.onResult(resultFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void notifyError(final String error) {
|
||||
if (listener != null) {
|
||||
listener.onError(error);
|
||||
}
|
||||
}
|
||||
|
||||
private FftFactory fftFactory = new FftFactory(FftFactory.Level.Original);
|
||||
|
||||
private void notifyData(final byte[] data) {
|
||||
if (listener != null) {
|
||||
listener.onData(data);
|
||||
byte[] fftData = fftFactory.makeFftData(data);
|
||||
if (fftData != null) {
|
||||
listener.onSoundSize(getDb(fftData));
|
||||
listener.onFftData(fftData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int getDb(byte[] data) {
|
||||
double sum = 0;
|
||||
double ave;
|
||||
int length = Math.min(data.length, 128);
|
||||
int offsetStart = 8;
|
||||
for (int i = offsetStart; i < length; i++) {
|
||||
sum += data[i];
|
||||
}
|
||||
ave = (sum / (length - offsetStart)) * 65536 / 128f;
|
||||
int i = (int) (Math.log10(ave) * 20);
|
||||
return i < 0 ? 27 : i;
|
||||
}
|
||||
|
||||
private void initMp3EncoderThread(int bufferSize) {
|
||||
try {
|
||||
mp3EncodeThread = new Mp3EncodeThread(resultFile, bufferSize, currentConfig);
|
||||
mp3EncodeThread.start();
|
||||
} catch (Exception e) {
|
||||
// Log.e(e, TAG, e.getMessage());
|
||||
CallerLogger.INSTANCE.d("$M_DEVA$TAG", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private class AudioRecordThread extends Thread {
|
||||
private final AudioRecord audioRecord;
|
||||
private int bufferSize;
|
||||
|
||||
AudioRecordThread() {
|
||||
bufferSize = AudioRecord.getMinBufferSize(currentConfig.getSampleRate(),
|
||||
currentConfig.getChannelConfig(), currentConfig.getEncodingConfig()) * RECORD_AUDIO_BUFFER_TIMES;
|
||||
Logger.d(TAG, "record buffer size = %s", bufferSize);
|
||||
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, currentConfig.getSampleRate(),
|
||||
currentConfig.getChannelConfig(), currentConfig.getEncodingConfig(), bufferSize);
|
||||
if (currentConfig.getFormat() == RecordConfig.RecordFormat.MP3) {
|
||||
if (mp3EncodeThread == null) {
|
||||
initMp3EncoderThread(bufferSize);
|
||||
} else {
|
||||
Logger.e(TAG, "mp3EncodeThread != null, 请检查代码");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
|
||||
switch (currentConfig.getFormat()) {
|
||||
case MP3:
|
||||
startMp3Recorder();
|
||||
break;
|
||||
default:
|
||||
startPcmRecorder();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void startPcmRecorder() {
|
||||
state = RecordState.RECORDING;
|
||||
notifyState();
|
||||
Logger.d(TAG, "开始录制 Pcm");
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(tmpFile);
|
||||
audioRecord.startRecording();
|
||||
byte[] byteBuffer = new byte[bufferSize];
|
||||
|
||||
while (state == RecordState.RECORDING) {
|
||||
int end = audioRecord.read(byteBuffer, 0, byteBuffer.length);
|
||||
notifyData(byteBuffer);
|
||||
fos.write(byteBuffer, 0, end);
|
||||
fos.flush();
|
||||
}
|
||||
audioRecord.stop();
|
||||
files.add(tmpFile);
|
||||
if (state == RecordState.STOP) {
|
||||
makeFile();
|
||||
} else {
|
||||
Logger.i(TAG, "暂停!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
notifyError("录音失败");
|
||||
} finally {
|
||||
try {
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (state != RecordState.PAUSE) {
|
||||
state = RecordState.IDLE;
|
||||
notifyState();
|
||||
Logger.d(TAG, "录音结束");
|
||||
}
|
||||
}
|
||||
|
||||
private void startMp3Recorder() {
|
||||
state = RecordState.RECORDING;
|
||||
notifyState();
|
||||
|
||||
try {
|
||||
audioRecord.startRecording();
|
||||
short[] byteBuffer = new short[bufferSize];
|
||||
|
||||
while (state == RecordState.RECORDING) {
|
||||
int end = audioRecord.read(byteBuffer, 0, byteBuffer.length);
|
||||
if (mp3EncodeThread != null) {
|
||||
mp3EncodeThread.addChangeBuffer(new Mp3EncodeThread.ChangeBuffer(byteBuffer, end));
|
||||
}
|
||||
notifyData(ByteUtils.toBytes(byteBuffer));
|
||||
}
|
||||
audioRecord.stop();
|
||||
} catch (Exception e) {
|
||||
notifyError("录音失败");
|
||||
}
|
||||
if (state != RecordState.PAUSE) {
|
||||
state = RecordState.IDLE;
|
||||
notifyState();
|
||||
stopMp3Encoded();
|
||||
} else {
|
||||
Logger.d(TAG, "暂停");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stopMp3Encoded() {
|
||||
if (mp3EncodeThread != null) {
|
||||
mp3EncodeThread.stopSafe(new Mp3EncodeThread.EncordFinishListener() {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
notifyFinish();
|
||||
mp3EncodeThread = null;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Logger.e(TAG, "mp3EncodeThread is null, 代码业务流程有误,请检查!! ");
|
||||
}
|
||||
}
|
||||
|
||||
private void makeFile() {
|
||||
switch (currentConfig.getFormat()) {
|
||||
case MP3:
|
||||
return;
|
||||
case WAV:
|
||||
mergePcmFile();
|
||||
makeWav();
|
||||
break;
|
||||
case PCM:
|
||||
mergePcmFile();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
notifyFinish();
|
||||
Logger.i(TAG, "录音完成! path: %s ; 大小:%s", resultFile.getAbsoluteFile(), resultFile.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Wav头文件
|
||||
*/
|
||||
private void makeWav() {
|
||||
if (!FileUtils.isFile(resultFile) || resultFile.length() == 0) {
|
||||
return;
|
||||
}
|
||||
byte[] header = WavUtils.generateWavFileHeader((int) resultFile.length(), currentConfig.getSampleRate(), currentConfig.getChannelCount(), currentConfig.getEncoding());
|
||||
WavUtils.writeHeader(resultFile, header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并文件
|
||||
*/
|
||||
private void mergePcmFile() {
|
||||
boolean mergeSuccess = mergePcmFiles(resultFile, files);
|
||||
if (!mergeSuccess) {
|
||||
notifyError("合并失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并Pcm文件
|
||||
*
|
||||
* @param recordFile 输出文件
|
||||
* @param files 多个文件源
|
||||
* @return 是否成功
|
||||
*/
|
||||
private boolean mergePcmFiles(File recordFile, List<File> files) {
|
||||
if (recordFile == null || files == null || files.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
BufferedOutputStream outputStream = null;
|
||||
byte[] buffer = new byte[1024];
|
||||
try {
|
||||
fos = new FileOutputStream(recordFile);
|
||||
outputStream = new BufferedOutputStream(fos);
|
||||
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(files.get(i)));
|
||||
int readCount;
|
||||
while ((readCount = inputStream.read(buffer)) > 0) {
|
||||
outputStream.write(buffer, 0, readCount);
|
||||
}
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
files.get(i).delete();
|
||||
}
|
||||
files.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getFilePath(String fileName) {
|
||||
if (!FileUtils.createOrExistsDir(ROOT_PATH)) {
|
||||
Logger.w(TAG, "文件夹创建失败:%s", ROOT_PATH);
|
||||
return null;
|
||||
}
|
||||
|
||||
String format = currentConfig.getFormat().getExtension();
|
||||
String filePath = String.format(Locale.getDefault(), "%s%s%s", ROOT_PATH, fileName, format);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
private String getTempFilePath() {
|
||||
if (!FileUtils.createOrExistsDir(TEMP_PATH)) {
|
||||
Logger.e(TAG, "文件夹创建失败:%s", TEMP_PATH);
|
||||
}
|
||||
String fileName = String.format(Locale.getDefault(), "tmp_%s", FileUtils.getNowString(new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.SIMPLIFIED_CHINESE)));
|
||||
return String.format(Locale.getDefault(), "%s%s.pcm", TEMP_PATH, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示当前状态
|
||||
*/
|
||||
public enum RecordState {
|
||||
/**
|
||||
* 空闲状态
|
||||
*/
|
||||
IDLE,
|
||||
/**
|
||||
* 录音中
|
||||
*/
|
||||
RECORDING,
|
||||
/**
|
||||
* 暂停中
|
||||
*/
|
||||
PAUSE,
|
||||
/**
|
||||
* 正在停止
|
||||
*/
|
||||
STOP,
|
||||
/**
|
||||
* 录音流程结束(转换结束)
|
||||
*/
|
||||
FINISH
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record;
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.listener.RecordListener;
|
||||
|
||||
|
||||
public class RecordManager {
|
||||
private static final String TAG = RecordManager.class.getSimpleName();
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private volatile static RecordManager instance;
|
||||
private final RecordHelper recordHelper;
|
||||
/**
|
||||
* 录音配置
|
||||
*/
|
||||
private final RecordConfig currentConfig = new RecordConfig();
|
||||
|
||||
private RecordManager() {
|
||||
recordHelper = new RecordHelper(currentConfig);
|
||||
}
|
||||
|
||||
public static RecordManager getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (RecordManager.class) {
|
||||
if (instance == null) {
|
||||
instance = new RecordManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @param showLog 是否开启日志
|
||||
// */
|
||||
// public void setISDebug(boolean showLog) {
|
||||
// Logger.IsDebug = showLog;
|
||||
// }
|
||||
|
||||
public void start(String fileName) {
|
||||
recordHelper.start(fileName);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
recordHelper.stop();
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
recordHelper.resume();
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
recordHelper.pause();
|
||||
}
|
||||
|
||||
|
||||
public RecordConfig getRecordConfig() {
|
||||
return currentConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 录音数据监听回调
|
||||
*/
|
||||
public void registerRecordListener(RecordListener listener) {
|
||||
recordHelper.registerRecordListener(listener);
|
||||
}
|
||||
public void unregisterRecordListener() {
|
||||
recordHelper.unregisterRecordListener();
|
||||
}
|
||||
|
||||
public boolean changeFormat(RecordConfig.RecordFormat recordFormat) {
|
||||
if (getState() == RecordHelper.RecordState.IDLE) {
|
||||
currentConfig.setFormat(recordFormat);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean changeSampleRate(int sampleRate) {
|
||||
if (getState() == RecordHelper.RecordState.IDLE) {
|
||||
currentConfig.setSampleRate(sampleRate);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean changeEncodingConfig(int encodingConfig) {
|
||||
if (getState() == RecordHelper.RecordState.IDLE) {
|
||||
currentConfig.setEncodingConfig(encodingConfig);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的录音状态
|
||||
*
|
||||
* @return 状态
|
||||
*/
|
||||
public RecordHelper.RecordState getState() {
|
||||
return recordHelper.getState();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.fft;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 复数
|
||||
*
|
||||
* @author test
|
||||
*/
|
||||
public class Complex {
|
||||
|
||||
/**
|
||||
* 实数部分
|
||||
*/
|
||||
private final double real;
|
||||
|
||||
/**
|
||||
* 虚数部分 imaginary
|
||||
*/
|
||||
private final double im;
|
||||
|
||||
public Complex(double real, double imag) {
|
||||
this.real = real;
|
||||
im = imag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("hypot: %s, complex: %s+%si", hypot(), real, im);
|
||||
}
|
||||
|
||||
public double hypot() {
|
||||
return Math.hypot(real, im);
|
||||
}
|
||||
|
||||
public double phase() {
|
||||
return Math.atan2(im, real);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复数求和
|
||||
*/
|
||||
public Complex plus(Complex b) {
|
||||
double real = this.real + b.real;
|
||||
double imag = this.im + b.im;
|
||||
return new Complex(real, imag);
|
||||
}
|
||||
|
||||
// return a new Complex object whose value is (this - b)
|
||||
public Complex minus(Complex b) {
|
||||
double real = this.real - b.real;
|
||||
double imag = this.im - b.im;
|
||||
return new Complex(real, imag);
|
||||
}
|
||||
|
||||
// return a new Complex object whose value is (this * b)
|
||||
public Complex times(Complex b) {
|
||||
Complex a = this;
|
||||
double real = a.real * b.real - a.im * b.im;
|
||||
double imag = a.real * b.im + a.im * b.real;
|
||||
return new Complex(real, imag);
|
||||
}
|
||||
|
||||
// return a new object whose value is (this * alpha)
|
||||
public Complex scale(double alpha) {
|
||||
return new Complex(alpha * real, alpha * im);
|
||||
}
|
||||
|
||||
// return a new Complex object whose value is the conjugate of this
|
||||
public Complex conjugate() {
|
||||
return new Complex(real, -im);
|
||||
}
|
||||
|
||||
// return a new Complex object whose value is the reciprocal of this
|
||||
public Complex reciprocal() {
|
||||
double scale = real * real + im * im;
|
||||
return new Complex(real / scale, -im / scale);
|
||||
}
|
||||
|
||||
// return the real or imaginary part
|
||||
public double re() {
|
||||
return real;
|
||||
}
|
||||
|
||||
public double im() {
|
||||
return im;
|
||||
}
|
||||
|
||||
// return a / b
|
||||
public Complex divides(Complex b) {
|
||||
Complex a = this;
|
||||
return a.times(b.reciprocal());
|
||||
}
|
||||
|
||||
// return a new Complex object whose value is the complex exponential of this
|
||||
public Complex exp() {
|
||||
return new Complex(Math.exp(real) * Math.cos(im), Math.exp(real) * Math.sin(im));
|
||||
}
|
||||
|
||||
// return a new Complex object whose value is the complex sine of this
|
||||
public Complex sin() {
|
||||
return new Complex(Math.sin(real) * Math.cosh(im), Math.cos(real) * Math.sinh(im));
|
||||
}
|
||||
|
||||
// return a new Complex object whose value is the complex cosine of this
|
||||
public Complex cos() {
|
||||
return new Complex(Math.cos(real) * Math.cosh(im), -Math.sin(real) * Math.sinh(im));
|
||||
}
|
||||
|
||||
// return a new Complex object whose value is the complex tangent of this
|
||||
public Complex tan() {
|
||||
return sin().divides(cos());
|
||||
}
|
||||
|
||||
|
||||
// a static version of plus
|
||||
public static Complex plus(Complex a, Complex b) {
|
||||
double real = a.real + b.real;
|
||||
double imag = a.im + b.im;
|
||||
Complex sum = new Complex(real, imag);
|
||||
return sum;
|
||||
}
|
||||
|
||||
// See Section 3.3.
|
||||
@Override
|
||||
public boolean equals(Object x) {
|
||||
if (x == null) return false;
|
||||
if (this.getClass() != x.getClass()) return false;
|
||||
Complex that = (Complex) x;
|
||||
return (this.real == that.real) && (this.im == that.im);
|
||||
}
|
||||
|
||||
// See Section 3.3.
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(real, im);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.fft;
|
||||
|
||||
|
||||
public class FFT {
|
||||
|
||||
// compute the FFT of x[], assuming its length is a power of 2
|
||||
public static Complex[] fft(Complex[] x) {
|
||||
int n = x.length;
|
||||
|
||||
// base case
|
||||
if (n == 1) return new Complex[]{x[0]};
|
||||
|
||||
// radix 2 Cooley-Tukey FFT
|
||||
if (n % 2 != 0) {
|
||||
throw new IllegalArgumentException("n is not a power of 2");
|
||||
}
|
||||
|
||||
// fft of even terms
|
||||
Complex[] even = new Complex[n / 2];
|
||||
for (int k = 0; k < n / 2; k++) {
|
||||
even[k] = x[2 * k];
|
||||
}
|
||||
Complex[] q = fft(even);
|
||||
|
||||
// fft of odd terms
|
||||
for (int k = 0; k < n / 2; k++) {
|
||||
even[k] = x[2 * k + 1];
|
||||
}
|
||||
Complex[] r = fft(even);
|
||||
|
||||
// combine
|
||||
Complex[] y = new Complex[n];
|
||||
for (int k = 0; k < n / 2; k++) {
|
||||
double kth = -2 * k * Math.PI / n;
|
||||
Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
|
||||
y[k] = q[k].plus(wk.times(r[k]));
|
||||
y[k + n / 2] = q[k].minus(wk.times(r[k]));
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
public static double[] fft(double[] x, int sc) {
|
||||
int len = x.length;
|
||||
if (len == 1) {
|
||||
return x;
|
||||
}
|
||||
Complex[] cs = new Complex[len];
|
||||
double[] ds = new double[len / 2];
|
||||
for (int i = 0; i < len; i++) {
|
||||
cs[i] = new Complex(x[i], 0);
|
||||
}
|
||||
Complex[] ffts = fft(cs);
|
||||
|
||||
for (int i = 0; i < ds.length; i++) {
|
||||
ds[i] = Math.sqrt(Math.pow(ffts[i].re(), 2) + Math.pow(ffts[i].im(), 2)) / x.length;
|
||||
}
|
||||
return ds;
|
||||
}
|
||||
|
||||
// compute the inverse FFT of x[], assuming its length is a power of 2
|
||||
public static Complex[] ifft(Complex[] x) {
|
||||
int n = x.length;
|
||||
Complex[] y = new Complex[n];
|
||||
|
||||
// take conjugate
|
||||
for (int i = 0; i < n; i++) {
|
||||
y[i] = x[i].conjugate();
|
||||
}
|
||||
|
||||
// compute forward FFT
|
||||
y = fft(y);
|
||||
|
||||
// take conjugate again
|
||||
for (int i = 0; i < n; i++) {
|
||||
y[i] = y[i].conjugate();
|
||||
}
|
||||
|
||||
// divide by n
|
||||
for (int i = 0; i < n; i++) {
|
||||
y[i] = y[i].scale(1.0 / n);
|
||||
}
|
||||
|
||||
return y;
|
||||
|
||||
}
|
||||
|
||||
// compute the circular convolution of x and y
|
||||
public static Complex[] cconvolve(Complex[] x, Complex[] y) {
|
||||
|
||||
// should probably pad x and y with 0s so that they have same length
|
||||
// and are powers of 2
|
||||
if (x.length != y.length) {
|
||||
throw new IllegalArgumentException("Dimensions don't agree");
|
||||
}
|
||||
|
||||
int n = x.length;
|
||||
|
||||
// compute FFT of each sequence
|
||||
Complex[] a = fft(x);
|
||||
Complex[] b = fft(y);
|
||||
|
||||
// point-wise multiply
|
||||
Complex[] c = new Complex[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
c[i] = a[i].times(b[i]);
|
||||
}
|
||||
|
||||
// compute inverse FFT
|
||||
return ifft(c);
|
||||
}
|
||||
|
||||
|
||||
// compute the linear convolution of x and y
|
||||
public static Complex[] convolve(Complex[] x, Complex[] y) {
|
||||
Complex ZERO = new Complex(0, 0);
|
||||
|
||||
Complex[] a = new Complex[2 * x.length];
|
||||
for (int i = 0; i < x.length; i++) a[i] = x[i];
|
||||
for (int i = x.length; i < 2 * x.length; i++) a[i] = ZERO;
|
||||
|
||||
Complex[] b = new Complex[2 * y.length];
|
||||
for (int i = 0; i < y.length; i++) b[i] = y[i];
|
||||
for (int i = y.length; i < 2 * y.length; i++) b[i] = ZERO;
|
||||
|
||||
return cconvolve(a, b);
|
||||
}
|
||||
|
||||
// display an array of Complex numbers to standard output
|
||||
public static void show(Complex[] x, String title) {
|
||||
System.out.println(title);
|
||||
System.out.println("-------------------");
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
System.out.println(x[i]);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private static final int SIZE = 16384 / 4;
|
||||
|
||||
public static double fun(int x) {
|
||||
return Math.sin(15f * x);//f= 3.142
|
||||
}
|
||||
|
||||
public static double getY(double[] d) {
|
||||
double y = 0;
|
||||
int x = 0;
|
||||
for (int i = 0; i < d.length; i++) {
|
||||
if (d[i] > y) {
|
||||
y = d[i];
|
||||
x = i;
|
||||
}
|
||||
}
|
||||
x++;
|
||||
log(String.format("x: %s , y: %s", x, y));
|
||||
log(String.format("频率: %sHz", (float) x / SIZE));
|
||||
log(String.format("频率2: %sHz", (float) (SIZE - x) / SIZE));
|
||||
log(String.format("振幅: %s", y));
|
||||
return y;
|
||||
}
|
||||
|
||||
public static void log(String s) {
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.fft;
|
||||
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.utils.ByteUtils;
|
||||
|
||||
/**
|
||||
* FFT 数据处理工厂
|
||||
*/
|
||||
public class FftFactory {
|
||||
private static final String TAG = FftFactory.class.getSimpleName();
|
||||
private Level level = Level.Original;
|
||||
|
||||
public FftFactory(Level level) {
|
||||
// this.level = level;
|
||||
}
|
||||
|
||||
public byte[] makeFftData(byte[] pcmData) {
|
||||
// Logger.d(TAG, "pcmData length: %s", pcmData.length);
|
||||
if (pcmData.length < 1024) {
|
||||
return null;
|
||||
}
|
||||
|
||||
double[] doubles = ByteUtils.toHardDouble(ByteUtils.toShorts(pcmData));
|
||||
double[] fft = FFT.fft(doubles, 0);
|
||||
|
||||
switch (level) {
|
||||
case Original:
|
||||
return ByteUtils.toSoftBytes(fft);
|
||||
case Maximal:
|
||||
// return doFftMaximal(fft);
|
||||
default:
|
||||
return ByteUtils.toHardBytes(fft);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private byte[] doFftMaximal(double[] fft) {
|
||||
byte[] bytes = ByteUtils.toSoftBytes(fft);
|
||||
byte[] result = new byte[bytes.length];
|
||||
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
|
||||
if (isSimpleData(bytes, i)) {
|
||||
result[i] = bytes[i];
|
||||
} else {
|
||||
result[Math.max(i - 1, 0)] = (byte) (bytes[i] / 2);
|
||||
result[Math.min(i + 1, result.length - 1)] = (byte) (bytes[i] / 2);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isSimpleData(byte[] data, int i) {
|
||||
|
||||
int start = Math.max(0, i - 5);
|
||||
int end = Math.min(data.length, i + 5);
|
||||
|
||||
byte max = 0, min = 127;
|
||||
for (int j = start; j < end; j++) {
|
||||
if (data[j] > max) {
|
||||
max = data[j];
|
||||
}
|
||||
if (data[j] < min) {
|
||||
min = data[j];
|
||||
}
|
||||
}
|
||||
|
||||
return data[i] == min || data[i] == max;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FFT 处理等级
|
||||
*/
|
||||
public enum Level {
|
||||
|
||||
/**
|
||||
* 原始数据,不做任何优化
|
||||
*/
|
||||
Original,
|
||||
|
||||
/**
|
||||
* 对音乐进行优化
|
||||
*/
|
||||
Music,
|
||||
|
||||
/**
|
||||
* 对人声进行优化
|
||||
*/
|
||||
People,
|
||||
|
||||
/**
|
||||
* 极限优化
|
||||
*/
|
||||
Maximal
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.listener;
|
||||
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.RecordHelper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface RecordListener {
|
||||
/**
|
||||
* 实时返回音量大小
|
||||
*
|
||||
* @param soundSize 当前音量大小
|
||||
*/
|
||||
void onSoundSize(int soundSize);
|
||||
|
||||
/**
|
||||
* @param data 录音可视化数据,即傅里叶转换后的数据:fftData
|
||||
*/
|
||||
void onFftData(byte[] data);
|
||||
|
||||
/**
|
||||
* 当前的录音状态发生变化
|
||||
*
|
||||
* @param data 当前音频数据
|
||||
*/
|
||||
void onData(byte[] data);
|
||||
|
||||
/**
|
||||
* 录音完成回调
|
||||
* 录音文件
|
||||
*
|
||||
* @param result 录音文件
|
||||
*/
|
||||
void onResult(File result);
|
||||
|
||||
|
||||
/**
|
||||
* 当前的录音状态发生变化
|
||||
*
|
||||
* @param state 当前状态
|
||||
*/
|
||||
void onStateChange(RecordHelper.RecordState state);
|
||||
|
||||
/**
|
||||
* 录音错误
|
||||
*
|
||||
* @param error 错误
|
||||
*/
|
||||
void onError(String error);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.mp3;
|
||||
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.RecordConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class Mp3EncodeThread extends Thread {
|
||||
private static final String TAG = Mp3EncodeThread.class.getSimpleName();
|
||||
private List<ChangeBuffer> cacheBufferList = Collections.synchronizedList(new LinkedList<ChangeBuffer>());
|
||||
private File file;
|
||||
private FileOutputStream os;
|
||||
private byte[] mp3Buffer;
|
||||
private EncordFinishListener encordFinishListener;
|
||||
|
||||
/**
|
||||
* 是否已停止录音
|
||||
*/
|
||||
private volatile boolean isOver = false;
|
||||
|
||||
/**
|
||||
* 是否继续轮询数据队列
|
||||
*/
|
||||
private volatile boolean start = true;
|
||||
|
||||
public Mp3EncodeThread(File file, int bufferSize, RecordConfig currentConfig) {
|
||||
this.file = file;
|
||||
mp3Buffer = new byte[(int) (7200 + (bufferSize * 2 * 1.25))];
|
||||
int sampleRate = currentConfig.getSampleRate();
|
||||
Mp3Encoder.init(sampleRate, currentConfig.getChannelCount(), sampleRate, currentConfig.getRealEncoding());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
this.os = new FileOutputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (start) {
|
||||
ChangeBuffer next = next();
|
||||
lameData(next);
|
||||
}
|
||||
}
|
||||
|
||||
public void addChangeBuffer(ChangeBuffer changeBuffer) {
|
||||
if (changeBuffer != null) {
|
||||
cacheBufferList.add(changeBuffer);
|
||||
synchronized (this) {
|
||||
notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopSafe(EncordFinishListener encordFinishListener) {
|
||||
this.encordFinishListener = encordFinishListener;
|
||||
isOver = true;
|
||||
synchronized (this) {
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
private ChangeBuffer next() {
|
||||
for (; ; ) {
|
||||
if (cacheBufferList == null || cacheBufferList.size() == 0) {
|
||||
try {
|
||||
if (isOver) {
|
||||
finish();
|
||||
}
|
||||
synchronized (this) {
|
||||
wait();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
return cacheBufferList.remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void lameData(ChangeBuffer changeBuffer) {
|
||||
if (changeBuffer == null) {
|
||||
return;
|
||||
}
|
||||
short[] buffer = changeBuffer.getData();
|
||||
int readSize = changeBuffer.getReadSize();
|
||||
if (readSize > 0) {
|
||||
int encodedSize = Mp3Encoder.encode(buffer, buffer, readSize, mp3Buffer);
|
||||
// if (encodedSize < 0) {
|
||||
// Logger.e(TAG, "Lame encoded size: " + encodedSize);
|
||||
// }
|
||||
try {
|
||||
os.write(mp3Buffer, 0, encodedSize);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void finish() {
|
||||
start = false;
|
||||
final int flushResult = Mp3Encoder.flush(mp3Buffer);
|
||||
if (flushResult > 0) {
|
||||
try {
|
||||
os.write(mp3Buffer, 0, flushResult);
|
||||
os.close();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (encordFinishListener != null) {
|
||||
encordFinishListener.onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChangeBuffer {
|
||||
private short[] rawData;
|
||||
private int readSize;
|
||||
|
||||
public ChangeBuffer(short[] rawData, int readSize) {
|
||||
this.rawData = rawData.clone();
|
||||
this.readSize = readSize;
|
||||
}
|
||||
|
||||
short[] getData() {
|
||||
return rawData;
|
||||
}
|
||||
|
||||
int getReadSize() {
|
||||
return readSize;
|
||||
}
|
||||
}
|
||||
|
||||
public interface EncordFinishListener {
|
||||
/**
|
||||
* 格式转换完毕
|
||||
*/
|
||||
void onFinish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.mp3;
|
||||
|
||||
|
||||
public class Mp3Encoder {
|
||||
|
||||
static {
|
||||
System.loadLibrary("lamemp3");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取lame版本号
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static native String getVersion();
|
||||
|
||||
public native static void close();
|
||||
|
||||
public native static int encode(short[] buffer_l, short[] buffer_r, int samples, byte[] mp3buf);
|
||||
|
||||
public native static int flush(byte[] mp3buf);
|
||||
|
||||
public native static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate, int quality);
|
||||
|
||||
public static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate) {
|
||||
init(inSampleRate, outChannel, outSampleRate, outBitrate, 7);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.mp3;
|
||||
|
||||
import android.media.MediaExtractor;
|
||||
import android.media.MediaFormat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.mogo.eagle.core.utilcode.util.FileUtils;
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.RecordConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class Mp3Utils {
|
||||
private static final String TAG = Mp3Utils.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* 获取mp3音频的总时长 单位:ms
|
||||
*
|
||||
* @param mp3FilePath MP3文件路径
|
||||
* @return 时长
|
||||
*/
|
||||
public static long getDuration(String mp3FilePath) {
|
||||
if (!FileUtils.isFileExists(mp3FilePath)) {
|
||||
return 0;
|
||||
}
|
||||
if (!mp3FilePath.endsWith(RecordConfig.RecordFormat.MP3.getExtension())) {
|
||||
return 0;
|
||||
}
|
||||
MediaExtractor mex = null;
|
||||
try {
|
||||
mex = new MediaExtractor();
|
||||
mex.setDataSource(mp3FilePath);
|
||||
MediaFormat mf = mex.getTrackFormat(0);
|
||||
long duration = mf.getLong(MediaFormat.KEY_DURATION) / 1000L;
|
||||
return duration;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
} finally {
|
||||
if (mex != null) {
|
||||
mex.release();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class ByteUtils {
|
||||
public static double[] toHardDouble(short[] shorts) {
|
||||
int length = 512;
|
||||
double[] ds = new double[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
ds[i] = shorts[i];
|
||||
}
|
||||
return ds;
|
||||
}
|
||||
|
||||
public static byte[] toHardBytes(double[] doubles) {
|
||||
byte[] bytes = new byte[doubles.length];
|
||||
for (int i = 0; i < doubles.length; i++) {
|
||||
double item = doubles[i];
|
||||
bytes[i] = (byte) (item > 127 ? 127 : item);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] toSoftBytes(double[] doubles) {
|
||||
double max = getMax(doubles);
|
||||
|
||||
double sc = 1f;
|
||||
if (max > 127) {
|
||||
sc = (max / 128f);
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[doubles.length];
|
||||
for (int i = 0; i < doubles.length; i++) {
|
||||
double item = doubles[i] / sc;
|
||||
bytes[i] = (byte) (item > 127 ? 127 : item);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
public static double getMax(double[] data) {
|
||||
double max = 0;
|
||||
for (double datum : data) {
|
||||
if (datum > max) {
|
||||
max = datum;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* short[] 转 byte[]
|
||||
*/
|
||||
public static byte[] toBytes(short[] src) {
|
||||
int count = src.length;
|
||||
byte[] dest = new byte[count << 1];
|
||||
for (int i = 0; i < count; i++) {
|
||||
dest[i * 2] = (byte) (src[i]);
|
||||
dest[i * 2 + 1] = (byte) (src[i] >> 8);
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* short[] 转 byte[]
|
||||
*/
|
||||
public static byte[] toBytes(short src) {
|
||||
byte[] dest = new byte[2];
|
||||
dest[0] = (byte) (src);
|
||||
dest[1] = (byte) (src >> 8);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* int 转 byte[]
|
||||
*/
|
||||
public static byte[] toBytes(int i) {
|
||||
byte[] b = new byte[4];
|
||||
b[0] = (byte) (i & 0xff);
|
||||
b[1] = (byte) ((i >> 8) & 0xff);
|
||||
b[2] = (byte) ((i >> 16) & 0xff);
|
||||
b[3] = (byte) ((i >> 24) & 0xff);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* String 转 byte[]
|
||||
*/
|
||||
public static byte[] toBytes(String str) {
|
||||
return str.getBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* long类型转成byte数组
|
||||
*/
|
||||
public static byte[] toBytes(long number) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(8);
|
||||
buffer.putLong(0, number);
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public static int toInt(byte[] src, int offset) {
|
||||
return ((src[offset] & 0xFF)
|
||||
| ((src[offset + 1] & 0xFF) << 8)
|
||||
| ((src[offset + 2] & 0xFF) << 16)
|
||||
| ((src[offset + 3] & 0xFF) << 24));
|
||||
}
|
||||
|
||||
public static int toInt(byte[] src) {
|
||||
return toInt(src, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组到long的转换.
|
||||
*/
|
||||
public static long toLong(byte[] b) {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(8);
|
||||
buffer.put(b, 0, b.length);
|
||||
return buffer.getLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* byte[] 转 short[]
|
||||
* short: 2字节
|
||||
*/
|
||||
public static short[] toShorts(byte[] src) {
|
||||
int count = src.length >> 1;
|
||||
short[] dest = new short[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
dest[i] = (short) ((src[i * 2] & 0xff) | ((src[2 * i + 1] & 0xff) << 8));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
public static byte[] merger(byte[] bt1, byte[] bt2) {
|
||||
byte[] bt3 = new byte[bt1.length + bt2.length];
|
||||
System.arraycopy(bt1, 0, bt3, 0, bt1.length);
|
||||
System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
|
||||
return bt3;
|
||||
}
|
||||
|
||||
public static String toString(byte[] b) {
|
||||
return Arrays.toString(b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
package com.zhjt.mogo_core_function_devatools.badcase.record.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.mogo.eagle.core.utilcode.util.FileUtils;
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.record.RecordConfig;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
/**
|
||||
* @author zhaolewei on 2018/7/3.
|
||||
* pcm 转 wav 工具类
|
||||
* http://soundfile.sapp.org/doc/WaveFormat/
|
||||
*/
|
||||
public class WavUtils {
|
||||
private static final String TAG = WavUtils.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* 生成wav格式的Header
|
||||
* wave是RIFF文件结构,每一部分为一个chunk,其中有RIFF WAVE chunk,
|
||||
* FMT Chunk,Fact chunk(可选),Data chunk
|
||||
*
|
||||
* @param totalAudioLen 不包括header的音频数据总长度
|
||||
* @param sampleRate 采样率,也就是录制时使用的频率
|
||||
* @param channels audioRecord的频道数量
|
||||
* @param sampleBits 位宽
|
||||
*/
|
||||
public static byte[] generateWavFileHeader(int totalAudioLen, int sampleRate, int channels, int sampleBits) {
|
||||
WavHeader wavHeader = new WavHeader(totalAudioLen, sampleRate, (short) channels, (short) sampleBits);
|
||||
return wavHeader.getHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将header写入到pcm文件中 不修改文件名
|
||||
*
|
||||
* @param file 写入的pcm文件
|
||||
* @param header wav头数据
|
||||
*/
|
||||
public static void writeHeader(File file, byte[] header) {
|
||||
if (!FileUtils.isFile(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RandomAccessFile wavRaf = null;
|
||||
try {
|
||||
wavRaf = new RandomAccessFile(file, "rw");
|
||||
wavRaf.seek(0);
|
||||
wavRaf.write(header);
|
||||
wavRaf.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (wavRaf != null) {
|
||||
wavRaf.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pcm 转 WAV 文件
|
||||
*
|
||||
* @param pcmFile File
|
||||
* @param header wavHeader
|
||||
* @throws IOException Exception
|
||||
*/
|
||||
public static void pcmToWav(File pcmFile, byte[] header) throws IOException {
|
||||
if (!FileUtils.isFile(pcmFile)) {
|
||||
return;
|
||||
}
|
||||
String pcmPath = pcmFile.getAbsolutePath();
|
||||
String wavPath = pcmPath.substring(0, pcmPath.length() - 4) + ".wav";
|
||||
writeHeader(new File(wavPath), header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取WAV文件的头信息
|
||||
*
|
||||
* @param wavFilePath 文件地址
|
||||
* @return header
|
||||
*/
|
||||
private static byte[] getHeader(String wavFilePath) {
|
||||
if (!new File(wavFilePath).isFile()) {
|
||||
return null;
|
||||
}
|
||||
byte[] buffer = null;
|
||||
File file = new File(wavFilePath);
|
||||
final int size = 44;
|
||||
FileInputStream fis = null;
|
||||
ByteArrayOutputStream bos = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
bos = new ByteArrayOutputStream(size);
|
||||
byte[] b = new byte[size];
|
||||
int len;
|
||||
if ((len = fis.read(b)) != size) {
|
||||
return null;
|
||||
}
|
||||
bos.write(b, 0, len);
|
||||
buffer = bos.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (fis != null) {
|
||||
fis.close();
|
||||
fis = null;
|
||||
}
|
||||
if (bos != null) {
|
||||
bos.close();
|
||||
bos = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取wav音频时长 ms
|
||||
*
|
||||
* @param filePath wav文件路径
|
||||
* @return 时长 -1: 获取失败
|
||||
*/
|
||||
public static long getWavDuration(String filePath) {
|
||||
if (!filePath.endsWith(RecordConfig.RecordFormat.WAV.getExtension())) {
|
||||
return -1;
|
||||
}
|
||||
byte[] header = getHeader(filePath);
|
||||
return getWavDuration(header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取wav音频时长 ms
|
||||
*
|
||||
* @param header wav音频文件字节数组
|
||||
* @return 时长 -1: 获取失败
|
||||
*/
|
||||
public static long getWavDuration(byte[] header) {
|
||||
if (header == null || header.length < 44) {
|
||||
Log.e(TAG, "header size有误");
|
||||
return -1;
|
||||
}
|
||||
int byteRate = ByteUtils.toInt(header, 28);//28-31
|
||||
int waveSize = ByteUtils.toInt(header, 40);//40-43
|
||||
return waveSize * 1000L / byteRate;
|
||||
}
|
||||
|
||||
public static String headerToString(byte[] header) {
|
||||
if (header == null || header.length < 44) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
stringBuilder.append((char) header[i]);
|
||||
}
|
||||
stringBuilder.append(",");
|
||||
|
||||
stringBuilder.append(ByteUtils.toInt(header, 4));
|
||||
stringBuilder.append(",");
|
||||
|
||||
for (int i = 8; i < 16; i++) {
|
||||
stringBuilder.append((char) header[i]);
|
||||
}
|
||||
stringBuilder.append(",");
|
||||
|
||||
for (int i = 16; i < 24; i++) {
|
||||
stringBuilder.append(header[i]);
|
||||
}
|
||||
stringBuilder.append(",");
|
||||
|
||||
stringBuilder.append(ByteUtils.toInt(header, 24));
|
||||
stringBuilder.append(",");
|
||||
|
||||
stringBuilder.append(ByteUtils.toInt(header, 28));
|
||||
stringBuilder.append(",");
|
||||
|
||||
for (int i = 32; i < 36; i++) {
|
||||
stringBuilder.append(header[i]);
|
||||
}
|
||||
stringBuilder.append(",");
|
||||
|
||||
for (int i = 36; i < 40; i++) {
|
||||
stringBuilder.append((char) header[i]);
|
||||
}
|
||||
stringBuilder.append(",");
|
||||
|
||||
stringBuilder.append(ByteUtils.toInt(header, 40));
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public static class WavHeader {
|
||||
/**
|
||||
* RIFF数据块
|
||||
*/
|
||||
final String riffChunkId = "RIFF";
|
||||
int riffChunkSize;
|
||||
final String riffType = "WAVE";
|
||||
|
||||
/**
|
||||
* FORMAT 数据块
|
||||
*/
|
||||
final String formatChunkId = "fmt ";
|
||||
final int formatChunkSize = 16;
|
||||
final short audioFormat = 1;
|
||||
short channels;
|
||||
int sampleRate;
|
||||
int byteRate;
|
||||
short blockAlign;
|
||||
short sampleBits;
|
||||
|
||||
/**
|
||||
* FORMAT 数据块
|
||||
*/
|
||||
final String dataChunkId = "data";
|
||||
int dataChunkSize;
|
||||
|
||||
WavHeader(int totalAudioLen, int sampleRate, short channels, short sampleBits) {
|
||||
this.riffChunkSize = totalAudioLen;
|
||||
this.channels = channels;
|
||||
this.sampleRate = sampleRate;
|
||||
this.byteRate = sampleRate * sampleBits / 8 * channels;
|
||||
this.blockAlign = (short) (channels * sampleBits / 8);
|
||||
this.sampleBits = sampleBits;
|
||||
this.dataChunkSize = totalAudioLen - 44;
|
||||
}
|
||||
|
||||
public byte[] getHeader() {
|
||||
byte[] result;
|
||||
result = ByteUtils.merger(ByteUtils.toBytes(riffChunkId), ByteUtils.toBytes(riffChunkSize));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(riffType));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(formatChunkId));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(formatChunkSize));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(audioFormat));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(channels));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(sampleRate));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(byteRate));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(blockAlign));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(sampleBits));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(dataChunkId));
|
||||
result = ByteUtils.merger(result, ByteUtils.toBytes(dataChunkSize));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import retrofit2.http.*
|
||||
internal interface BadCaseApi {
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST("/yycp-vehicle-management-service/tool/badcase/add")
|
||||
@POST("/yycp-vehicle-management-service/tool/badcase/add/v2")
|
||||
suspend fun post(@FieldMap map: Map<String, String>): Response<UploadResult>
|
||||
|
||||
@GET("/yycp-vehicle-management-service/tool/badcase/reasons")
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.zhjt.mogo_core_function_devatools.env
|
||||
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import android.os.Process
|
||||
import com.mogo.commons.constants.*
|
||||
import com.mogo.commons.debug.*
|
||||
import com.mogo.eagle.core.function.call.map.*
|
||||
import com.mogo.eagle.core.utilcode.mogo.storage.*
|
||||
import com.mogo.eagle.core.utilcode.util.*
|
||||
|
||||
|
||||
object EnvChangeManager {
|
||||
|
||||
private val sp = Utils.getApp().getSharedPreferences("env_change", MODE_PRIVATE)
|
||||
|
||||
private fun updateCityCode(cityCode: String?) {
|
||||
sp.edit().putString("city_code", cityCode).commit()
|
||||
}
|
||||
|
||||
private fun updateNetMode(netMode: Int) {
|
||||
sp.edit().putInt("net_mode", netMode).commit()
|
||||
}
|
||||
|
||||
private fun getConfig() : Pair<String, Int>? {
|
||||
val cityCode = sp.getString("city_code", null)
|
||||
val severType = sp.getInt("net_mode", -1)
|
||||
if (cityCode == null || severType == -1) {
|
||||
return null
|
||||
}
|
||||
return Pair(cityCode, severType)
|
||||
}
|
||||
|
||||
fun getCityName(): String {
|
||||
val cache = getConfig()
|
||||
return if (cache == null) {
|
||||
val cityCode = CallerMapLocationListenerManager.getCurrentLocation()?.cityCode ?: SharedPrefsMgr.getInstance(Utils.getApp()).getString(SharedPrefsConstants.LOCATION_CITY_CODE) ?: "010"
|
||||
updateCityCode(cityCode)
|
||||
when(cityCode) {
|
||||
"010" -> "北京"
|
||||
"0734" -> "衡阳"
|
||||
else -> "未知"
|
||||
}
|
||||
} else {
|
||||
when(cache.first) {
|
||||
"010" -> "北京"
|
||||
"0734" -> "衡阳"
|
||||
else -> "未知"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getNetMode(): String {
|
||||
val cache = getConfig()
|
||||
if (cache == null) {
|
||||
val mode = DebugConfig.getNetMode()
|
||||
updateNetMode(mode)
|
||||
return when(mode) {
|
||||
DebugConfig.NET_MODE_RELEASE -> "生产"
|
||||
DebugConfig.NET_MODE_QA -> "测试"
|
||||
DebugConfig.NET_MODE_DEMO -> "演示"
|
||||
else -> "未知"
|
||||
}
|
||||
} else {
|
||||
return when(cache.second) {
|
||||
DebugConfig.NET_MODE_RELEASE -> "生产"
|
||||
DebugConfig.NET_MODE_QA -> "测试"
|
||||
DebugConfig.NET_MODE_DEMO -> "演示"
|
||||
else -> "未知"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun changeTo(cityCode: String, netMode: Int) {
|
||||
updateCityCode(cityCode)
|
||||
updateNetMode(netMode)
|
||||
restartApp()
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
updateCityCode(null)
|
||||
updateNetMode(-1)
|
||||
restartApp()
|
||||
}
|
||||
|
||||
private fun restartApp() {
|
||||
Utils.getApp().startActivity(Utils.getApp().packageManager.getLaunchIntentForPackage(Utils.getApp().packageName))
|
||||
Process.killProcess(Process.myPid())
|
||||
}
|
||||
|
||||
fun getEnvConfig(): EnvConfig? = getConfig()?.let {
|
||||
EnvConfig(it.first, it.second,
|
||||
if (it.first == "010") 116.397446 else 112.582654,
|
||||
if (it.first == "010") 39.909004 else 26.816478)
|
||||
}
|
||||
|
||||
data class EnvConfig(val cityCode: String, val netMode: Int, val lat: Double, val lon: Double)
|
||||
}
|
||||
@@ -7,7 +7,11 @@ import androidx.lifecycle.*
|
||||
import androidx.lifecycle.Lifecycle.Event
|
||||
import androidx.lifecycle.Lifecycle.Event.ON_CREATE
|
||||
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
|
||||
import com.mogo.eagle.core.function.call.autopilot.*
|
||||
import com.mogo.eagle.core.utilcode.kotlin.*
|
||||
import com.mogo.eagle.core.utilcode.util.AppStateManager
|
||||
import com.mogo.eagle.core.utilcode.util.IAppStateListener
|
||||
import com.zhjt.mogo_core_function_devatools.ext.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.CanStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.GpsStatus
|
||||
@@ -27,17 +31,57 @@ import com.zhjt.mogo_core_function_devatools.status.flow.trace.TracingImpl
|
||||
import com.zhjt.mogo_core_function_devatools.status.ui.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import mogo_msg.MogoReportMsg
|
||||
|
||||
object StatusManager {
|
||||
|
||||
private const val TAG = "StatusManager"
|
||||
|
||||
private lateinit var model: StatusModel
|
||||
|
||||
private var hidePop: (() -> Unit)? = null
|
||||
|
||||
private var timer: Job? = null
|
||||
|
||||
private val listener = object : IMoGoAutopilotStatusListener {
|
||||
override fun onAutopilotGuardian(guardianInfo: MogoReportMsg.MogoReportMessage?) {
|
||||
super.onAutopilotGuardian(guardianInfo)
|
||||
guardianInfo?.code?.takeIf {
|
||||
it.contains("RTK_STATUS", true) || it.contains("CAN", true)
|
||||
}?.run {
|
||||
req()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val appStateListener = object : IAppStateListener {
|
||||
|
||||
override fun onAppStateChanged(isForeground: Boolean) {
|
||||
if (isForeground) {
|
||||
req()
|
||||
} else {
|
||||
timer?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val flows: ArrayList<IFlow<out Status>> by lazy {
|
||||
ArrayList()
|
||||
}
|
||||
|
||||
private fun req() {
|
||||
timer?.cancel()
|
||||
model.viewModelScope.launch(Dispatchers.IO) {
|
||||
CallerAutoPilotManager.sendStatusQueryReq()
|
||||
while (true) {
|
||||
delay(60000) //一分钟主动请求一次
|
||||
CallerAutoPilotManager.sendStatusQueryReq()
|
||||
}
|
||||
}.also {
|
||||
timer = it
|
||||
}
|
||||
}
|
||||
|
||||
fun init(ctx: Context) {
|
||||
val owner = ctx as? ViewModelStoreOwner ?: throw IllegalStateException("ctx: $ctx is a instance of ViewModelStoreOwner.")
|
||||
model = ViewModelProvider(owner).get(StatusModel::class.java)
|
||||
@@ -55,6 +99,9 @@ object StatusManager {
|
||||
|
||||
private fun onCreate(ctx: Context) {
|
||||
val values = model.status.value?.second ?: throw IllegalStateException("state is not right.")
|
||||
CallerAutoPilotStatusListenerManager.addListener(TAG, listener)
|
||||
AppStateManager.registerAppStateListener(appStateListener)
|
||||
req()
|
||||
values.map {
|
||||
when (it) {
|
||||
is CanStatus -> CanImpl(ctx)
|
||||
@@ -79,7 +126,6 @@ object StatusManager {
|
||||
f.onCreate()
|
||||
}
|
||||
}
|
||||
|
||||
ctx.normalPop(content,
|
||||
width = 665.PX,
|
||||
height = WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
@@ -88,7 +134,12 @@ object StatusManager {
|
||||
isFocusable = false).also { hidePop = it }
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun onDestroy(ctx: Context) {
|
||||
CallerAutoPilotStatusListenerManager.removeListener(TAG)
|
||||
AppStateManager.unRegisterAppStateListener(appStateListener)
|
||||
timer?.cancel()
|
||||
flows.forEach {
|
||||
it.onDestroy()
|
||||
}
|
||||
|
||||
@@ -81,12 +81,16 @@ internal class GpsStatus(val enabled: Boolean = false, val isGranted: Boolean =
|
||||
/**
|
||||
* RTK/GNSS定位状态
|
||||
*/
|
||||
internal class RTKStatus(var enabled: Boolean = false): Status() {
|
||||
internal class RTKStatus(var enabled: Boolean = false, var desc: String = "RTK"): Status() {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as RTKStatus
|
||||
if (enabled != other.enabled) return false
|
||||
|
||||
if (desc != other.desc) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.flow.can
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.util.Log
|
||||
import chassis.Chassis.GearPosition
|
||||
import chassis.Chassis.LightSwitch
|
||||
import com.mogo.eagle.core.function.api.autopilot.*
|
||||
@@ -11,6 +11,8 @@ import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.CanStatus
|
||||
import kotlinx.coroutines.*
|
||||
import mogo_msg.MogoReportMsg.MogoReportMessage
|
||||
import system_master.SystemStatusInfo.StatusInfo
|
||||
import java.util.concurrent.atomic.*
|
||||
|
||||
internal class CanImpl(ctx: Context): IFlow<CanStatus>(ctx), IMoGoAutopilotVehicleStateListener, IMoGoAutopilotStatusListener {
|
||||
|
||||
@@ -19,6 +21,7 @@ internal class CanImpl(ctx: Context): IFlow<CanStatus>(ctx), IMoGoAutopilotVehic
|
||||
}
|
||||
|
||||
private var job: Job? = null
|
||||
private val state: AtomicInteger by lazy { AtomicInteger(Int.MIN_VALUE) }
|
||||
|
||||
override fun onCreate() {
|
||||
send(CanStatus(CallerAutoPilotManager.isConnected()))
|
||||
@@ -33,7 +36,7 @@ internal class CanImpl(ctx: Context): IFlow<CanStatus>(ctx), IMoGoAutopilotVehic
|
||||
|
||||
private fun isCanEnabled(): Boolean {
|
||||
val code = CallerAutoPilotStatusListenerManager.getAutoPilotReportMessageCode()
|
||||
return CallerAutoPilotManager.isConnected() && code != "EHW_CAN"
|
||||
return CallerAutoPilotManager.isConnected() && code != "EHW_CAN" && (state.get() == Int.MIN_VALUE || state.get() == 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -69,12 +72,19 @@ internal class CanImpl(ctx: Context): IFlow<CanStatus>(ctx), IMoGoAutopilotVehic
|
||||
timeOutCheck()
|
||||
}
|
||||
|
||||
override fun onAutopilotStatusRespByQuery(status: StatusInfo) {
|
||||
val state = status.healthInfoList?.find { "can_adapter".equals(it.name, true) }?.state?.ordinal
|
||||
Log.d(TAG, "state: $state")
|
||||
if (state != null) {
|
||||
this.state.set(state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun timeOutCheck() {
|
||||
job?.safeCancel()
|
||||
launch(Dispatchers.Default) {
|
||||
delay(4000)
|
||||
send(CanStatus(false))
|
||||
send(CanStatus(isCanEnabled()))
|
||||
}.also { job = it }
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ import com.zhjt.mogo_core_function_devatools.status.entity.RTKStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import kotlinx.coroutines.*
|
||||
import mogo.telematics.pad.MessagePad.GnssInfo
|
||||
import system_master.SystemStatusInfo.HealthInfo
|
||||
import system_master.SystemStatusInfo.StatusInfo
|
||||
import java.util.concurrent.atomic.*
|
||||
|
||||
internal class RTKImpl(ctx: Context): IFlow<RTKStatus>(ctx), IMoGoAutopilotCarStateListener, IMoGoAutopilotStatusListener {
|
||||
companion object {
|
||||
@@ -17,6 +20,10 @@ internal class RTKImpl(ctx: Context): IFlow<RTKStatus>(ctx), IMoGoAutopilotCarSt
|
||||
|
||||
private var job: Job? = null
|
||||
|
||||
private val healthInfo by lazy {
|
||||
AtomicReference<HealthInfo>(null)
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
send(RTKStatus(isRTKEnabled()))
|
||||
Log.d(TAG, "-- onCreate --")
|
||||
@@ -27,29 +34,42 @@ internal class RTKImpl(ctx: Context): IFlow<RTKStatus>(ctx), IMoGoAutopilotCarSt
|
||||
private fun isRTKEnabled(): Boolean {
|
||||
val code = CallerAutoPilotStatusListenerManager.getAutoPilotReportMessageCode()
|
||||
val gnssInfo = CallerAutopilotCarStatusListenerManager.getCurrentGnssInfo()
|
||||
val status = healthInfo.get()
|
||||
return CallerAutoPilotManager.isConnected() && (
|
||||
code != "EHW_RTK" &&
|
||||
code != "EHW_GNSS" &&
|
||||
code != "ESYS_RTK_STATUS_FAULT" &&
|
||||
code != "ELCT_RTK_STATUS_FAULT" &&
|
||||
code != "ELCT_RTK_STATUS_UNKNOWN") && gnssInfo != null
|
||||
code != "ELCT_RTK_STATUS_UNKNOWN") && gnssInfo != null && (status == null || status.state?.ordinal == 0)
|
||||
}
|
||||
|
||||
override fun onAutopilotCarStateData(gnssInfo: GnssInfo?) {
|
||||
send(RTKStatus(isRTKEnabled()))
|
||||
send(RTKStatus(isRTKEnabled(), getDesc()))
|
||||
timeOutCheck()
|
||||
}
|
||||
|
||||
override fun onAutopilotIpcConnectStatusChanged(status: Int, reason: String?) {
|
||||
super.onAutopilotIpcConnectStatusChanged(status, reason)
|
||||
send(RTKStatus(isRTKEnabled()))
|
||||
send(RTKStatus(isRTKEnabled(), getDesc()))
|
||||
}
|
||||
|
||||
override fun onAutopilotStatusRespByQuery(status: StatusInfo) {
|
||||
val info = status.healthInfoList?.find { "localization".equals(it.name, true) }
|
||||
Log.d(TAG, "info: $info")
|
||||
if (info != null) {
|
||||
healthInfo.set(info)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDesc(): String {
|
||||
return healthInfo.get()?.desc?.uppercase() ?: "RTK"
|
||||
}
|
||||
|
||||
private fun timeOutCheck() {
|
||||
job?.safeCancel()
|
||||
launch(Dispatchers.Default) {
|
||||
delay(4000)
|
||||
send(RTKStatus(false))
|
||||
send(RTKStatus(isRTKEnabled(), getDesc()))
|
||||
}.also { job = it }
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ internal class StatusAdapter(val ctx: Context, var data: ArrayList<Status>): Rec
|
||||
} else {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_rtk_disable)
|
||||
}
|
||||
tv.text = "RTK"
|
||||
tv.text = status.desc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 332 B |
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/icon_ai_select" android:state_focused="true" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/icon_ai_select" android:state_focused="false" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/icon_ai_select" android:state_selected="true" />
|
||||
<item android:drawable="@drawable/icon_ai_select" android:state_focused="true" />
|
||||
<item android:drawable="@drawable/icon_ai_normal" />
|
||||
</selector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="#3A57C5"
|
||||
android:endColor="#323C6F"
|
||||
android:angle="0"
|
||||
/>
|
||||
<corners android:radius="40px" />
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="#3A57C5"
|
||||
android:endColor="#1E2E87"
|
||||
android:angle="0"
|
||||
/>
|
||||
<corners android:radius="8px" />
|
||||
</shape>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval"
|
||||
android:useLevel="false">
|
||||
<solid android:color="#6D7BAF" />
|
||||
<stroke android:width="5px" android:color="#FFCCCCCC" />
|
||||
<size
|
||||
android:width="105px"
|
||||
android:height="105px" />
|
||||
</shape>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval"
|
||||
android:useLevel="false">
|
||||
<gradient
|
||||
android:startColor="#029DFF"
|
||||
android:endColor="#0056FF"
|
||||
android:angle="145"
|
||||
/>
|
||||
<stroke android:width="5px" android:color="#FFA7B6F0" />
|
||||
<size
|
||||
android:width="105px"
|
||||
android:height="105px" />
|
||||
</shape>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/icon_bad_case_select" android:state_focused="true" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/icon_bad_case_select" android:state_focused="false" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/icon_bad_case_select" android:state_selected="true" />
|
||||
<item android:drawable="@drawable/icon_bad_case_select" android:state_focused="true" />
|
||||
<item android:drawable="@drawable/icon_bad_case_normal" />
|
||||
</selector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:state_checked="false"
|
||||
android:drawable="@drawable/icon_ap_badcase_default" />
|
||||
<item
|
||||
android:state_checked="true"
|
||||
android:drawable="@drawable/icon_ap_badcase_check" />
|
||||
</selector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#263869" />
|
||||
<corners android:radius="20px" />
|
||||
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#5EBFFF" />
|
||||
|
||||
</shape>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FF3B4577" />
|
||||
<corners android:radius="8px" />
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="#029DFF"
|
||||
android:endColor="#0056FF"
|
||||
android:angle="0"
|
||||
/>
|
||||
<corners android:radius="8px" />
|
||||
</shape>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:startColor="#029DFF"
|
||||
android:endColor="#0056FF"
|
||||
android:angle="0"
|
||||
/>
|
||||
<corners android:radius="24px" />
|
||||
</shape>
|
||||
@@ -0,0 +1,327 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:layout_width="960px"
|
||||
tools:layout_height="match_parent"
|
||||
android:background="#F0151D41"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivConfigClose"
|
||||
android:layout_width="107px"
|
||||
android:layout_height="107px"
|
||||
android:layout_marginTop="66px"
|
||||
android:layout_marginEnd="40px"
|
||||
android:src="@drawable/icon_close_nor"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/viewConfigTitleLine"
|
||||
android:layout_width="14px"
|
||||
android:layout_height="50px"
|
||||
android:layout_marginStart="80px"
|
||||
android:layout_marginTop="92px"
|
||||
android:background="#2966EC"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvConfigTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Case上报"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="42px"
|
||||
app:layout_constraintTop_toTopOf="@id/viewConfigTitleLine"
|
||||
app:layout_constraintBottom_toBottomOf="@id/viewConfigTitleLine"
|
||||
app:layout_constraintLeft_toLeftOf="@id/viewConfigTitleLine"
|
||||
android:layout_marginStart="50px"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvConfigSave"
|
||||
android:layout_width="800px"
|
||||
android:layout_height="126px"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:text="保存"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="42px"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/save_button_bg"
|
||||
android:layout_marginBottom="100px"
|
||||
/>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="800px"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/viewConfigTitleLine"
|
||||
app:layout_constraintBottom_toTopOf="@id/tvConfigSave"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:layout_marginTop="100px"
|
||||
android:scrollbars="none"
|
||||
>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/viewConfigTitleLine"
|
||||
app:layout_constraintLeft_toLeftOf="@id/viewConfigTitleLine"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvIdentityTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="身份"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbSafetyOfficer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="安全员"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:button="@null"
|
||||
android:checked="true"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="30px"
|
||||
android:paddingTop="@dimen/dp_30"
|
||||
android:paddingBottom="@dimen/dp_30"
|
||||
android:paddingEnd="@dimen/dp_30"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvIdentityTitle"
|
||||
app:layout_constraintLeft_toLeftOf="@id/tvIdentityTitle"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbDeveloper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="QA、研发"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="30px"
|
||||
android:paddingTop="@dimen/dp_30"
|
||||
android:paddingBottom="@dimen/dp_30"
|
||||
android:paddingEnd="@dimen/dp_30"
|
||||
android:layout_marginStart="@dimen/dp_200"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvIdentityTitle"
|
||||
app:layout_constraintLeft_toRightOf="@id/rbSafetyOfficer"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbProduct"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="产品、运营、演示"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="30px"
|
||||
android:paddingTop="@dimen/dp_30"
|
||||
android:paddingBottom="@dimen/dp_30"
|
||||
android:paddingEnd="@dimen/dp_30"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbSafetyOfficer"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbSafetyOfficer"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvRecordTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="录制窗口"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbProduct"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbProduct"
|
||||
android:layout_marginTop="@dimen/dp_80"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInitiativeTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="主动:"
|
||||
android:textColor="#FFA7B6F0"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvRecordTitle"
|
||||
app:layout_constraintLeft_toLeftOf="@id/tvRecordTitle"
|
||||
android:layout_marginTop="@dimen/dp_30"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInitiativePre"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="前:"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvInitiativeTitle"
|
||||
app:layout_constraintLeft_toLeftOf="@id/tvInitiativeTitle"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/etInitiativePreTime"
|
||||
android:layout_width="228px"
|
||||
android:layout_height="84px"
|
||||
app:layout_constraintTop_toTopOf="@id/tvInitiativePre"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvInitiativePre"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvInitiativePre"
|
||||
android:background="@drawable/badcase_record_edit_bg"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:hint="12s"
|
||||
android:textColorHint="#FFFFFFFF"
|
||||
android:gravity="center"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInitiativeAfter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="后:"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvInitiativePre"
|
||||
app:layout_constraintLeft_toLeftOf="@id/tvInitiativePre"
|
||||
android:layout_marginTop="@dimen/dp_60"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/etInitiativeAfterTime"
|
||||
android:layout_width="228px"
|
||||
android:layout_height="84px"
|
||||
app:layout_constraintTop_toTopOf="@id/tvInitiativeAfter"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvInitiativeAfter"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvInitiativeAfter"
|
||||
android:background="@drawable/badcase_record_edit_bg"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:hint="8s"
|
||||
android:textColorHint="#FFFFFFFF"
|
||||
android:gravity="center"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPassiveTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="被动:"
|
||||
android:textColor="#FFA7B6F0"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintTop_toTopOf="@id/tvInitiativeTitle"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvInitiativeTitle"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvInitiativeTitle"
|
||||
android:layout_marginStart="@dimen/dp_300"
|
||||
android:alpha="0.5"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPassivePre"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="前:"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvPassiveTitle"
|
||||
app:layout_constraintLeft_toLeftOf="@id/tvPassiveTitle"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
android:alpha="0.5"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/etPassivePreTime"
|
||||
android:layout_width="228px"
|
||||
android:layout_height="84px"
|
||||
app:layout_constraintTop_toTopOf="@id/tvPassivePre"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvPassivePre"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvPassivePre"
|
||||
android:background="@drawable/badcase_record_edit_bg"
|
||||
android:alpha="0.5"
|
||||
android:text="12s"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:gravity="center"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPassiveAfter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="后:"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvPassivePre"
|
||||
app:layout_constraintLeft_toLeftOf="@id/tvPassivePre"
|
||||
android:layout_marginTop="@dimen/dp_60"
|
||||
android:alpha="0.5"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/etPassiveAfterTime"
|
||||
android:layout_width="228px"
|
||||
android:layout_height="84px"
|
||||
app:layout_constraintTop_toTopOf="@id/tvPassiveAfter"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvPassiveAfter"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvPassiveAfter"
|
||||
android:background="@drawable/badcase_record_edit_bg"
|
||||
android:alpha="0.5"
|
||||
android:text="8s"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:gravity="center"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivRecordTemplate"
|
||||
android:layout_width="19px"
|
||||
android:layout_height="15px"
|
||||
android:src="@drawable/icon_expand"
|
||||
app:layout_constraintLeft_toLeftOf="@id/tvInitiativeAfter"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvInitiativeAfter"
|
||||
android:layout_marginTop="@dimen/dp_80"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvRecordTemplate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="预置录制模板"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintTop_toTopOf="@id/ivRecordTemplate"
|
||||
app:layout_constraintBottom_toBottomOf="@id/ivRecordTemplate"
|
||||
app:layout_constraintLeft_toRightOf="@id/ivRecordTemplate"
|
||||
android:layout_marginStart="@dimen/dp_30"
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rgRecordConfig"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintLeft_toLeftOf="@id/tvRecordTemplate"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvRecordTemplate"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginTop="@dimen/dp_20"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</merge>
|
||||
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.mogo.eagle.core.widget.RoundConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="924px"
|
||||
android:layout_height="668px"
|
||||
app:roundLayoutRadius="40px"
|
||||
android:background="@color/module_switch_map_bg">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="113px"
|
||||
android:background="@drawable/ai_collect_title_bg"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCollectNum"
|
||||
android:layout_width="120px"
|
||||
android:layout_height="113px"
|
||||
android:background="@drawable/icon_num_bg"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="46px"
|
||||
android:gravity="center"
|
||||
android:text="1"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCollectTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvCollectNum"
|
||||
app:layout_constraintTop_toTopOf="@id/tvCollectNum"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvCollectNum"
|
||||
android:text="时间:14:23:10"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:layout_marginStart="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbLargeCar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="大型车:大货、大巴、特种车辆"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvCollectNum"
|
||||
android:layout_marginStart="@dimen/dp_50"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
android:checked="true"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbTrafficLight"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="交通灯:水平、箭头、雨天交通灯"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbLargeCar"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbLargeCar"
|
||||
android:layout_marginTop="@dimen/dp_30"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbWater"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="积水:距离10米内,面积大于1平米"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbTrafficLight"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbTrafficLight"
|
||||
android:layout_marginTop="@dimen/dp_30"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbConstruction"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="施工:锥桶、路障"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbWater"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbWater"
|
||||
android:layout_marginTop="@dimen/dp_30"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbAccident"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="车祸路段:有三角板"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toTopOf="@id/rbConstruction"
|
||||
app:layout_constraintBottom_toBottomOf="@id/rbConstruction"
|
||||
app:layout_constraintLeft_toRightOf="@id/rbConstruction"
|
||||
android:layout_marginStart="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbRain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="中雨交通流"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbConstruction"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbConstruction"
|
||||
android:layout_marginTop="@dimen/dp_30"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbNightTraffic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="夜间交通流"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbAccident"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbAccident"
|
||||
android:layout_marginTop="@dimen/dp_30"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCollectReport"
|
||||
android:layout_width="270px"
|
||||
android:layout_height="70px"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@id/tvCollectCancel"
|
||||
android:background="@drawable/report_button_bg"
|
||||
android:text="上报"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="30px"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="@dimen/dp_40"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCollectCancel"
|
||||
android:layout_width="270px"
|
||||
android:layout_height="70px"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvCollectReport"
|
||||
android:text="取消"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="30px"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/cancel_button_bg"
|
||||
android:layout_marginBottom="@dimen/dp_40"
|
||||
/>
|
||||
|
||||
|
||||
</com.mogo.eagle.core.widget.RoundConstraintLayout>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivBadCaseTools"
|
||||
android:layout_width="120px"
|
||||
android:layout_height="120px"
|
||||
android:src="@drawable/bad_case_selector"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAiCollectTools"
|
||||
android:layout_width="120px"
|
||||
android:layout_height="120px"
|
||||
android:src="@drawable/ai_collect_selector"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/ivBadCaseTools"
|
||||
android:layout_marginStart="50px"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,210 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.mogo.eagle.core.widget.RoundConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="924px"
|
||||
android:layout_height="668px"
|
||||
app:roundLayoutRadius="40px"
|
||||
android:background="@color/module_switch_map_bg">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="113px"
|
||||
android:background="@drawable/ai_collect_title_bg"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInitiativeNum"
|
||||
android:layout_width="120px"
|
||||
android:layout_height="113px"
|
||||
android:background="@drawable/icon_num_bg"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="46px"
|
||||
android:gravity="center"
|
||||
android:text="1"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInitiativeTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvInitiativeNum"
|
||||
app:layout_constraintTop_toTopOf="@id/tvInitiativeNum"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvInitiativeNum"
|
||||
android:text="时间:14:23:10"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:layout_marginStart="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInitiativeIdentity"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="@id/tvInitiativeNum"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tvInitiativeNum"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:text="身份:QA"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="38px"
|
||||
android:layout_marginEnd="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="严重画龙"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvInitiativeNum"
|
||||
android:layout_marginStart="@dimen/dp_50"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbTwo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="点刹、顿挫"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvInitiativeNum"
|
||||
app:layout_constraintLeft_toRightOf="@id/rbOne"
|
||||
android:layout_marginStart="@dimen/dp_50"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbThree"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="速度过慢"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvInitiativeNum"
|
||||
app:layout_constraintLeft_toRightOf="@id/rbTwo"
|
||||
android:layout_marginStart="@dimen/dp_50"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbFour"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="速度过快"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbOne"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbOne"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbFive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="存在碰撞风险"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbTwo"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbTwo"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rbSix"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="感知、定位、地图等其他"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="34px"
|
||||
android:button="@null"
|
||||
android:drawableLeft="@drawable/badcase_radio_button_style"
|
||||
android:drawablePadding="@dimen/dp_20"
|
||||
app:layout_constraintTop_toBottomOf="@id/rbThree"
|
||||
app:layout_constraintLeft_toLeftOf="@id/rbThree"
|
||||
android:layout_marginTop="@dimen/dp_50"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInitiativeReport"
|
||||
android:layout_width="270px"
|
||||
android:layout_height="70px"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@id/tvInitiativeCancel"
|
||||
android:background="@drawable/report_button_bg"
|
||||
android:text="上报"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="30px"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="@dimen/dp_40"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInitiativeCancel"
|
||||
android:layout_width="270px"
|
||||
android:layout_height="70px"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/tvInitiativeReport"
|
||||
android:text="取消"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="30px"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/cancel_button_bg"
|
||||
android:layout_marginBottom="@dimen/dp_40"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:id="@+id/viewAudioBg"
|
||||
android:layout_width="824px"
|
||||
android:layout_height="174px"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/tvInitiativeReport"
|
||||
android:background="@drawable/bad_case_audio_bg"
|
||||
android:layout_marginBottom="@dimen/dp_40"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:id="@+id/viewAudioButton"
|
||||
android:layout_width="105px"
|
||||
android:layout_height="105px"
|
||||
app:layout_constraintLeft_toLeftOf="@id/viewAudioBg"
|
||||
app:layout_constraintRight_toRightOf="@id/viewAudioBg"
|
||||
app:layout_constraintTop_toTopOf="@id/viewAudioBg"
|
||||
app:layout_constraintBottom_toBottomOf="@id/viewAudioBg"
|
||||
android:background="@drawable/bad_case_audio_normal"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40px"
|
||||
android:layout_height="55px"
|
||||
android:src="@drawable/icon_audio"
|
||||
app:layout_constraintLeft_toLeftOf="@id/viewAudioButton"
|
||||
app:layout_constraintRight_toRightOf="@id/viewAudioButton"
|
||||
app:layout_constraintTop_toTopOf="@id/viewAudioButton"
|
||||
app:layout_constraintBottom_toBottomOf="@id/viewAudioButton"
|
||||
/>
|
||||
|
||||
</com.mogo.eagle.core.widget.RoundConstraintLayout>
|
||||
@@ -38,10 +38,12 @@ import static com.zhjt.dispatch.model.DispatchServiceModel.DISPATCH_RESULT_AFFIR
|
||||
import static com.zhjt.dispatch.model.DispatchServiceModel.DISPATCH_RESULT_MANUAL_CANCEL;
|
||||
import static com.zhjt.dispatch.model.DispatchServiceModel.DISPATCH_RESULT_TIMER_CANCEL;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import mogo.telematics.pad.MessagePad;
|
||||
import mogo_msg.MogoReportMsg;
|
||||
import system_master.SystemStatusInfo;
|
||||
|
||||
//负责监听自动驾驶状态并进行状态上报,自动驾驶路线上报,接收调度指令展示指令弹窗
|
||||
public class DispatchAutoPilotManager implements IMogoOnMessageListener<DispatchAdasAutoPilotLocReceiverBean>
|
||||
@@ -339,4 +341,8 @@ public class DispatchAutoPilotManager implements IMogoOnMessageListener<Dispatch
|
||||
@Override
|
||||
public void onAutopilotIpcConnectStatusChanged(int status, @Nullable String reason) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,7 +391,13 @@ import kotlin.collections.ArrayList
|
||||
|
||||
// 控制 BadCase 按钮展示
|
||||
if (HmiBuildConfig.isShowBadCaseView) {
|
||||
CallerDevaToolsManager.initBadCase(vsBadCaseToolsView)
|
||||
ivBadCaseTools.visibility = View.VISIBLE
|
||||
ivAiCollectTools.visibility = View.VISIBLE
|
||||
CallerDevaToolsManager.initBadCase(ivBadCaseTools)
|
||||
CallerDevaToolsManager.initAiCollect(ivAiCollectTools)
|
||||
}else{
|
||||
ivBadCaseTools.visibility = View.GONE
|
||||
ivAiCollectTools.visibility = View.GONE
|
||||
}
|
||||
|
||||
// 控制 红绿灯 展示
|
||||
@@ -525,88 +543,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,6 +888,14 @@ import kotlin.collections.ArrayList
|
||||
dismissToolsFloatView()
|
||||
}
|
||||
|
||||
override fun showSmallFragment() {
|
||||
// TODO:("展示全览模式地图")
|
||||
}
|
||||
|
||||
override fun hideSmallFragment() {
|
||||
// TODO:("隐藏全览模式地图")
|
||||
}
|
||||
|
||||
private fun showCameraList(cameraList: List<CameraEntity>?) {
|
||||
context?.let {
|
||||
if (cameraViewFloat == null) {
|
||||
|
||||
@@ -11,9 +11,12 @@ import android.text.Html
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.*
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import chassis.Chassis
|
||||
import com.mogo.cloud.passport.MoGoAiCloudClient
|
||||
@@ -52,6 +55,7 @@ import com.mogo.eagle.core.function.hmi.R
|
||||
import com.mogo.eagle.core.function.hmi.ui.logcatch.ILogViewListener
|
||||
import com.mogo.eagle.core.function.hmi.ui.logcatch.LogInfoView
|
||||
import com.mogo.eagle.core.function.hmi.ui.upgrade.UpgradeListAdapter
|
||||
import com.mogo.eagle.core.network.*
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel
|
||||
@@ -66,6 +70,7 @@ import com.mogo.map.uicontroller.VisualAngleMode.*
|
||||
import com.mogo.module.service.routeoverlay.*
|
||||
import com.zhidao.easysocket.utils.L
|
||||
import com.zhidao.support.adas.high.other.permission.BackgrounderPermission
|
||||
import com.zhjt.mogo_core_function_devatools.env.*
|
||||
import kotlinx.android.synthetic.main.view_debug_setting.view.*
|
||||
import mogo.telematics.pad.MessagePad
|
||||
import mogo_msg.MogoReportMsg
|
||||
@@ -536,6 +541,10 @@ class DebugSettingView @JvmOverloads constructor(
|
||||
// 演示模式
|
||||
tbIsDemoMode.setOnCheckedChangeListener { _, isChecked ->
|
||||
CallerAutoPilotManager.setDemoMode(isChecked)
|
||||
if(!isChecked){
|
||||
//关闭美化模式时,通知工控机
|
||||
CallerAutoPilotManager.setIPCDemoMode(isChecked)
|
||||
}
|
||||
FunctionBuildConfig.isDemoMode = isChecked
|
||||
tbIsDrawAutopilotTrajectoryData.isEnabled = !isChecked
|
||||
FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = isChecked
|
||||
@@ -765,7 +774,7 @@ class DebugSettingView @JvmOverloads constructor(
|
||||
/**
|
||||
* 设置鹰眼本地参数配置监听
|
||||
*/
|
||||
private fun setEagleEyeConfigListener() {
|
||||
@SuppressLint("SetTextI18n") private fun setEagleEyeConfigListener() {
|
||||
//初始化刹车加速度阈值信息
|
||||
val brakeThreshold = SharedPrefsMgr.getInstance(context)
|
||||
.getFloat(MoGoConfig.BRAKE_ACCELERATION_THRESHOLD, -2.5F)
|
||||
@@ -810,6 +819,41 @@ class DebugSettingView @JvmOverloads constructor(
|
||||
tbReportWarning.visibility = GONE
|
||||
}
|
||||
|
||||
//切换环境
|
||||
tvCurEnv.text = "当前环境:${EnvChangeManager.getCityName()}${EnvChangeManager.getNetMode()}"
|
||||
btChangeEnv.onClick {
|
||||
PopupMenu(context, btChangeEnv).also { p ->
|
||||
p.menuInflater.inflate(R.menu.menu_env_pop, p.menu)
|
||||
MenuCompat.setGroupDividerEnabled(p.menu, true)
|
||||
p.setOnMenuItemClickListener { item ->
|
||||
when(item.itemId) {
|
||||
R.id.group_hy -> {
|
||||
return@setOnMenuItemClickListener false
|
||||
}
|
||||
R.id.group_bj -> {
|
||||
return@setOnMenuItemClickListener false
|
||||
}
|
||||
R.id.env_reset ->
|
||||
EnvChangeManager.reset()
|
||||
R.id.hy_product ->
|
||||
EnvChangeManager.changeTo("0734", DebugConfig.NET_MODE_RELEASE)
|
||||
R.id.hy_qa ->
|
||||
EnvChangeManager.changeTo("0734", DebugConfig.NET_MODE_QA)
|
||||
R.id.hy_demo ->
|
||||
EnvChangeManager.changeTo("0734", DebugConfig.NET_MODE_DEMO)
|
||||
R.id.bj_product ->
|
||||
EnvChangeManager.changeTo("010", DebugConfig.NET_MODE_RELEASE)
|
||||
R.id.bj_qa ->
|
||||
EnvChangeManager.changeTo("010", DebugConfig.NET_MODE_QA)
|
||||
R.id.bj_demo ->
|
||||
EnvChangeManager.changeTo("010", DebugConfig.NET_MODE_DEMO)
|
||||
else ->
|
||||
throw AssertionError("invalid item: $item")
|
||||
}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import chassis.Chassis;
|
||||
import mogo.telematics.pad.MessagePad;
|
||||
import mogo_msg.MogoReportMsg;
|
||||
import system_master.SystemStatusInfo;
|
||||
|
||||
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS_P;
|
||||
|
||||
@@ -142,13 +143,17 @@ public class SteeringWheelView extends ConstraintLayout {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotSNRequest() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private final IMoGoAutopilotVehicleStateListener mIMoGoAutopilotVehicleStateListener = new IMoGoAutopilotVehicleStateListener() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -63,6 +63,7 @@ import java.util.Map;
|
||||
|
||||
import mogo.telematics.pad.MessagePad;
|
||||
import mogo_msg.MogoReportMsg;
|
||||
import system_master.SystemStatusInfo;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
@@ -396,6 +397,11 @@ public class MainActivity extends MvpActivity<MainView, MainPresenter> implement
|
||||
public void onAutopilotSNRequest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {
|
||||
|
||||
}
|
||||
|
||||
private void updateConnectInfoView(@NonNull AutopilotStatusInfo autoPilotStatusInfo) {
|
||||
if (!isFloatingLayerHidden) {// 遮罩层显示的时候
|
||||
mConnAdapter.updateData(autoPilotStatusInfo);
|
||||
|
||||
|
After Width: | Height: | Size: 8.3 KiB |
@@ -109,16 +109,26 @@
|
||||
|
||||
<!--问题反馈-->
|
||||
<ImageView
|
||||
android:id="@+id/vsBadCaseToolsView"
|
||||
android:layout_width="@dimen/module_hmi_check_size"
|
||||
android:layout_height="@dimen/module_hmi_check_size"
|
||||
android:layout_marginStart="25px"
|
||||
android:layout_marginBottom="40px"
|
||||
android:background="@drawable/icon_car_ap_badcase_entrance"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/ivBadCaseTools"
|
||||
android:layout_width="120px"
|
||||
android:layout_height="120px"
|
||||
android:src="@drawable/bad_case_selector"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivToolsIcon"
|
||||
app:layout_goneMarginStart="50px" />
|
||||
app:layout_constraintStart_toEndOf="@id/ivToolsIcon"
|
||||
android:layout_marginStart="50px"
|
||||
android:layout_marginBottom="40px"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAiCollectTools"
|
||||
android:layout_width="120px"
|
||||
android:layout_height="120px"
|
||||
android:src="@drawable/ai_collect_selector"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/ivBadCaseTools"
|
||||
android:layout_marginStart="50px"
|
||||
android:layout_marginBottom="40px"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:id="@+id/viewUpgradeTips"
|
||||
|
||||
@@ -1036,6 +1036,40 @@
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/llChangeEnv"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/dp_10"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:background="@drawable/debug_setting_edit_bg"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCurEnv"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#000"
|
||||
android:gravity="start"
|
||||
android:layout_marginStart="@dimen/dp_10"
|
||||
android:textSize="@dimen/dp_24"
|
||||
android:layout_weight="1"
|
||||
tools:text="当前环境:"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btChangeEnv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/dp_8"
|
||||
android:layout_marginEnd="@dimen/dp_10"
|
||||
android:text="切换环境"
|
||||
android:textSize="@dimen/dp_24"
|
||||
android:textColor="#1A1A1A"/>
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnBrakeThreshold"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -1046,8 +1080,8 @@
|
||||
android:paddingEnd="@dimen/dp_20"
|
||||
android:text="设置刹车加速度阈值"
|
||||
android:textSize="@dimen/dp_24"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toBottomOf="@id/llChangeEnv"
|
||||
app:layout_constraintLeft_toLeftOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="0dp"
|
||||
@@ -1132,12 +1166,9 @@
|
||||
android:textOff="开启异常上报提示"
|
||||
android:textOn="关闭异常上报提示"
|
||||
android:textSize="@dimen/dp_24"
|
||||
app:layout_constraintTop_toBottomOf="@id/btnConnectServerIp"
|
||||
/>
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/btnConnectServerIp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/tbHmiController"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 Google Inc.
|
||||
|
||||
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.
|
||||
-->
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/group_hy"
|
||||
android:title="衡阳">
|
||||
<menu>
|
||||
<item android:id="@+id/hy_product" android:title="生产环境"/>
|
||||
<item android:id="@+id/hy_qa" android:title="测试环境"/>
|
||||
<item android:id="@+id/hy_demo" android:title="演示环境"/>
|
||||
</menu>
|
||||
</item>
|
||||
<item android:id="@+id/group_bj"
|
||||
android:title="北京">
|
||||
<menu>
|
||||
<item android:id="@+id/bj_product" android:title="生产环境"/>
|
||||
<item android:id="@+id/bj_qa" android:title="测试环境"/>
|
||||
<item android:id="@+id/bj_demo" android:title="演示环境"/>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<item android:id="@+id/env_reset" android:title="重置" />
|
||||
</menu>
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<string name="check_vehicle_detection">车辆检测</string>
|
||||
<string name="debug_panel">调试面板</string>
|
||||
<string name="debug_panel_fb">反馈</string>
|
||||
<string name="debug_panel_fb">录包设置</string>
|
||||
<string name="check_vehicle_speed_setting">车速设置</string>
|
||||
<string name="check_system_operation">系统运行</string>
|
||||
<string name="check_system_shut_down">关机</string>
|
||||
|
||||
@@ -3,9 +3,13 @@ package com.mogo.eagle.core.function.map
|
||||
import com.mogo.eagle.core.data.config.FunctionBuildConfig
|
||||
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotPointCloudListener
|
||||
import com.mogo.eagle.core.function.api.base.IMoGoSubscriber
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotPointCloudListenerManager
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
|
||||
import com.zhidaoauto.map.sdk.open.business.PointCloudHelper
|
||||
import mogo.telematics.pad.MessagePad
|
||||
import rule_segement.MogoPointCloudOuterClass
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* 订阅点云数据
|
||||
@@ -56,9 +60,9 @@ class MapPointCloudSubscriber private constructor() : IMoGoSubscriber, IMoGoAuto
|
||||
* @param isStrong 是否加粗显示
|
||||
* @return 是否执行
|
||||
*/
|
||||
Logger.d(TAG, "====开始传入地图点云数据====")
|
||||
//Logger.d(TAG, "====开始传入地图点云数据====")
|
||||
val result = PointCloudHelper.updatePointCloudDataByPb(pointCloud, false, true, true)
|
||||
Logger.d(TAG, "====结束传入地图点云数据=====$result")
|
||||
//Logger.d(TAG, "====结束传入地图点云数据=====$result")
|
||||
} else {
|
||||
if (isDrawPointCloud) {
|
||||
Logger.d(TAG, "====停止点云绘制====")
|
||||
@@ -67,4 +71,16 @@ class MapPointCloudSubscriber private constructor() : IMoGoSubscriber, IMoGoAuto
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAutopilotPointCloudDataUpdate(header: MessagePad.Header?, pointCloud: MogoPointCloudOuterClass.MogoPointCloud?) {
|
||||
// Logger.d(TAG, "数据对比:"
|
||||
// + "\n自车定位:自车的 satelliteTime==${getPrettyNumber(CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().satelliteTime.toString())} 经纬度:${CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().locationLat},${CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().locationLon}"
|
||||
// + "\n点云数据:点云的 header?.timestamp==${getPrettyNumber(header?.timestamp.toString())} 经纬度:${pointCloud?.selfLatitude},${pointCloud?.selfLongitude} 点云数量:addDataCount==${pointCloud?.addDataCount} delDataCount===${pointCloud?.delDataCount} "
|
||||
// )
|
||||
}
|
||||
|
||||
fun getPrettyNumber(number: String): String {
|
||||
return BigDecimal.valueOf(number.toDouble())
|
||||
.stripTrailingZeros().toPlainString()
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
@@ -25,6 +26,7 @@ import java.util.List;
|
||||
|
||||
import mogo.telematics.pad.MessagePad;
|
||||
import mogo_msg.MogoReportMsg;
|
||||
import system_master.SystemStatusInfo;
|
||||
|
||||
/**
|
||||
* @author donghongyu
|
||||
@@ -176,4 +178,9 @@ public class SmallMapFragment extends BaseFragment
|
||||
clearPolyline();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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测试数据
|
||||
*/
|
||||
|
||||