[6.8.0]冷启动三期主页状态条

This commit is contained in:
xuxinchao
2024-11-21 15:12:37 +08:00
parent 4f60cfbdfe
commit cb0d4614c5
168 changed files with 918 additions and 1 deletions

View File

@@ -63,7 +63,7 @@ object ColdStartManager: IMoGoAutopilotStatusListener, IDataCenterBizListener {
//已登录且距离上次冷启动成功超过1分钟再次展示冷启动页面
if(!ColdStartConfig.getShowWindowStatus() && loginStatus && System.currentTimeMillis().minus(ColdStartConfig.getColdStartSuccessTime())>60000){
if(System.currentTimeMillis().minus(ipcConnectedTime) > 60000){
CallerHmiManager.showColdStartWindow()
CallerHmiManager.showColdStartProcessView()
}
}
}else{

View File

@@ -646,4 +646,11 @@ class MoGoHmiProvider : IMoGoHmiProvider {
}
}
/**
* 展示冷启动进度条
*/
override fun showColdStartProcessView() {
CallerHmiViewControlListenerManager.invokeColdStartProcessView()
}
}

View File

@@ -0,0 +1,248 @@
package com.mogo.eagle.core.function.hmi.ui.widget
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.TransitionDrawable
import android.os.CountDownTimer
import android.text.TextUtils
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoColdStartStateListener
import com.mogo.eagle.core.function.api.hmi.view.IViewControlListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerColdStartStateListenerManager
import com.mogo.eagle.core.function.call.hmi.CallerHmiViewControlListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.util.FrameAnimatorContainerUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.zhjt.mogo.adas.data.AdasConstants
import com.zhjt.mogo_core_function_devatools.coldstart.ColdStartConfig
import kotlinx.android.synthetic.main.view_cold_start_process.view.ivColdStartProcess
import system_master.SsmInfo
import system_master.SystemStatusInfo
import java.util.Locale
import java.util.regex.Matcher
import java.util.regex.Pattern
/**
* 冷启动三期
* 冷启动进度条,点击后会打开冷启动详情页面
*/
class ColdStartProcessView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr),IViewControlListener,
IMoGoAutopilotStatusListener, IMoGoColdStartStateListener {
companion object {
const val TAG = "ColdStartProcessView"
const val LOAD_SSM_WAITING_TIME = 180000L //SSM加载超时等待时间
const val COLD_START_WAITING_TIME = 600000L //冷启动超时等待时间
}
private var ipcConnectStatus = false //连接域控状态,默认是未连接
private var ssmConnectStatus = false //SSM连接状态默认是未连接
private var coldStartStatus = false //冷启动状态,默认是未冷启动成功
private var currentColdStartSuccess = false //冷启动成功状态,也是成功后动画完成状态
private var connectSSMTimer: CountDownTimer?= null //连接SSM等待倒计时
private var connectColdStartTimer: CountDownTimer?= null //连接冷启动等待倒计时
private var newColdStart: Boolean = false //SSM是否支持新版冷启动
private var coldStartProcessNormalAnim: FrameAnimatorContainerUtils ?= null
private var coldStartProcessAbnormalAnim: FrameAnimatorContainerUtils ?= null
init {
LayoutInflater.from(context).inflate(R.layout.view_cold_start_process, this, true)
initView()
}
private fun initView(){
coldStartProcessNormalAnim = FrameAnimatorContainerUtils(R.array.cold_start_process_normal,40,ivColdStartProcess)
coldStartProcessAbnormalAnim = FrameAnimatorContainerUtils(R.array.cold_start_process_abnormal,40,ivColdStartProcess)
val transition = TransitionDrawable(
arrayOf(
ContextCompat.getDrawable(context, R.drawable.icon_cold_start_success_01), // 当前图片
ContextCompat.getDrawable(context, R.drawable.icon_cold_start_success_02) // 要渐变到的图片
)
)
ivColdStartProcess.setImageDrawable(transition)
transition.isCrossFadeEnabled = true
var tag = true
val testTimer = object: CountDownTimer(30000,500){
override fun onTick(millisUntilFinished: Long) {
ThreadUtils.runOnUiThread {
tag = if(tag){
transition.startTransition(500)
false
}else{
transition.reverseTransition(500)
true
}
}
}
override fun onFinish() {
ThreadUtils.runOnUiThread {
this@ColdStartProcessView.visibility = View.GONE
}
}
}
ivColdStartProcess.setOnClickListener {
testTimer.start()
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerHmiViewControlListenerManager.addListener(TAG,this)
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
CallerColdStartStateListenerManager.addListener(TAG,this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerHmiViewControlListenerManager.removeListener(TAG)
CallerAutoPilotStatusListenerManager.removeListener(TAG)
CallerColdStartStateListenerManager.removeListener(TAG)
coldStartProcessNormalAnim?.release()
coldStartProcessAbnormalAnim?.release()
}
override fun showColdStartProcessView() {
super.showColdStartProcessView()
if(this.visibility != View.VISIBLE){
this.visibility = View.VISIBLE
}
}
/**
* 域控连接状态
*/
override fun onAutopilotIpcConnectStatusChanged(
status: AdasConstants.IpcConnectionStatus,
reason: String?
) {
if(status == AdasConstants.IpcConnectionStatus.CONNECTED){
//域控连接成功
if(coldStartProcessAbnormalAnim?.isPlaying() == true){
coldStartProcessAbnormalAnim?.stop()
}
coldStartProcessNormalAnim?.start()
}else{
//域控连接失败
if(coldStartProcessNormalAnim?.isPlaying() == true){
coldStartProcessNormalAnim?.stop()
}
coldStartProcessAbnormalAnim?.start()
}
}
/**
* 状态查询应答
* @param status 数据
* HQ、M1 MAP350开始弃用其他车型MAP360开始弃用
*/
override fun onAutopilotStatusRespByQuery(status: SystemStatusInfo.StatusInfo){
ThreadUtils.runOnUiThread{
//SSM连接成功
connectSSMSuccess()
//通过autoPilotReady判断冷启动是否成功
if(status.hasAutoPilotReady() && status.autoPilotReady && !coldStartStatus){
coldStartStatus = true
showColdStartSuccessView()
//记录冷启动成功时间
ColdStartConfig.setColdStartSuccessTime(System.currentTimeMillis())
}
}
}
/**
* 定频SSM接口
* 1hz hq m1 MAP350开始支持其他车型MAP360开始支持
* 定频SSM接入后 onStatusQueryResp 状态查询应答接口将弃用
* @param statusInf 数据
*/
override fun onSystemStatus(statusInf: SsmInfo.SsmStatusInf){
}
/**
* 冷启动状态
*/
override fun onColdStartState(
token: Long,
timestamp: Long,
isQuery: Boolean,
coldStartState: SsmInfo.ColdStartState?
) {
}
private fun connectSSMSuccess(){
}
private fun showColdStartSuccessView(){
}
private val pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+")
/**
* 解析版本 格式 xxx.xxx.xxx(x的数量不固定)
* 仅用于版本比较,不能用于展示
* 例如:
* "12.03.04" 解析结果120304
* "2.11.0" 解析结果21100
* "3.0.0" 解析结果30000
* 目前已用于DockerVersion和MaserVersion的解析
*
* @param isUseAll 是否使用全部截取数据 true表示 12.34.56 截取之后 123456 false表示12.34.56 截取之后 12
* @param ver 版本字符串 例如:"MAP-taxi_RoboTaxi_df_2.8.0.3_20220928_test" 解析结果为280
* @return -1表示解析失败
*/
private fun parseVersion(isUseAll: Boolean, ver: String): Int {
var version = -1
if (!TextUtils.isEmpty(ver)) {
try {
val matcher: Matcher = pattern.matcher(ver)
if (matcher.find()) {
var group = matcher.group()
if (!TextUtils.isEmpty(group)) {
val format = "%02d"
if (isUseAll) {
val temp = group.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
group = String.format(Locale.getDefault(), format, temp[0].toInt())
group += String.format(Locale.getDefault(), format, temp[1].toInt())
group += String.format(Locale.getDefault(), format, temp[2].toInt())
} else {
group = group.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()[0]
}
version = group.toInt()
}
}
} catch (e: Exception) {
Log.e(TAG, "版本解析失败=$ver", e)
}
}
return version
}
}

View File

@@ -0,0 +1,263 @@
package com.mogo.eagle.core.function.hmi.util
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.widget.ImageView
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.ThreadPoolManagerUtils
import java.lang.ref.SoftReference
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.Future
class FrameAnimatorContainerUtils (resId: Int,
fps: Int,
imageView: ImageView,
isOnce: Boolean = false,// 一次性的 true 值播放一次 false 重复播放
initFirstFrame:Boolean = true,
width:Int = -1,
height:Int = -1){
private val TAG = "FrameAnimatorContainerUtils"
private lateinit var mFrames: IntArray // 帧数组
private var mIndex = 0 // 当前帧
private var mShouldRun = false // 开始/停止播放用
private var mIsRunning = false // 动画是否正在播放,防止重复播放
private var mSoftReferenceImageView: SoftReference<ImageView>? = null // 软引用ImageView以便及时释放掉
private var mHandler: Handler? = null
private var mDelayMillis = 0
var mOnAnimationStoppedListener: OnAnimationStoppedListener? = null//播放停止监听
var isOnce:Boolean = false
private val readQueue = ArrayBlockingQueue<Pair<Bitmap,BitmapFactory.Options>>(8,true)
private val writeQueue = ArrayBlockingQueue<Pair<Bitmap,BitmapFactory.Options>>(8,true)
private var currentPoll:Pair<Bitmap,BitmapFactory.Options>?=null
private var decodeImage: Future<*>?=null
init {
createAnimation(imageView, getData(resId), fps,initFirstFrame,width,height)
this.isOnce = isOnce
}
private fun createAnimation(
imageView: ImageView,
frames: IntArray,
fps: Int,
initFirstFrame: Boolean,
width: Int,
height: Int
) {
mHandler = object: Handler(Looper.myLooper()!!){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if(msg.what==0){
val imageView = mSoftReferenceImageView!!.get()
if (!mShouldRun || imageView == null) {
mIsRunning = false
if (mOnAnimationStoppedListener != null) {
mOnAnimationStoppedListener!!.AnimationStopped()
}
return
}
mIsRunning = true
//新开线程去读下一帧
if (imageView.isShown) {
if (!mShouldRun) {
mIsRunning = false
CallerLogger.d(TAG,"暂停播放")
if (mOnAnimationStoppedListener != null) {
mOnAnimationStoppedListener!!.AnimationStopped()
}
return
}
mHandler?.sendEmptyMessageDelayed(0,mDelayMillis.toLong())
if(currentPoll!=null){
writeQueue.offer(currentPoll)
currentPoll = null
}
currentPoll = readQueue.poll()
if(currentPoll!=null){
val bitmap = currentPoll!!.first
imageView.setImageBitmap(bitmap)
}else{
CallerLogger.d(TAG,"加载过慢了")
}
}
}
}
}
mFrames = frames
mIndex = -1
mSoftReferenceImageView = SoftReference(imageView)
mShouldRun = false
mIsRunning = false
mDelayMillis = 1000 / fps //帧动画时间间隔,毫秒
CallerLogger.d(TAG,"两帧时间:${mDelayMillis}")
if(initFirstFrame) {
imageView.setImageResource(mFrames[0])
}
var widthImage = -1
var heightImage = -1
var config = Bitmap.Config.ARGB_8888
if(width>0&&height>0){
widthImage = width
heightImage = height
}else{
try {
val bmp = (imageView.drawable as BitmapDrawable).bitmap
widthImage = bmp.width
heightImage = bmp.height
config = bmp.config
}catch (e:Exception){
throw RuntimeException("请设置图片或传递大小")
}
}
// 当图片大小类型相同时进行复用避免频繁GC
for (i in 0..7) {
val mBitmap = Bitmap.createBitmap(widthImage, heightImage, config)
val mBitmapOptions = BitmapFactory.Options()
//设置Bitmap内存复用
mBitmapOptions.inBitmap = mBitmap //Bitmap复用内存块类似对象池避免不必要的内存分配和回收
mBitmapOptions.inMutable = true //解码时返回可变Bitmap
mBitmapOptions.inSampleSize = 1 //缩放比例
writeQueue.add(Pair(mBitmap,mBitmapOptions))
}
decodeImage = ThreadPoolManagerUtils.getsInstance().submit(object: Runnable{
override fun run() {
while (true) {
val (bitmap1, options) = writeQueue.take()
mIndex++
if (mIndex >= mFrames.size){
mIndex = 0
if(isOnce){
stop()
return
}
}
val index: Int = mIndex
val imageRes: Int = mFrames[index]
var bitmap: Bitmap? = null
try {
bitmap = BitmapFactory.decodeResource(
imageView.resources,
imageRes,
options
)
options.inBitmap = bitmap
} catch (e: Exception) {
e.printStackTrace()
}
if (bitmap != null) {
readQueue.put(Pair(bitmap, options))
}
}
}
})
}
//循环读取下一帧
private val next: Int
get() {
mIndex++
if (mIndex >= mFrames.size){
mIndex = 0
}
return mIndex
}
@Synchronized
fun reStart(){
resetQueue()
mIndex = 0
mIsRunning = false
start()
}
private fun resetQueue(){
val temp = mutableListOf<Pair<Bitmap, BitmapFactory.Options>>()
val writeIterator = writeQueue.iterator()
while (writeIterator.hasNext()) {
temp.add(writeIterator.next())
}
val readIterator = readQueue.iterator()
while (readIterator.hasNext()) {
temp.add(readIterator.next())
}
for (pair in temp) {
writeQueue.add(pair)
}
}
fun release(){
mShouldRun = false
decodeImage?.cancel(true)
}
/**
* 播放动画,同步锁防止多线程读帧时,数据安全问题
*/
@Synchronized
fun start() {
mShouldRun = true
if (mIsRunning) return
mHandler?.removeCallbacksAndMessages(null)
mHandler?.sendEmptyMessage(0)
}
/**
* 停止播放
*/
@Synchronized
fun stop() {
mShouldRun = false
}
fun isPlaying():Boolean{
return mShouldRun
}
/**
* 设置停止播放监听
* @param listener 设置监听
*/
fun setOnAnimStopListener(listener: OnAnimationStoppedListener?) {
mOnAnimationStoppedListener = listener
}
/**
* 从xml中读取帧数组
* @param resId
* @return
*/
fun getData(resId: Int): IntArray {
val array = AbsMogoApplication.getApp().resources.obtainTypedArray(resId)
val len = array.length()
val intArray = IntArray(array.length())
for (i in 0 until len) {
intArray[i] = array.getResourceId(i, 0)
}
array.recycle()
return intArray
}
fun setData(mFrames: IntArray){
this.mFrames = mFrames
}
/**
* 停止播放监听
*/
interface OnAnimationStoppedListener {
fun AnimationStopped()
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Some files were not shown because too many files have changed in this diff Show More