[状态栏]状态栏需求代码提交

[状态栏]网络可访问状态获取
This commit is contained in:
renwj
2022-05-26 20:38:51 +08:00
parent 8c23dbe411
commit 9150b3f40a
57 changed files with 1422 additions and 25 deletions

View File

@@ -288,6 +288,10 @@ public class BusPassengerModel {
private final IMoGoAutopilotStatusListener mGoAutopilotStatusListener = new IMoGoAutopilotStatusListener(){
@Override
public void onAutopilotNotData(long timestamp) {
}
@Override
public void onAutopilotGuardian(@Nullable MogoReportMsg.MogoReportMessage guardianInfo) {

View File

@@ -277,4 +277,8 @@ public class BusPresenter extends Presenter<BusFragment>
public void onAutopilotGuardian(MogoReportMsg.MogoReportMessage guardianInfo) {
}
@Override
public void onAutopilotNotData(long timestamp) {
}
}

View File

@@ -482,6 +482,11 @@ public class TaxiPassengerModel implements IOCHTaxiPassengerNaviChangedCallback
private final IMoGoAutopilotStatusListener mGoAutopilotStatusListener = new IMoGoAutopilotStatusListener(){
@Override
public void onAutopilotNotData(long timestamp) {
}
@Override
public void onAutopilotGuardian(@Nullable MogoReportMsg.MogoReportMessage guardianInfo) {

View File

@@ -1042,6 +1042,10 @@ public class TaxiModel {
private final IMoGoAutopilotStatusListener mGoAutopilotStatusListener = new IMoGoAutopilotStatusListener() {
@Override
public void onAutopilotNotData(long timestamp) {
}
@Override
public void onAutopilotGuardian(@Nullable MogoReportMsg.MogoReportMessage guardianInfo) {

View File

@@ -148,6 +148,11 @@ class MoGoAdasListenerImpl : OnAdasListener {
CallerAutopilotVehicleStateListenerManager.invokeAutopilotSteeringData(vehicleState.steering)
//挂挡档位数据
CallerAutopilotVehicleStateListenerManager.invokeAutopilotGearData(vehicleState.gear)
} else {
/**
* 底盘没有返回数据
*/
CallerAutopilotVehicleStateListenerManager.invokeAutopilotNotData(header.timestamp.toLong())
}
}

View File

@@ -149,4 +149,8 @@ public class MoGoHandAdasMsgManager implements
}
@Override
public void onAutopilotNotData(long timestamp) {
}
}

View File

@@ -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>

View File

@@ -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)
}
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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()
}
}

View File

@@ -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
}
}

View File

@@ -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()
}
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}

View File

@@ -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"
}
}
}
}
}

View File

@@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -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>

View File

@@ -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>

View File

@@ -334,4 +334,7 @@ public class DispatchAutoPilotManager implements IMogoOnMessageListener<Dispatch
}
@Override
public void onAutopilotNotData(long timestamp) {
}
}

View File

@@ -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)

View File

@@ -1779,6 +1779,10 @@ class DebugSettingView @JvmOverloads constructor(
}
}
override fun onAutopilotNotData(timestamp: Long) {
}
/**
* 吐司提示
*/

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -35,6 +35,11 @@ interface IMoGoAutopilotStatusListener {
*/
fun onAutopilotGuardian(guardianInfo: MogoReportMsg.MogoReportMessage?) {}
/**
* 工控机底盘没有返回数据
*/
fun onAutopilotNotData(timestamp: Long) {}
companion object {
/**

View File

@@ -30,4 +30,9 @@ interface IMoGoAutopilotVehicleStateListener {
* @param gear 档位
*/
fun onAutopilotGearData(gear: Chassis.GearPosition)
/**
* 底盘没有返回数据
*/
fun onAutopilotNotData(timestamp: Long)
}

View File

@@ -99,4 +99,9 @@ interface IDevaToolsProvider : IProvider {
* 下载指定包
*/
fun downLoadPackage(downloadKey: String, downloadUrl: String)
/**
* 展示状态栏
*/
fun showStatusBar(ctx: Context)
}

View File

@@ -4,6 +4,7 @@ import androidx.annotation.Nullable
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.base.CallerBase
import com.mogo.eagle.core.utilcode.kotlin.*
import com.mogo.eagle.core.utilcode.util.GsonUtils
import mogo.telematics.pad.MessagePad
import mogo_msg.MogoReportMsg
@@ -23,6 +24,9 @@ object CallerAutoPilotStatusListenerManager : CallerBase() {
private val M_AUTOPILOT_STATUS_LISTENERS: ConcurrentHashMap<String, IMoGoAutopilotStatusListener> =
ConcurrentHashMap()
@Volatile
private var autoPilotMessageCode: String = ""
/**
* 查询AutoPilot状态
*/
@@ -37,6 +41,8 @@ object CallerAutoPilotStatusListenerManager : CallerBase() {
return GsonUtils.toJson(mAutopilotStatusInfo)
}
fun getAutoPilotReportMessageCode(): String = autoPilotMessageCode
/**
* 通过Gnss定位更新来同步更新自动驾驶状态
*/
@@ -163,6 +169,7 @@ object CallerAutoPilotStatusListenerManager : CallerBase() {
M_AUTOPILOT_STATUS_LISTENERS.forEach {
val tag = it.key
val listener = it.value
autoPilotMessageCode = guardianInfo?.code ?: ""
listener.onAutopilotGuardian(guardianInfo)
}
}

View File

@@ -17,6 +17,8 @@ object CallerAutopilotCarStatusListenerManager : CallerBase() {
private val M_AUTOPILOT_STATUS_LISTENERS: ConcurrentHashMap<String, IMoGoAutopilotCarStateListener> =
ConcurrentHashMap()
@Volatile
private var gnssInfo: MessagePad.GnssInfo? = null
/**
* 添加 ADAS车辆状态&定位 监听
@@ -56,12 +58,18 @@ object CallerAutopilotCarStatusListenerManager : CallerBase() {
}
}
fun getCurrentGnssInfo():MessagePad.GnssInfo? {
return gnssInfo
}
/**
* 车辆状态数据 回调
* @param gnssInfo
*/
@Synchronized
fun invokeAutopilotCarStateData(gnssInfo: MessagePad.GnssInfo?) {
this.gnssInfo = gnssInfo
M_AUTOPILOT_STATUS_LISTENERS.forEach {
val tag = it.key
val listener = it.value

View File

@@ -113,4 +113,14 @@ object CallerAutopilotVehicleStateListenerManager : CallerBase() {
fun getAutopilotTimeStamp(): Long {
return this.timeStamp
}
/**
* 底盘没有返回数据
*/
fun invokeAutopilotNotData(timestamp: Long) {
M_AUTOPILOT_VEHICLE_LISTENERS.forEach{
val listener = it.value
listener.onAutopilotNotData(timestamp)
}
}
}

View File

@@ -136,4 +136,11 @@ object CallerDevaToolsManager {
fun downLoadPackage(downloadKey: String, downloadUrl: String) {
devaToolsProviderApi?.downLoadPackage(downloadKey, downloadUrl)
}
/**
* 展示状态栏
*/
fun showStatusBar(ctx: Context) {
devaToolsProviderApi?.showStatusBar(ctx)
}
}

View File

@@ -8,8 +8,7 @@ import android.graphics.drawable.GradientDrawable
import android.text.*
import android.text.style.ForegroundColorSpan
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
import android.view.*
import android.widget.EditText
import android.widget.TextView
import androidx.annotation.ColorInt
@@ -40,20 +39,38 @@ val <T: View> T.lifecycleOwner: LifecycleOwner
if (it.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
it.currentState = Lifecycle.State.CREATED
}
if (it.currentState.isAtLeast(Lifecycle.State.CREATED)) {
it.currentState = Lifecycle.State.STARTED
}
if (it.currentState.isAtLeast(Lifecycle.State.STARTED)) {
it.currentState = Lifecycle.State.RESUMED
}
}
}
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View?) {
lifecycle.let {
if (it.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
it.currentState = Lifecycle.State.CREATED
}
if (it.currentState.isAtLeast(Lifecycle.State.CREATED)) {
it.currentState = Lifecycle.State.STARTED
}
if (it.currentState.isAtLeast(Lifecycle.State.STARTED)) {
it.currentState = Lifecycle.State.RESUMED
}
}
}
override fun onViewDetachedFromWindow(v: View?) {
lifecycle.let {
if (it.currentState.isAtLeast(Lifecycle.State.CREATED)) {
if (it.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
this@lifecycleOwner.removeOnAttachStateChangeListener(this)
it.currentState = Lifecycle.State.DESTROYED
}
@@ -159,7 +176,6 @@ val Int.PX: Int
val Int.SP: Int
get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this.toFloat(), Utils.getApp().resources.displayMetrics).toInt()
fun <T: EditText> T.watch(@IntRange(from = 1) maxCharCount: Int, onCountChanged:((count: Int) -> Unit)? = null, onTextChanged:((editable: Editable?) -> Unit)? = null, onReachMaxCountAction:((text: T) -> Unit)? = null, onGetFocus:((text: T) -> Unit)? = null) {
doAfterTextChanged { itx ->
onTextChanged?.invoke(itx)

View File

@@ -2,7 +2,7 @@ package com.mogo.eagle.core.utilcode.rv.divider
import android.graphics.Canvas
import android.graphics.Rect
import android.util.Pair
import android.util.*
import android.view.View
import androidx.annotation.IntRange
import androidx.recyclerview.widget.GridLayoutManager
@@ -84,17 +84,23 @@ class CommonDividerItemDecoration internal constructor(builder: Builder) : ItemD
}
} else {
val averageDistance = (((spanCount - 1) * mHorizontalInnerSpace + 2 * mHorizontalExternalSpace + deltaHL + deltaHR) * 1.0f / spanCount).toInt()
Log.d("UUUU", "step::0::$averageDistance: lastRight: $mLastRight : state: $state " )
if (spanIndex == 0) {
outRect.left = mHorizontalExternalSpace + deltaHL
outRect.right = averageDistance - outRect.left
mLastRight = outRect.right
Log.d("UUUU", "step::1::$averageDistance: lastRight: $mLastRight" )
} else if (spanIndex == spanCount - 1) {
outRect.right = mHorizontalExternalSpace + deltaHR
outRect.left = averageDistance - outRect.right
Log.d("UUUU", "step::2::$averageDistance: lastRight: $mLastRight ::outRect.left: ${outRect.left}" )
} else {
outRect.left = mHorizontalInnerSpace - mLastRight
outRect.right = averageDistance - outRect.left
mLastRight = outRect.right
if (state.didStructureChange()) {
mLastRight = outRect.right
}
Log.d("UUUU", "step::2::$averageDistance: lastRight: $mLastRight ::outRect.left: ${outRect.left}" )
}
} //所在的行数
if (lookUp.getSpanGroupIndex(childPosition, spanCount) == lookUp.getSpanGroupIndex(mFromPosition, spanCount)) {
@@ -131,7 +137,9 @@ class CommonDividerItemDecoration internal constructor(builder: Builder) : ItemD
} else {
outRect.top = mVerticalInnerSpace - mLastBottom
outRect.bottom = averageDistance - outRect.top
mLastBottom = outRect.bottom
if (state.didStructureChange()) {
mLastBottom = outRect.bottom
}
}
}
if (lookUp.getSpanGroupIndex(childPosition, spanCount) == lookUp.getSpanGroupIndex(mFromPosition, spanCount)) { //第一列
@@ -222,7 +230,9 @@ class CommonDividerItemDecoration internal constructor(builder: Builder) : ItemD
} else {
outRect.left = mHorizontalInnerSpace - mLastRight
outRect.right = averageDistance - outRect.left
mLastRight = outRect.right
if (state.didStructureChange()) {
mLastRight = outRect.right
}
}
}
if (childPosition == mFromPosition) {
@@ -279,7 +289,9 @@ class CommonDividerItemDecoration internal constructor(builder: Builder) : ItemD
} else {
outRect.top = mVerticalInnerSpace - mLastBottom
outRect.bottom = averageDistance - outRect.top
mLastBottom = outRect.bottom
if (state.didStructureChange()) {
mLastBottom = outRect.bottom
}
}
}
if (childPosition == mFromPosition) {

View File

@@ -1,5 +1,6 @@
package com.mogo.eagle.core.utilcode.util;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
@@ -56,6 +57,7 @@ public final class PermissionUtils {
private static SimpleCallback sSimpleCallback4WriteSettings;
private static SimpleCallback sSimpleCallback4DrawOverlays;
private static SimpleCallback sSimpleCallback4AccessFineLocation;
/**
* Return the permissions used in application.
@@ -152,6 +154,19 @@ public final class PermissionUtils {
PermissionActivityImpl.start(PermissionActivityImpl.TYPE_WRITE_SETTINGS);
}
public static void requestAccessFineLocation(final SimpleCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (isGranted(Manifest.permission.ACCESS_FINE_LOCATION)) {
if (callback != null) {
callback.onGranted();
}
return;
}
sSimpleCallback4AccessFineLocation = callback;
PermissionActivityImpl.start(PermissionActivityImpl.TYPE_ACCESS_FINE_LOCATION);
}
}
@TargetApi(Build.VERSION_CODES.M)
private static void startWriteSettingsActivity(final Activity activity, final int requestCode) {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
@@ -424,6 +439,7 @@ public final class PermissionUtils {
private static final int TYPE_RUNTIME = 0x01;
private static final int TYPE_WRITE_SETTINGS = 0x02;
private static final int TYPE_DRAW_OVERLAYS = 0x03;
private static final int TYPE_ACCESS_FINE_LOCATION = 0x04;
private static int currentRequestCode = -1;
@@ -483,6 +499,9 @@ public final class PermissionUtils {
} else if (type == TYPE_DRAW_OVERLAYS) {
currentRequestCode = TYPE_DRAW_OVERLAYS;
startOverlayPermissionActivity(activity, TYPE_DRAW_OVERLAYS);
} else if (type == TYPE_ACCESS_FINE_LOCATION) {
currentRequestCode = TYPE_ACCESS_FINE_LOCATION;
startOverlayPermissionActivity(activity, TYPE_ACCESS_FINE_LOCATION);
} else {
activity.finish();
Log.e("PermissionUtils", "type is wrong.");
@@ -550,6 +569,15 @@ public final class PermissionUtils {
sSimpleCallback4DrawOverlays.onDenied();
}
sSimpleCallback4DrawOverlays = null;
} else if (requestCode == TYPE_ACCESS_FINE_LOCATION) {
if (sSimpleCallback4AccessFineLocation == null) {
return;
}
if (isGranted(Manifest.permission.ACCESS_FINE_LOCATION)) {
sSimpleCallback4AccessFineLocation.onGranted();
} else {
sSimpleCallback4AccessFineLocation.onDenied();
}
}
}
}

View File

@@ -210,4 +210,7 @@ public class MogoRouteOverlayManager implements
}
@Override
public void onAutopilotNotData(long timestamp) {
}
}