[状态栏]状态栏需求代码提交
[状态栏]网络可访问状态获取
@@ -148,6 +148,11 @@ class MoGoAdasListenerImpl : OnAdasListener {
|
||||
CallerAutopilotVehicleStateListenerManager.invokeAutopilotSteeringData(vehicleState.steering)
|
||||
//挂挡档位数据
|
||||
CallerAutopilotVehicleStateListenerManager.invokeAutopilotGearData(vehicleState.gear)
|
||||
} else {
|
||||
/**
|
||||
* 底盘没有返回数据
|
||||
*/
|
||||
CallerAutopilotVehicleStateListenerManager.invokeAutopilotNotData(header.timestamp.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,4 +149,8 @@ public class MoGoHandAdasMsgManager implements
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotNotData(long timestamp) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.zhjt.mogo_core_function_devatools">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
</manifest>
|
||||
@@ -13,6 +13,7 @@ import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
|
||||
import com.zhjt.mogo_core_function_devatools.feedback.FeedbackManager
|
||||
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchManager
|
||||
import com.zhjt.mogo_core_function_devatools.scene.SceneManager.Companion.sceneManager
|
||||
import com.zhjt.mogo_core_function_devatools.status.*
|
||||
import com.zhjt.mogo_core_function_devatools.trace.TraceManager.Companion.traceManager
|
||||
import com.zhjt.mogo_core_function_devatools.upgrade.UpgradeManager.Companion.upgradeManager
|
||||
import record_cache.RecordPanelOuterClass
|
||||
@@ -97,4 +98,7 @@ class DevaToolsProvider : IDevaToolsProvider {
|
||||
upgradeManager.downLoadPackage(mContext!!, downloadKey,downloadUrl)
|
||||
}
|
||||
|
||||
override fun showStatusBar(ctx: Context) {
|
||||
StatusManager.init(ctx)
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import com.zhjt.mogo_core_function_devatools.badcase.repository.net.api.entity.B
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.biz.BadCasePresenter
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.biz.BadCaseView
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.repository.db.entity.AutoPilotRecord
|
||||
import com.zhjt.mogo_core_function_devatools.ext.pop
|
||||
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
|
||||
@@ -263,7 +263,7 @@ internal object BadCaseManager : LifecycleEventObserver {
|
||||
val activity = viewHolder?.get()?.context as? FragmentActivity ?: throw IllegalStateException("please ensure context is FragmentActivity.")
|
||||
BadCaseView(activity).also { itx ->
|
||||
itx.register(record, onDismiss, onSelect)
|
||||
activity.pop(itx, 960.PX, WindowManager.LayoutParams.MATCH_PARENT, key = "BadCaseFloatWindow_").also {
|
||||
activity.enqueuePop(itx, 960.PX, WindowManager.LayoutParams.MATCH_PARENT, key = "BadCaseFloatWindow_").also {
|
||||
hideFloat = it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.PopupWindow.INPUT_METHOD_NEEDED
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.*
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.mogo.eagle.core.utilcode.kotlin.PX
|
||||
@@ -26,7 +27,6 @@ import com.mogo.eagle.core.utilcode.kotlin.shape
|
||||
import com.mogo.eagle.core.utilcode.reminder.Reminder
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.impl.PopupWindowReminder
|
||||
import com.mogo.eagle.core.utilcode.util.BarUtils
|
||||
import com.mogo.eagle.core.utilcode.util.WindowUtils
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -67,7 +67,7 @@ internal fun Context.toast(text: CharSequence, duration: Long = 2, unit: TimeUni
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
internal fun Context.pop(content: View, width: Int, height: Int, key: String, onOuterViewClicked:((focus: View) -> Unit)? = null): () -> Unit {
|
||||
fun Context.enqueuePop(content: View, width: Int, height: Int, key: String = "", startX: Int = 0, startY: Int = 0, gravity: Int = Gravity.START, onOuterViewClicked:((focus: View) -> Unit)? = null, isOverride: Boolean = false, isFocusable: Boolean = true): () -> Unit {
|
||||
val activity = (this as? FragmentActivity) ?: throw IllegalStateException("please use Activity to trigger pop show.")
|
||||
val isImmersiveMode = BarUtils.isImmersiveMode(activity)
|
||||
var tempReminder: PopupWindowReminder? = null
|
||||
@@ -75,11 +75,11 @@ internal fun Context.pop(content: View, width: Int, height: Int, key: String, on
|
||||
val pop = PopupWindow(width, height).also {
|
||||
it.isOutsideTouchable = true
|
||||
it.isTouchable = true
|
||||
it.isFocusable = true
|
||||
it.isFocusable = isFocusable
|
||||
it.inputMethodMode = INPUT_METHOD_NEEDED
|
||||
it.isClippingEnabled = false
|
||||
var startX = 0f
|
||||
var startY = 0f
|
||||
var x = 0f
|
||||
var y = 0f
|
||||
val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop
|
||||
var isClicked = false
|
||||
it.setTouchInterceptor { v, event ->
|
||||
@@ -91,12 +91,12 @@ internal fun Context.pop(content: View, width: Int, height: Int, key: String, on
|
||||
|
||||
when(event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
startX = event.x
|
||||
startY = event.y
|
||||
x = event.x
|
||||
y = event.y
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX = event.x - startX
|
||||
val deltaY = event.y - startY
|
||||
val deltaX = event.x - x
|
||||
val deltaY = event.y - y
|
||||
isClicked = !(abs(deltaX) > touchSlop || abs(deltaY) > touchSlop)
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
@@ -115,7 +115,7 @@ internal fun Context.pop(content: View, width: Int, height: Int, key: String, on
|
||||
it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||
val transition = Slide(Gravity.START).also {
|
||||
val transition = Slide(gravity).also {
|
||||
it.interpolator = AccelerateDecelerateInterpolator()
|
||||
it.duration = 200
|
||||
}
|
||||
@@ -130,9 +130,9 @@ internal fun Context.pop(content: View, width: Int, height: Int, key: String, on
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
pop.showAtLocation(activity.window.decorView, Gravity.START, 0, if (isImmersiveMode) 0 else BarUtils.getStatusBarHeight())
|
||||
pop.showAtLocation(activity.window.decorView, gravity, startX, if (isImmersiveMode) 0 else BarUtils.getStatusBarHeight() + startY)
|
||||
}
|
||||
override fun isOverride(): Boolean = false
|
||||
override fun isOverride(): Boolean = isOverride
|
||||
}
|
||||
tempReminder = reminder
|
||||
Reminder.enqueue(activity.lifeCycleOwner, reminder)
|
||||
@@ -142,6 +142,94 @@ internal fun Context.pop(content: View, width: Int, height: Int, key: String, on
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
fun Context.normalPop(content: View, width: Int, height: Int, key: String = "", startX: Int = 0, startY: Int = 0, gravity: Int = Gravity.START, onOuterViewClicked:((focus: View) -> Unit)? = null, isFocusable: Boolean = true): () -> Unit {
|
||||
val activity = (this as? FragmentActivity) ?: throw IllegalStateException("please use Activity to trigger pop show.")
|
||||
val isImmersiveMode = BarUtils.isImmersiveMode(activity)
|
||||
var tempPop: PopupWindow? = null
|
||||
activity.lifecycleScope.launchWhenResumed {
|
||||
val pop = PopupWindow(width, height).also {
|
||||
tempPop = it
|
||||
it.isOutsideTouchable = true
|
||||
it.isTouchable = true
|
||||
it.isFocusable = isFocusable
|
||||
it.inputMethodMode = INPUT_METHOD_NEEDED
|
||||
it.isClippingEnabled = false
|
||||
var x = 0f
|
||||
var y = 0f
|
||||
val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop
|
||||
var isClicked = false
|
||||
it.setTouchInterceptor { v, event ->
|
||||
val out = Rect()
|
||||
v.getGlobalVisibleRect(out)
|
||||
if (out.contains(event.x.toInt(), event.y.toInt()) && isVisibleOnPoint(content, event.x.toInt(), event.y.toInt())) {
|
||||
return@setTouchInterceptor false
|
||||
}
|
||||
|
||||
when(event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
x = event.x
|
||||
y = event.y
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX = event.x - x
|
||||
val deltaY = event.y - y
|
||||
isClicked = !(abs(deltaX) > touchSlop || abs(deltaY) > touchSlop)
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (isClicked) {
|
||||
isClicked = false
|
||||
onOuterViewClicked?.invoke(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
return@setTouchInterceptor true
|
||||
} finally {
|
||||
activity.window.decorView.dispatchTouchEvent(event)
|
||||
}
|
||||
}
|
||||
it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||
val transition = Slide(gravity).also {
|
||||
it.interpolator = AccelerateDecelerateInterpolator()
|
||||
it.duration = 200
|
||||
}
|
||||
pop.enterTransition = transition
|
||||
pop.exitTransition = transition
|
||||
}
|
||||
pop.contentView = content
|
||||
pop.showAtLocation(activity.window.decorView, gravity, startX, if (isImmersiveMode) 0 else BarUtils.getStatusBarHeight() + startY)
|
||||
}
|
||||
return {
|
||||
tempPop?.takeIf { it.isShowing }?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
fun isVisibleOnPoint(content: View, x: Int, y: Int): Boolean {
|
||||
if (content is ViewGroup) {
|
||||
for (i in 0 until content.childCount) {
|
||||
val child = content.getChildAt(i)
|
||||
val out = Rect()
|
||||
child.getGlobalVisibleRect(out)
|
||||
if (out.contains(x, y) && !child.isVisible) {
|
||||
return false
|
||||
}
|
||||
val ret = isVisibleOnPoint(child, x, y)
|
||||
if (!ret) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
val out = Rect()
|
||||
content.getGlobalVisibleRect(out)
|
||||
return !(out.contains(x, y) && !content.isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun FragmentActivity.softKeyboardHeightChanged(block: ((height: Int) -> Unit)): ()-> Unit {
|
||||
val decor = window.decorView
|
||||
var softKeyboardDisplayed = false
|
||||
@@ -25,7 +25,7 @@ import com.zhjt.mogo_core_function_devatools.R
|
||||
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.badcase.toRecord
|
||||
import com.zhjt.mogo_core_function_devatools.ext.pop
|
||||
import com.zhjt.mogo_core_function_devatools.ext.enqueuePop
|
||||
import com.zhjt.mogo_core_function_devatools.ext.toast
|
||||
import com.zhjt.mogo_core_function_devatools.feedback.biz.FeedBackView
|
||||
import com.zhjt.mogo_core_function_devatools.feedback.biz.bean.Feedback.BadCase
|
||||
@@ -46,7 +46,7 @@ internal object FeedbackManager {
|
||||
|
||||
fun showFeedbackWindow(ctx: Context) {
|
||||
val activity = ctx as? FragmentActivity ?: throw IllegalStateException("please ensure context is FragmentActivity.")
|
||||
activity.pop(FeedBackView(ctx).also { itx ->
|
||||
activity.enqueuePop(FeedBackView(ctx).also { itx ->
|
||||
itx.registerCallback(object : IFeedbackCallback {
|
||||
override fun onClose(v: View) {
|
||||
KeyboardUtils.hideSoftInput(v)
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import androidx.core.view.*
|
||||
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.utilcode.kotlin.*
|
||||
import com.zhjt.mogo_core_function_devatools.ext.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.CanStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.GpsStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.IpcStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.NetStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.RTKStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.Status
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.TracingStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.model.StatusModel
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.can.CanImpl
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.gps.GpsImpl
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.ipc.IpcImpl
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.nets.NetsImpl
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.rtk.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.rtk.RTKImpl
|
||||
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.*
|
||||
|
||||
object StatusManager {
|
||||
|
||||
private lateinit var model: StatusModel
|
||||
|
||||
private var hidePop: (() -> Unit)? = null
|
||||
|
||||
private val flows: ArrayList<IFlow<out Status>> by lazy {
|
||||
ArrayList()
|
||||
}
|
||||
|
||||
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)
|
||||
ctx.lifeCycleOwner.lifecycle.addObserver(object : LifecycleEventObserver {
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Event) {
|
||||
if (event == ON_CREATE) {
|
||||
onCreate(ctx)
|
||||
}
|
||||
if (event == ON_DESTROY) {
|
||||
onDestroy(ctx)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun onCreate(ctx: Context) {
|
||||
val values = model.status.value?.second ?: throw IllegalStateException("state is not right.")
|
||||
values.map {
|
||||
when (it) {
|
||||
is CanStatus -> CanImpl(ctx)
|
||||
is IpcStatus -> IpcImpl(ctx)
|
||||
is NetStatus -> NetsImpl(ctx)
|
||||
is GpsStatus -> GpsImpl(ctx)
|
||||
is TracingStatus -> TracingImpl(ctx)
|
||||
is RTKStatus -> RTKImpl(ctx)
|
||||
}
|
||||
}.also { flows += it }
|
||||
for (f in flows) {
|
||||
ctx.lifeCycleScope.launch(Dispatchers.Default) {
|
||||
f.asFlow().collect {
|
||||
model.update(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
hidePop?.invoke()
|
||||
val content = StatusView(model, ctx)
|
||||
content.doOnAttach {
|
||||
Log.d("OOOO", "--- onAttach -----")
|
||||
for (f in flows) {
|
||||
f.onCreate()
|
||||
}
|
||||
}
|
||||
|
||||
ctx.normalPop(content,
|
||||
width = 665.PX,
|
||||
height = WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
gravity = Gravity.END,
|
||||
startX = 55.PX,
|
||||
isFocusable = false).also { hidePop = it }
|
||||
}
|
||||
|
||||
private fun onDestroy(ctx: Context) {
|
||||
flows.clear()
|
||||
hidePop?.invoke()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.entity
|
||||
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.TracingStatus.Tracing
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.TracingStatus.Tracing.*
|
||||
|
||||
internal sealed class Status
|
||||
|
||||
/**
|
||||
* 工控机
|
||||
*/
|
||||
internal class IpcStatus(val enabled: Boolean = false): Status() {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as IpcStatus
|
||||
if (enabled != other.enabled) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return enabled.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "IpcStatus(enabled=$enabled)"
|
||||
}
|
||||
}
|
||||
|
||||
internal class NetStatus(val enabled: Boolean = false, val name: String? = null, val speed: Speed? = null): Status() {
|
||||
class Speed(val tx: Int, val rx: Int) {
|
||||
|
||||
override fun toString(): String {
|
||||
return "Speed(tx=$tx, rx=$rx)"
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as NetStatus
|
||||
if (enabled != other.enabled) return false
|
||||
if (name != other.name) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = enabled.hashCode()
|
||||
result = 31 * result + (name?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "NetStatus(enabled=$enabled, name=$name, speed=$speed)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* android系统定位状态
|
||||
*/
|
||||
internal class GpsStatus(val enabled: Boolean = false, val isGranted: Boolean = false): Status() {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as GpsStatus
|
||||
if (enabled != other.enabled) return false
|
||||
if (isGranted != other.isGranted) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = enabled.hashCode()
|
||||
result = 31 * result + isGranted.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "GpsStatus(enabled=$enabled, isGranted=$isGranted)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RTK/GNSS定位状态
|
||||
*/
|
||||
internal class RTKStatus(val enabled: Boolean = false): Status() {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as RTKStatus
|
||||
if (enabled != other.enabled) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return enabled.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can总线
|
||||
*/
|
||||
internal class CanStatus(val enabled: Boolean = false): Status() {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as CanStatus
|
||||
if (enabled != other.enabled) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return enabled.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "CanStatus(enabled=$enabled)"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 寻迹/算路/未知
|
||||
*/
|
||||
internal class TracingStatus(val state: Tracing): Status() {
|
||||
|
||||
enum class Tracing(val code: String? = "") {
|
||||
/**
|
||||
* 寻迹-已找到轨迹文件
|
||||
*/
|
||||
TRACK_FINDED("IMAP_TRA_EXIST"),
|
||||
|
||||
/**
|
||||
* 寻迹-轨迹文件加载成功
|
||||
*/
|
||||
TRACK_LOADED("IMAP_TRA_LOADED"),
|
||||
|
||||
/**
|
||||
* 寻迹-没有找到轨迹文件
|
||||
*/
|
||||
TRACK_NOT_EXIST("EMAP_TRA_NOT_EXIST"),
|
||||
|
||||
/**
|
||||
* 寻迹-找到了轨迹文件,加载失败了
|
||||
*/
|
||||
TRACK_LOAD_FAIL("EMAP_TRA_LOAD_FAILED"),
|
||||
|
||||
/**
|
||||
* 算路加载成功
|
||||
*/
|
||||
ROUTE_LOADED("IMAP_TRA_ROUTING"),
|
||||
|
||||
/**
|
||||
* 算路失败
|
||||
*/
|
||||
ROUTE_FAILED("EMAP_HADMAP_ENGINE_NO_ROUTING_INFO"),
|
||||
|
||||
UNKNOWN;
|
||||
|
||||
fun isException(): Boolean = this == TRACK_FINDED || this == TRACK_NOT_EXIST || this == TRACK_LOAD_FAIL || this == ROUTE_FAILED || this == UNKNOWN
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "TracingStatus(state=$state)"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TracingStatus
|
||||
|
||||
if (state != other.state) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return state.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun String.toState(): Tracing {
|
||||
return when(this) {
|
||||
"IMAP_TRA_EXIST" -> TRACK_FINDED
|
||||
"IMAP_TRA_LOADED" -> TRACK_LOADED
|
||||
"EMAP_TRA_NOT_EXIST" -> TRACK_NOT_EXIST
|
||||
"EMAP_TRA_LOAD_FAILED" -> TRACK_LOAD_FAIL
|
||||
"IMAP_TRA_ROUTING" -> ROUTE_LOADED
|
||||
"EMAP_HADMAP_ENGINE_NO_ROUTING_INFO" -> ROUTE_FAILED
|
||||
else -> UNKNOWN
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.flow
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import androidx.annotation.*
|
||||
import com.mogo.eagle.core.utilcode.kotlin.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.Status
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.*
|
||||
import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlin.OptIn
|
||||
import kotlin.coroutines.*
|
||||
|
||||
internal abstract class IFlow< T : Status>(val ctx: Context) : CoroutineScope {
|
||||
|
||||
private val chl: Channel<T> by lazy {
|
||||
Channel(CONFLATED)
|
||||
}
|
||||
|
||||
@Volatile
|
||||
private var old: T? = null
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = ctx.lifeCycleScope.coroutineContext
|
||||
|
||||
abstract fun onCreate()
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun asFlow(): Flow<T> = channelFlow {
|
||||
val it = chl.iterator()
|
||||
while (it.hasNext()) {
|
||||
send(it.next())
|
||||
}
|
||||
}
|
||||
|
||||
fun send(t: T) {
|
||||
if (old == t) {
|
||||
return
|
||||
}
|
||||
old = t
|
||||
launch {
|
||||
chl.send(t)
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
open fun onDestroy() {
|
||||
chl.safeCancel()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.flow.can
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import chassis.Chassis.GearPosition
|
||||
import chassis.Chassis.LightSwitch
|
||||
import com.mogo.eagle.core.data.autopilot.*
|
||||
import com.mogo.eagle.core.function.api.autopilot.*
|
||||
import com.mogo.eagle.core.function.call.autopilot.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.CanStatus
|
||||
|
||||
internal class CanImpl(ctx: Context): IFlow<CanStatus>(ctx), IMoGoAutopilotVehicleStateListener, IMoGoAutopilotStatusListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "CanImpl"
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
Log.d(TAG, "-- onCreate --")
|
||||
send(CanStatus(false))
|
||||
CallerAutopilotVehicleStateListenerManager.addListener(TAG, this)
|
||||
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
|
||||
}
|
||||
|
||||
override fun onAutopilotStatusResponse(autoPilotStatusInfo: AutopilotStatusInfo) {
|
||||
super.onAutopilotStatusResponse(autoPilotStatusInfo)
|
||||
if (!CallerAutoPilotManager.isConnected()) {
|
||||
send(CanStatus(false))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAutopilotLightSwitchData(lightSwitch: LightSwitch?) {
|
||||
send(CanStatus(true))
|
||||
}
|
||||
|
||||
override fun onAutopilotBrakeLightData(brakeLight: Boolean) {
|
||||
send(CanStatus(true))
|
||||
}
|
||||
|
||||
override fun onAutopilotSteeringData(steering: Float) {
|
||||
send(CanStatus(true))
|
||||
}
|
||||
|
||||
override fun onAutopilotGearData(gear: GearPosition) {
|
||||
send(CanStatus(true))
|
||||
}
|
||||
|
||||
override fun onAutopilotNotData(timestamp: Long) {
|
||||
send(CanStatus(false))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d(TAG, "-- onDestroy --")
|
||||
CallerAutoPilotStatusListenerManager.removeListener(TAG)
|
||||
CallerAutoPilotStatusListenerManager.removeListener(TAG)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.flow.gps
|
||||
|
||||
import android.*
|
||||
import android.content.*
|
||||
import android.location.*
|
||||
import android.util.*
|
||||
import androidx.core.location.*
|
||||
import com.mogo.eagle.core.utilcode.util.*
|
||||
import com.mogo.eagle.core.utilcode.util.PermissionUtils.SimpleCallback
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.GpsStatus
|
||||
import java.util.concurrent.atomic.*
|
||||
|
||||
internal class GpsImpl(ctx: Context): IFlow<GpsStatus>(ctx) {
|
||||
|
||||
companion object {
|
||||
const val TAG = "RTKImpl"
|
||||
}
|
||||
|
||||
private val registered = AtomicBoolean(false)
|
||||
|
||||
private var receiver: CheckLocationReceiver? = null
|
||||
|
||||
inner class CheckLocationReceiver(private val onClose: ()->Unit, private val onOpen:()-> Unit) : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action == LocationManager.MODE_CHANGED_ACTION) {
|
||||
if (isLocationEnabled()) {
|
||||
onOpen.invoke()
|
||||
} else {
|
||||
onClose.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val onClose = {
|
||||
send(false, PermissionUtils.isGranted(Manifest.permission.ACCESS_FINE_LOCATION))
|
||||
}
|
||||
|
||||
private val onOpen = {
|
||||
send(true, PermissionUtils.isGranted(Manifest.permission.ACCESS_FINE_LOCATION))
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
val isGranted = PermissionUtils.isGranted(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
send(isLocationEnabled(), isGranted)
|
||||
if (!isGranted) {
|
||||
PermissionUtils.requestAccessFineLocation(object : SimpleCallback {
|
||||
override fun onGranted() {
|
||||
Log.d(TAG,"权限: ${Manifest.permission.ACCESS_FINE_LOCATION} 被授予了....")
|
||||
send(enabled = isLocationEnabled(), isGranted = false)
|
||||
}
|
||||
|
||||
override fun onDenied() {
|
||||
ToastUtils.showShort("定位权限被拒绝, 可能导致RTK定位不准确,请开启~")
|
||||
send(enabled = isLocationEnabled(), isGranted = false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (registered.compareAndSet(false, true)) {
|
||||
ctx.registerReceiver(CheckLocationReceiver(onClose, onOpen).also { receiver = it }, IntentFilter().also { it.addAction(LocationManager.MODE_CHANGED_ACTION) })
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLocationEnabled() =
|
||||
LocationManagerCompat.isLocationEnabled(ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager)
|
||||
|
||||
private fun send(enabled: Boolean, isGranted: Boolean) {
|
||||
send(GpsStatus(enabled, isGranted))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
try {
|
||||
if (registered.compareAndSet(true, false) && receiver != null) {
|
||||
ctx.unregisterReceiver(receiver)
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.flow.ipc
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import com.mogo.eagle.core.data.autopilot.*
|
||||
import com.mogo.eagle.core.function.api.autopilot.*
|
||||
import com.mogo.eagle.core.function.call.autopilot.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.IpcStatus
|
||||
|
||||
internal class IpcImpl(ctx: Context): IFlow<IpcStatus>(ctx), IMoGoAutopilotStatusListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "IpcImpl"
|
||||
}
|
||||
|
||||
private var state: Int = -1
|
||||
|
||||
override fun onCreate() {
|
||||
Log.d(TAG, "-- onCreate --")
|
||||
checkAndSend()
|
||||
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
|
||||
}
|
||||
|
||||
|
||||
override fun onAutopilotStatusResponse(autoPilotStatusInfo: AutopilotStatusInfo) {
|
||||
super.onAutopilotStatusResponse(autoPilotStatusInfo)
|
||||
Log.d(TAG, "-- onAutopilotStatusResponse -- state: ${autoPilotStatusInfo.state}")
|
||||
if (state != autoPilotStatusInfo.state) {
|
||||
checkAndSend()
|
||||
state = autoPilotStatusInfo.state
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAndSend() {
|
||||
Log.d(TAG, "-- checkAndSend --")
|
||||
send(IpcStatus(CallerAutoPilotManager.isConnected()))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d(TAG, "-- onDestroy --")
|
||||
CallerAutoPilotStatusListenerManager.removeListener(TAG)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.flow.nets
|
||||
|
||||
import android.content.*
|
||||
import android.net.*
|
||||
import android.net.wifi.*
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Build.VERSION_CODES
|
||||
import android.util.*
|
||||
import com.mogo.eagle.core.utilcode.kotlin.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.NetStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.NetStatus.Speed
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.concurrent.atomic.*
|
||||
|
||||
internal class NetsImpl(ctx: Context): IFlow<NetStatus>(ctx) {
|
||||
|
||||
companion object {
|
||||
const val TAG = "NetsImpl"
|
||||
}
|
||||
|
||||
private val registered = AtomicBoolean(false)
|
||||
|
||||
private val connectMgr by lazy {
|
||||
ctx.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
}
|
||||
|
||||
private val wifiMgr by lazy {
|
||||
ctx.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||
}
|
||||
|
||||
private val cb = object : ConnectivityManager.NetworkCallback() {
|
||||
|
||||
override fun onAvailable(network: Network) {
|
||||
super.onAvailable(network)
|
||||
Log.d(TAG, "-- onAvailable --:: $network")
|
||||
checkAndSend()
|
||||
}
|
||||
|
||||
override fun onLosing(network: Network, maxMsToLive: Int) {
|
||||
super.onLosing(network, maxMsToLive)
|
||||
Log.d(TAG, "-- onLosing --:: $network::$maxMsToLive")
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
super.onLost(network)
|
||||
Log.d(TAG, "-- onLost --:: $network")
|
||||
checkAndSend()
|
||||
}
|
||||
|
||||
override fun onUnavailable() {
|
||||
super.onUnavailable()
|
||||
Log.d(TAG, "-- onUnavailable --")
|
||||
checkAndSend()
|
||||
}
|
||||
|
||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||
super.onCapabilitiesChanged(network, networkCapabilities)
|
||||
Log.d(TAG, "-- onCapabilitiesChanged --:$network::$networkCapabilities")
|
||||
checkAndSend()
|
||||
}
|
||||
|
||||
override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
|
||||
super.onLinkPropertiesChanged(network, linkProperties)
|
||||
Log.d(TAG, "-- onLinkPropertiesChanged --:$network::$linkProperties")
|
||||
}
|
||||
|
||||
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
|
||||
super.onBlockedStatusChanged(network, blocked)
|
||||
Log.d(TAG, "-- onBlockedStatusChanged --:$network::$blocked")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate() {
|
||||
Log.d(TAG, "-- onCreate --")
|
||||
checkAndSend()
|
||||
val builder = NetworkRequest.Builder()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.O) {
|
||||
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
|
||||
}
|
||||
if (registered.compareAndSet(false, true)) {
|
||||
connectMgr.registerNetworkCallback(builder.build(), cb)
|
||||
}
|
||||
}
|
||||
|
||||
private var loopCheckAndSendJob: Job? = null
|
||||
|
||||
private fun checkAndSend() {
|
||||
val connectionInfo = wifiMgr.connectionInfo
|
||||
val enabled = isNetConnected()
|
||||
val name = connectionInfo.ssid?.replace(Regex("[\\W]"), "")
|
||||
loopCheckAndSendJob?.safeCancel()
|
||||
launch(Dispatchers.Default) { delay(1000); checkAndSend() }.also { loopCheckAndSendJob = it }
|
||||
var tr = 0
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
|
||||
tr = connectionInfo.txLinkSpeedMbps
|
||||
}
|
||||
var sr = 0
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
|
||||
sr = connectionInfo.rxLinkSpeedMbps
|
||||
}
|
||||
val speed = Speed(tr, sr)
|
||||
Log.d(TAG, "checkAndSend----:enable: $enabled :: name: $name")
|
||||
send(enabled, name, speed)
|
||||
}
|
||||
|
||||
|
||||
private fun isNetConnected(): Boolean =
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||
val networkCapabilities = connectMgr.getNetworkCapabilities(connectMgr.activeNetwork)
|
||||
networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false
|
||||
} else {
|
||||
val activeNetworkInfo = connectMgr.activeNetworkInfo
|
||||
activeNetworkInfo != null && activeNetworkInfo.isConnected
|
||||
}
|
||||
|
||||
private fun send(enabled: Boolean, name: String?, speed: Speed? = null) {
|
||||
send(NetStatus(enabled, name, speed))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d(TAG, "-- onDestroy --")
|
||||
if (registered.compareAndSet(true, false)) {
|
||||
connectMgr.unregisterNetworkCallback(cb)
|
||||
}
|
||||
loopCheckAndSendJob?.safeCancel()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.flow.rtk
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import com.mogo.eagle.core.function.api.autopilot.*
|
||||
import com.mogo.eagle.core.function.call.autopilot.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.RTKStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import mogo.telematics.pad.MessagePad.GnssInfo
|
||||
|
||||
internal class RTKImpl(ctx: Context): IFlow<RTKStatus>(ctx), IMoGoAutopilotCarStateListener {
|
||||
companion object {
|
||||
const val TAG = "RTKImpl"
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
send(RTKStatus(isRTKEnabled()))
|
||||
Log.d(TAG, "-- onCreate --")
|
||||
CallerAutopilotCarStatusListenerManager.addListener(TAG, this)
|
||||
|
||||
}
|
||||
|
||||
private fun isRTKEnabled() =
|
||||
CallerAutoPilotManager.isConnected() && CallerAutoPilotStatusListenerManager.getAutoPilotReportMessageCode() != "EHW_RTK" && CallerAutopilotCarStatusListenerManager.getCurrentGnssInfo() != null
|
||||
|
||||
override fun onAutopilotCarStateData(gnssInfo: GnssInfo?) {
|
||||
send(RTKStatus(isRTKEnabled()))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d(TAG, "-- onDestroy --")
|
||||
CallerAutopilotCarStatusListenerManager.removeListener(TAG)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.flow.trace
|
||||
|
||||
import android.content.*
|
||||
import android.util.*
|
||||
import com.mogo.eagle.core.function.api.autopilot.*
|
||||
import com.mogo.eagle.core.function.call.autopilot.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.TracingStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.flow.IFlow
|
||||
import mogo_msg.MogoReportMsg.MogoReportMessage
|
||||
|
||||
internal class TracingImpl(ctx: Context): IFlow<TracingStatus>(ctx), IMoGoAutopilotStatusListener {
|
||||
|
||||
companion object {
|
||||
const val TAG = "TracingImpl"
|
||||
}
|
||||
|
||||
private var old: String = ""
|
||||
|
||||
override fun onCreate() {
|
||||
Log.d(TAG, "-- onCreate --")
|
||||
val autoPilotReportMessageCode = CallerAutoPilotStatusListenerManager.getAutoPilotReportMessageCode()
|
||||
old = autoPilotReportMessageCode
|
||||
send(TracingStatus(autoPilotReportMessageCode.toState()))
|
||||
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
|
||||
}
|
||||
|
||||
|
||||
override fun onAutopilotGuardian(guardianInfo: MogoReportMessage?) {
|
||||
super.onAutopilotGuardian(guardianInfo)
|
||||
val current = guardianInfo?.code
|
||||
if (current != null && current != old) {
|
||||
Log.d(TAG, "-- onAutopilotGuardian --")
|
||||
send(TracingStatus(current.toState()))
|
||||
old = current
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d(TAG, "-- onDestroy --")
|
||||
CallerAutoPilotStatusListenerManager.removeListener(TAG)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.model
|
||||
|
||||
import androidx.lifecycle.*
|
||||
import com.mogo.eagle.core.function.call.autopilot.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.CanStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.GpsStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.IpcStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.NetStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.RTKStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.Status
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.TracingStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.TracingStatus.Tracing.UNKNOWN
|
||||
import java.util.concurrent.atomic.*
|
||||
import kotlin.Pair
|
||||
|
||||
internal class StatusModel : ViewModel() {
|
||||
|
||||
companion object {
|
||||
|
||||
const val TAG = "StatusModel"
|
||||
val DEFAULTS = Pair(null, ArrayList<Status>().also {
|
||||
it += IpcStatus(CallerAutoPilotManager.isConnected())
|
||||
it += NetStatus(false)
|
||||
it += CanStatus(false)
|
||||
it += TracingStatus(UNKNOWN)
|
||||
it += RTKStatus(false)
|
||||
it += GpsStatus(enabled = false, isGranted = false)
|
||||
})
|
||||
}
|
||||
|
||||
val status: MutableLiveData<Pair<Status?, ArrayList<Status>>> = MutableLiveData()
|
||||
|
||||
private val preValue by lazy { AtomicReference<Pair<Status?, ArrayList<Status>>>() }
|
||||
|
||||
|
||||
init {
|
||||
status.value = DEFAULTS
|
||||
}
|
||||
|
||||
fun update(s: Status) {
|
||||
synchronized(preValue) {
|
||||
val v = preValue.get()?.second ?: status.value?.second ?: throw IllegalStateException("live data can't be null.")
|
||||
val nv = ArrayList(v).also { it.updateOrInsert(s) }
|
||||
val data = Pair(getExceptionStatus(nv), nv)
|
||||
preValue.set(data)
|
||||
status.postValue(data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getExceptionStatus(l: ArrayList<Status>): Status? {
|
||||
var ret: Status? = null
|
||||
for (s in l) {
|
||||
ret = when(s) {
|
||||
is IpcStatus -> {
|
||||
if (!s.enabled) {
|
||||
s
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
is CanStatus -> {
|
||||
if (CallerAutoPilotManager.isConnected() && !s.enabled) {
|
||||
s
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
is NetStatus -> {
|
||||
if (!s.enabled) {
|
||||
s
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
is GpsStatus -> {
|
||||
if (!s.enabled) {
|
||||
s
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
is TracingStatus -> {
|
||||
if (CallerAutoPilotManager.isConnected() && s.state.isException()) {
|
||||
s
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
is RTKStatus -> {
|
||||
if (!s.enabled) {
|
||||
s
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != null) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun ArrayList<Status>.updateOrInsert(s: Status) {
|
||||
val index = indexOfFirst { it.javaClass == s.javaClass }
|
||||
if (index < 0) {
|
||||
add(s)
|
||||
} else {
|
||||
set(index, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.ui
|
||||
|
||||
import android.annotation.*
|
||||
import android.content.*
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.*
|
||||
import android.transition.*
|
||||
import android.util.*
|
||||
import android.view.*
|
||||
import androidx.appcompat.widget.*
|
||||
import androidx.constraintlayout.widget.*
|
||||
import androidx.core.content.*
|
||||
import androidx.lifecycle.*
|
||||
import androidx.recyclerview.widget.*
|
||||
import com.mogo.eagle.core.utilcode.kotlin.*
|
||||
import com.mogo.eagle.core.utilcode.rv.divider.*
|
||||
import com.zhjt.mogo_core_function_devatools.R
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.Status
|
||||
import com.zhjt.mogo_core_function_devatools.status.model.StatusModel
|
||||
import com.zhjt.mogo_core_function_devatools.status.ui.adapter.StatusAdapter
|
||||
import com.zhjt.mogo_core_function_devatools.status.ui.diff.StatusDiffCallback
|
||||
import java.util.ArrayList
|
||||
import kotlin.Pair
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
internal class StatusView(private val model: StatusModel, ctx: Context): ConstraintLayout(ctx) {
|
||||
|
||||
companion object {
|
||||
const val TAG = "StatusView"
|
||||
}
|
||||
|
||||
private val iv: AppCompatImageView
|
||||
private val rv: RecyclerView
|
||||
private val dot: View
|
||||
|
||||
private var observer: Observer<Pair<Status?, ArrayList<Status>>>? = null
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.layout_status_bar, this, true)
|
||||
iv = findViewById(R.id.iv)
|
||||
rv = findViewById(R.id.rv)
|
||||
dot = findViewById(R.id.dot)
|
||||
init()
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
isClickable = true
|
||||
dot.background = shape(solid = Color.RED, shape = GradientDrawable.OVAL, width = 30.PX, height = 30.PX)
|
||||
iv.also {
|
||||
it.background = ContextCompat.getDrawable(context, R.drawable.icon_dev_status_un_fold)
|
||||
it.onClick { v ->
|
||||
val expand = v.tag as? Boolean ?: false
|
||||
animate(expand)
|
||||
v.tag = !expand
|
||||
}
|
||||
}
|
||||
rv.also { itx ->
|
||||
itx.itemAnimator?.run {
|
||||
changeDuration = 0
|
||||
addDuration = 0
|
||||
moveDuration = 0
|
||||
removeDuration = 0
|
||||
}
|
||||
itx.layoutManager = GridLayoutManager(context, 3, GridLayoutManager.VERTICAL, false)
|
||||
itx.background = shape(solid = Color.parseColor("#80000000"), radius = 40.PX)
|
||||
itx.addItemDecoration(
|
||||
CommonDividerItemDecoration.Builder()
|
||||
.horizontalExternalSpace(66.PX)
|
||||
.verticalExternalSpace(60.PX)
|
||||
.spanCountTBCare(false)
|
||||
.verticalInnerSpace(50.PX)
|
||||
.horizontalInnerSpace(116.PX)
|
||||
.build()
|
||||
)
|
||||
val adapter = model.status.value?.let { data -> StatusAdapter(context, data.second) }?.also { adapter -> itx.adapter = adapter }
|
||||
adapter?.let { _ ->
|
||||
model.status.observeForever(Observer<Pair<Status?, ArrayList<Status>>> { data ->
|
||||
Log.d("OOOO", "--- observeForever -----")
|
||||
|
||||
if (data.first != null) {
|
||||
dot.visibility = View.VISIBLE
|
||||
} else {
|
||||
dot.visibility = View.INVISIBLE
|
||||
}
|
||||
val old = adapter.data
|
||||
Log.d(TAG, "receive update --new - data: ${data.second.joinToString(",")}")
|
||||
Log.d(TAG, "receive update --old - data: ${old.joinToString(",")}")
|
||||
val result = DiffUtil.calculateDiff(StatusDiffCallback(old, data.second))
|
||||
adapter.data = data.second
|
||||
result.dispatchUpdatesTo(adapter)
|
||||
}.also { observer = it })
|
||||
}
|
||||
}
|
||||
|
||||
onDetach {
|
||||
observer?.also { model.status.removeObserver(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun animate(expand: Boolean) {
|
||||
iv.animate().rotation(if (expand) 0f else -180f).setDuration(200).start()
|
||||
TransitionManager.beginDelayedTransition(this, AutoTransition().setDuration(200))
|
||||
rv.visibility = if (expand) View.VISIBLE else View.INVISIBLE
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.ui.adapter
|
||||
|
||||
import android.content.*
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import androidx.core.content.*
|
||||
import androidx.recyclerview.widget.*
|
||||
import com.zhjt.mogo_core_function_devatools.*
|
||||
import com.zhjt.mogo_core_function_devatools.R.drawable
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.IpcStatus
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.Status
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.TracingStatus.Tracing.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.ui.adapter.StatusAdapter.StatusViewHolder
|
||||
|
||||
internal class StatusAdapter(val ctx: Context, var data: ArrayList<Status>): RecyclerView.Adapter<StatusViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatusViewHolder =
|
||||
StatusViewHolder(LayoutInflater.from(ctx).inflate(R.layout.layout_status_bar_item, parent, false))
|
||||
|
||||
override fun onBindViewHolder(holder: StatusViewHolder, position: Int) {
|
||||
holder.bind(data[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
internal class StatusViewHolder(item: View) : RecyclerView.ViewHolder(item) {
|
||||
|
||||
private val iv: ImageView by lazy {
|
||||
itemView.findViewById(R.id.iv)
|
||||
}
|
||||
|
||||
private val tv: TextView by lazy {
|
||||
itemView.findViewById(R.id.tv)
|
||||
}
|
||||
|
||||
fun bind(status: Status) {
|
||||
when(status) {
|
||||
is IpcStatus -> {
|
||||
if (status.enabled) {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_ipc_enable)
|
||||
} else {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_ipc_disable)
|
||||
}
|
||||
tv.text = "工控机"
|
||||
}
|
||||
is CanStatus -> {
|
||||
if (status.enabled) {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_can_enable)
|
||||
} else {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_can_disable)
|
||||
}
|
||||
tv.text = "Can"
|
||||
}
|
||||
is NetStatus -> {
|
||||
if (status.enabled) {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_net_enable)
|
||||
tv.text = status.name ?: "Wi-Fi"
|
||||
} else {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_net_disable)
|
||||
tv.text = "Wi-Fi"
|
||||
}
|
||||
}
|
||||
is GpsStatus -> {
|
||||
if (status.enabled && status.isGranted) {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_gps_enable)
|
||||
} else {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_gps_disable)
|
||||
}
|
||||
tv.text = "Gps"
|
||||
}
|
||||
is TracingStatus -> {
|
||||
when(status.state) {
|
||||
ROUTE_FAILED, TRACK_LOAD_FAIL, TRACK_NOT_EXIST, TRACK_FINDED, UNKNOWN -> {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_trace_unkown)
|
||||
tv.text = "未知"
|
||||
}
|
||||
TRACK_LOADED -> {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_track_enable)
|
||||
tv.text = "T"
|
||||
}
|
||||
ROUTE_LOADED -> {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_route_enable)
|
||||
tv.text = "V"
|
||||
}
|
||||
}
|
||||
}
|
||||
is RTKStatus -> {
|
||||
if (status.enabled) {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_rtk_enable)
|
||||
} else {
|
||||
iv.background = ContextCompat.getDrawable(itemView.context, drawable.icon_dev_status_rtk_disable)
|
||||
}
|
||||
tv.text = "RTK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.zhjt.mogo_core_function_devatools.status.ui.diff
|
||||
|
||||
import androidx.recyclerview.widget.*
|
||||
import com.zhjt.mogo_core_function_devatools.status.entity.Status
|
||||
|
||||
internal class StatusDiffCallback(private val old: ArrayList<Status>, private val update: ArrayList<Status>): DiffUtil.Callback() {
|
||||
|
||||
override fun getOldListSize(): Int = old.size
|
||||
|
||||
override fun getNewListSize(): Int = update.size
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
val oldStatus = old[oldItemPosition]
|
||||
val updateStatus = update[newItemPosition]
|
||||
return oldStatus == updateStatus
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,37 @@
|
||||
<?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_height="wrap_content"
|
||||
tools:layout_width="match_parent"
|
||||
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="468px"
|
||||
app:layout_constraintBottom_toTopOf="@+id/iv"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/iv"
|
||||
android:layout_width="102px"
|
||||
android:layout_height="102px"
|
||||
android:layout_marginTop="30px"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/rv"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:background="@drawable/icon_dev_status_un_fold"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/dot"
|
||||
android:layout_width="30px"
|
||||
android:layout_height="30px"
|
||||
app:layout_constraintEnd_toEndOf="@+id/iv"
|
||||
app:layout_constraintTop_toTopOf="@+id/iv"
|
||||
android:visibility="invisible"/>
|
||||
</merge>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="100px"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal">
|
||||
<ImageView
|
||||
android:id="@+id/iv"
|
||||
android:layout_width="96px"
|
||||
android:layout_height="96px" />
|
||||
<TextView
|
||||
android:id="@+id/tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20px"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="24px" />
|
||||
</LinearLayout>
|
||||
@@ -334,4 +334,7 @@ public class DispatchAutoPilotManager implements IMogoOnMessageListener<Dispatch
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotNotData(long timestamp) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,10 @@ class MoGoHmiFragment : MvpFragment<MoGoHmiContract.View?, HmiPresenter?>(),
|
||||
setProxyTrafficLightView(viewTrafficLightVr)
|
||||
setProxyLimitingSpeedView(viewLimitingVelocity)
|
||||
setProxyNotificationView(V2XNotificationView(view.context))
|
||||
|
||||
context?.also {
|
||||
CallerDevaToolsManager.showStatusBar(it)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
@@ -1779,6 +1779,10 @@ class DebugSettingView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAutopilotNotData(timestamp: Long) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 吐司提示
|
||||
*/
|
||||
|
||||
@@ -101,6 +101,10 @@ public class SteeringWheelView extends ConstraintLayout {
|
||||
}
|
||||
|
||||
private final IMoGoAutopilotStatusListener mGoAutopilotStatusListener = new IMoGoAutopilotStatusListener() {
|
||||
@Override
|
||||
public void onAutopilotNotData(long timestamp) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotArriveAtStation(@org.jetbrains.annotations.Nullable MessagePad.ArrivalNotification arrivalNotification) {
|
||||
|
||||
@@ -146,6 +150,11 @@ public class SteeringWheelView extends ConstraintLayout {
|
||||
};
|
||||
|
||||
private final IMoGoAutopilotVehicleStateListener mIMoGoAutopilotVehicleStateListener = new IMoGoAutopilotVehicleStateListener() {
|
||||
@Override
|
||||
public void onAutopilotNotData(long timestamp) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 车辆转向灯
|
||||
* @param lightSwitch
|
||||
|
||||
@@ -75,6 +75,10 @@ public class MainActivity extends MvpActivity<MainView, MainPresenter> implement
|
||||
FragmentStackTransactionListener,
|
||||
IMoGoAutopilotStatusListener {
|
||||
|
||||
@Override
|
||||
public void onAutopilotNotData(long timestamp) {
|
||||
}
|
||||
|
||||
protected static final String TAG = "MainActivity";
|
||||
private static final int REQUEST_CODE_DIALOG = 100;
|
||||
|
||||
@@ -473,4 +477,8 @@ public class MainActivity extends MvpActivity<MainView, MainPresenter> implement
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointerCaptureChanged(boolean hasCapture) {
|
||||
super.onPointerCaptureChanged(hasCapture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@ public class SmallMapFragment extends BaseFragment
|
||||
SmpServiceManager.init(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotNotData(long timestamp) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews(Bundle savedInstanceState) {
|
||||
super.initViews(savedInstanceState);
|
||||
|
||||