@@ -43,9 +43,20 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation rootProject.ext.dependencies.kotlinstdlibjdk7
|
||||
implementation rootProject.ext.dependencies.androidxccorektx
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
implementation rootProject.ext.dependencies.androidxconstraintlayout
|
||||
implementation rootProject.ext.dependencies.arouter
|
||||
implementation rootProject.ext.dependencies.rxandroid
|
||||
|
||||
kapt rootProject.ext.dependencies.aroutercompiler
|
||||
|
||||
if (Boolean.valueOf(RELEASE)) {
|
||||
|
||||
} else {
|
||||
api project(':services:mogo-service-api')
|
||||
implementation project(':modules:mogo-module-common')
|
||||
|
||||
implementation project(':core:mogo-core-data')
|
||||
implementation project(':core:mogo-core-utils')
|
||||
implementation project(':core:mogo-core-function-api')
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
GROUP=com.mogo.eagle.core
|
||||
POM_ARTIFACT_ID=function-impl
|
||||
POM_ARTIFACT_ID=function-hmi
|
||||
VERSION_CODE=1
|
||||
|
||||
@@ -2,4 +2,30 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.eagle.core.function.hmi">
|
||||
|
||||
<application>
|
||||
|
||||
<receiver android:name="com.mogo.eagle.core.function.hmi.receiver.V2XWarningBroadcastReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="com.hmi.v2x.notification" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="com.mogo.eagle.core.function.hmi.receiver.V2XTrafficLightBroadcastReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="com.hmi.v2x.trafficlight" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="com.mogo.eagle.core.function.hmi.receiver.V2XLimitingVelocityBroadcastReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="com.hmi.v2x.limitingvelocity" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.mogo.eagle.core.function.hmi;
|
||||
|
||||
/**
|
||||
* @author xiaoyuzhou
|
||||
* @date 2021/8/3 4:26 下午
|
||||
*/
|
||||
public class WaringConst {
|
||||
public static String MODULE_NAME = "MODULE_LEFT_PANEL";
|
||||
|
||||
// V2X 弹窗预警
|
||||
// 是否展示:true-展示,false-关闭
|
||||
public static String BROADCAST_V2X_IS_SHOW_EXTRA_KEY = "v2xIsShow";
|
||||
// 预警类型:@EventTypeEnum
|
||||
public static String BROADCAST_V2X_TYPE_EXTRA_KEY = "v2xType";
|
||||
// 预警弹窗文字内容
|
||||
public static String BROADCAST_V2X_ALERT_CONTENT_EXTRA_KEY = "alertContent";
|
||||
// 预警弹窗TTS播报
|
||||
public static String BROADCAST_V2X_TTS_CONTENT_EXTRA_KEY = "ttsContent";
|
||||
// 弹窗标志,一个弹窗绑定一个标志,不可重复
|
||||
public static String BROADCAST_V2X_TAG_EXTRA_KEY = "tag";
|
||||
|
||||
// 交通的灯
|
||||
// 是否展示:true-展示,false-关闭
|
||||
public static String BROADCAST_V2X_TRAFFIC_LIGHT_IS_SHOW__EXTRA_KEY = "trafficLightIsShow";
|
||||
// 选中的交通等类型,red、yellow、green
|
||||
public static String BROADCAST_V2X_TRAFFIC_LIGHT_CHECK_TYPE_EXTRA_KEY = "trafficLightCheckType";
|
||||
|
||||
// 限速标志
|
||||
// 是否展示:true-展示,false-关闭
|
||||
public static String BROADCAST_V2X_LIMITING_VELOCITY_IS_SHOW__EXTRA_KEY = "limitingVelocityIsShow";
|
||||
// 限速的速度
|
||||
public static String BROADCAST_V2X_LIMITING_VELOCITY_SPEED__EXTRA_KEY = "limitingVelocitySpeed";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
|
||||
import com.mogo.eagle.core.function.hmi.notification.interfaces.OnFloatAnimator
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.WarningStatusListener
|
||||
import com.mogo.utils.WindowUtils
|
||||
import com.mogo.utils.logger.Logger
|
||||
|
||||
/**
|
||||
* @author donghongyu
|
||||
* @date 2021/8/3 5:50 下午
|
||||
* 预警通知管理
|
||||
*/
|
||||
class WarningFloat {
|
||||
companion object {
|
||||
var TAG = "WarningNotificationManager"
|
||||
|
||||
@JvmStatic
|
||||
fun with(activity: Context): Builder = Builder(activity)
|
||||
|
||||
|
||||
/**
|
||||
* 关闭当前浮窗
|
||||
* @param tag 浮窗标签
|
||||
* @param force 立即关闭,有退出动画也不执行
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun dismiss(tag: String? = null, force: Boolean = false) =
|
||||
WarningFloatWindowManager.dismiss(tag, force)
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 浮窗的属性构建类,支持链式调用
|
||||
*/
|
||||
class Builder(private val activity: Context) {
|
||||
// 创建浮窗数据类,方便管理配置
|
||||
val config = WarningNotificationConfig()
|
||||
|
||||
/**
|
||||
* 设置浮窗的吸附模式
|
||||
* @param sidePattern 浮窗吸附模式
|
||||
*/
|
||||
fun setSidePattern(sidePattern: SidePattern) = apply { config.sidePattern = sidePattern }
|
||||
|
||||
/**
|
||||
* 设置倒计时关闭
|
||||
* @param countDownTime 倒计时时间
|
||||
*/
|
||||
fun setCountDownTime(countDownTime: Long) = apply { config.countDownTime = countDownTime }
|
||||
|
||||
/**
|
||||
* 设置浮窗的布局文件,以及布局的操作接口
|
||||
* @param layoutId 布局文件的资源Id
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun setLayout(layoutId: Int) = apply {
|
||||
config.layoutId = layoutId
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浮窗的布局视图,以及布局的操作接口
|
||||
* @param layoutView 自定义的布局视图
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun setLayout(layoutView: View) = apply {
|
||||
config.layoutView = layoutView
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浮窗的对齐方式,以及偏移量
|
||||
* @param gravity 对齐方式
|
||||
* @param offsetX 目标坐标的水平偏移量
|
||||
* @param offsetY 目标坐标的竖直偏移量
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun setGravity(gravity: Int, offsetX: Int = 0, offsetY: Int = 0) = apply {
|
||||
config.gravity = gravity
|
||||
config.offsetPair = Pair(offsetX, offsetY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浮窗的起始坐标,优先级高于setGravity
|
||||
* @param x 起始水平坐标
|
||||
* @param y 起始竖直坐标
|
||||
*/
|
||||
fun setLocation(x: Int, y: Int) = apply {
|
||||
config.locationPair = Pair(x, y)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浮窗的拖拽边距值
|
||||
* @param left 浮窗左侧边距
|
||||
* @param top 浮窗顶部边距
|
||||
* @param right 浮窗右侧边距
|
||||
* @param bottom 浮窗底部边距
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun setBorder(
|
||||
left: Int = 0,
|
||||
top: Int = -WindowUtils.getStatusBarHeight(activity),
|
||||
right: Int = WindowUtils.getScreenWidth(activity),
|
||||
bottom: Int = WindowUtils.getScreenHeight(activity)
|
||||
) = apply {
|
||||
config.leftBorder = left
|
||||
config.topBorder = top
|
||||
config.rightBorder = right
|
||||
config.bottomBorder = bottom
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浮窗的标签:只有一个浮窗时,可以不设置;
|
||||
* 有多个浮窗必须设置不容的浮窗,不然没法管理,所以禁止创建相同标签的浮窗
|
||||
* @param floatTag 浮窗标签
|
||||
*/
|
||||
fun setTag(floatTag: String?) = apply {
|
||||
config.floatTag = floatTag
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浮窗是否状态栏沉浸
|
||||
* @param immersionStatusBar 是否状态栏沉浸
|
||||
*/
|
||||
fun setImmersionStatusBar(immersionStatusBar: Boolean) = apply {
|
||||
config.immersionStatusBar = immersionStatusBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置浮窗的出入动画
|
||||
* @param floatAnimator 浮窗的出入动画,为空时不执行动画
|
||||
*/
|
||||
fun setAnimator(floatAnimator: OnFloatAnimator?) =
|
||||
apply { config.floatAnimator = floatAnimator }
|
||||
|
||||
/**
|
||||
* 设置视图状态监听;show,dismiss
|
||||
* @param listener 设置视图状态监听
|
||||
*/
|
||||
fun addWarningStatusListener(listener: WarningStatusListener?) =
|
||||
apply {
|
||||
if (listener != null) {
|
||||
config.statusListenerMap.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建浮窗,包括Activity浮窗和系统浮窗,如若系统浮窗无权限,先进行权限申请
|
||||
*/
|
||||
fun show() = apply {
|
||||
when {
|
||||
// 未设置浮窗布局文件/布局视图,不予创建
|
||||
config.layoutId == null && config.layoutView == null ->
|
||||
Logger.e(TAG, "需要传入 layoutId 或 layoutView ")
|
||||
// 申请浮窗权限
|
||||
else -> WarningFloatWindowManager.create(activity, config)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification
|
||||
|
||||
import android.animation.Animator
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.Rect
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import com.mogo.eagle.core.function.hmi.notification.anim.AnimatorManager
|
||||
import com.mogo.eagle.core.function.hmi.notification.enums.ShowPattern
|
||||
import com.mogo.eagle.core.function.hmi.notification.widget.ParentFrameLayout
|
||||
import com.mogo.utils.WindowUtils
|
||||
import com.mogo.utils.logger.Logger
|
||||
|
||||
/**
|
||||
* @author donghongyu
|
||||
* @date 2021/8/5 5:29 下午
|
||||
*/
|
||||
internal class WarningFloatWindowHelper(
|
||||
val context: Context,
|
||||
var config: WarningNotificationConfig
|
||||
) {
|
||||
|
||||
var TAG = "WarningFloatWindowHelper"
|
||||
|
||||
// 这里采用添加View的方式,配合动画完成弹窗的效果,仅限应用内部弹窗,伴随着Activity后台也会隐藏
|
||||
lateinit var windowManager: WindowManager
|
||||
lateinit var params: WindowManager.LayoutParams
|
||||
var frameLayout: ParentFrameLayout? = null
|
||||
private var enterAnimator: Animator? = null
|
||||
|
||||
private val closeWarningTask: Runnable = Runnable {
|
||||
exitAnim()
|
||||
}
|
||||
|
||||
fun createWindow(): Boolean {
|
||||
return if (context is Activity) {
|
||||
initParams()
|
||||
addView()
|
||||
config.isShow = true
|
||||
true
|
||||
} else {
|
||||
Logger.e(TAG, "activity 必须是 Activity 的实现")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun initParams() {
|
||||
// 获取 WindowManager
|
||||
windowManager = context.getSystemService(Service.WINDOW_SERVICE) as WindowManager
|
||||
params = WindowManager.LayoutParams().apply {
|
||||
// 设置窗口类型为应用子窗口,和PopupWindow同类型
|
||||
type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
|
||||
format = PixelFormat.RGBA_8888
|
||||
gravity = Gravity.START or Gravity.TOP
|
||||
// 设置浮窗以外的触摸事件可以传递给后面的窗口、不自动获取焦点
|
||||
flags = if (config.immersionStatusBar)
|
||||
// 没有边界限制,允许窗口扩展到屏幕外
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
else WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
width = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
height = WindowManager.LayoutParams.WRAP_CONTENT
|
||||
|
||||
// 如若设置了固定坐标,直接定位
|
||||
if (config.locationPair != Pair(0, 0)) {
|
||||
x = config.locationPair.first
|
||||
y = config.locationPair.second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加视图
|
||||
*/
|
||||
private fun addView() {
|
||||
// 创建一个frameLayout作为浮窗布局的父容器,因为需要做动画,以及管理滑动关闭事件
|
||||
frameLayout = ParentFrameLayout(context, config)
|
||||
frameLayout?.tag = config.floatTag
|
||||
// 将浮窗布局文件添加到父容器frameLayout中,并返回该浮窗文件
|
||||
val floatingView = config.layoutView?.also { frameLayout?.addView(it) }
|
||||
?: LayoutInflater.from(context).inflate(config.layoutId!!, frameLayout, true)
|
||||
// 为了避免创建的时候闪一下,我们先隐藏视图,不能直接设置GONE,否则定位会出现问题
|
||||
floatingView.visibility = View.INVISIBLE
|
||||
// 将frameLayout添加到系统windowManager中
|
||||
windowManager.addView(frameLayout, params)
|
||||
|
||||
// 在浮窗绘制完成的时候,设置初始坐标、执行入场动画
|
||||
frameLayout?.layoutListener = object : ParentFrameLayout.OnLayoutListener {
|
||||
override fun onLayout() {
|
||||
setGravity(frameLayout)
|
||||
frameLayout?.postDelayed({
|
||||
config.apply {
|
||||
enterAnim(floatingView)
|
||||
// 设置callbacks
|
||||
layoutView = floatingView
|
||||
callbacks?.createdResult(true, null, floatingView)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置触摸监听,直接关掉弹窗
|
||||
frameLayout?.setOnTouchListener { _, _ ->
|
||||
exitAnim()
|
||||
false
|
||||
}
|
||||
|
||||
// 设置倒计时指定秒数后自动关闭Window
|
||||
if (config.countDownTime > 0) {
|
||||
frameLayout?.postDelayed(closeWarningTask, config.countDownTime)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重置倒计时
|
||||
*/
|
||||
fun resetDownTime() {
|
||||
// 设置倒计时指定秒数后自动关闭Window
|
||||
if (config.countDownTime > 0) {
|
||||
Log.d(TAG, "重置弹窗时常")
|
||||
frameLayout?.removeCallbacks(closeWarningTask)
|
||||
frameLayout?.postDelayed(closeWarningTask, config.countDownTime)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 入场动画
|
||||
*/
|
||||
private fun enterAnim(floatingView: View) {
|
||||
config.statusListenerMap.forEach { listener ->
|
||||
listener.onShow()
|
||||
}
|
||||
if (frameLayout == null || config.isAnim) return
|
||||
enterAnimator = AnimatorManager(frameLayout!!, params, windowManager, config)
|
||||
.enterAnim()?.apply {
|
||||
// 可以延伸到屏幕外,动画结束按需去除该属性,不然旋转屏幕可能置于屏幕外部
|
||||
params.flags =
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
|
||||
addListener(object : Animator.AnimatorListener {
|
||||
override fun onAnimationRepeat(animation: Animator?) {}
|
||||
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
config.isAnim = false
|
||||
if (!config.immersionStatusBar) {
|
||||
// 不需要延伸到屏幕外了,防止屏幕旋转的时候,浮窗处于屏幕外
|
||||
params.flags =
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAnimationCancel(animation: Animator?) {}
|
||||
|
||||
override fun onAnimationStart(animation: Animator?) {
|
||||
floatingView.visibility = View.VISIBLE
|
||||
config.isAnim = true
|
||||
}
|
||||
})
|
||||
start()
|
||||
}
|
||||
if (enterAnimator == null) {
|
||||
floatingView.visibility = View.VISIBLE
|
||||
windowManager.updateViewLayout(floatingView, params)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出动画
|
||||
*/
|
||||
fun exitAnim() {
|
||||
config.statusListenerMap.forEach { listener ->
|
||||
listener.onDismiss()
|
||||
}
|
||||
if (frameLayout == null || (config.isAnim && enterAnimator == null)) return
|
||||
enterAnimator?.cancel()
|
||||
val animator: Animator? =
|
||||
AnimatorManager(frameLayout!!, params, windowManager, config).exitAnim()
|
||||
if (animator == null) remove() else {
|
||||
// 二次判断,防止重复调用引发异常
|
||||
if (config.isAnim) return
|
||||
config.isAnim = true
|
||||
params.flags =
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
animator.addListener(object : Animator.AnimatorListener {
|
||||
override fun onAnimationRepeat(animation: Animator?) {}
|
||||
|
||||
override fun onAnimationEnd(animation: Animator?) = remove()
|
||||
|
||||
override fun onAnimationCancel(animation: Animator?) {}
|
||||
|
||||
override fun onAnimationStart(animation: Animator?) {}
|
||||
})
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出动画执行结束/没有退出动画,进行回调、移除等操作
|
||||
*/
|
||||
fun remove(force: Boolean = false) = try {
|
||||
frameLayout?.removeCallbacks(closeWarningTask)
|
||||
config.isAnim = false
|
||||
WarningFloatWindowManager.remove(config.floatTag)
|
||||
// removeView是异步删除,在Activity销毁的时候会导致窗口泄漏,所以使用removeViewImmediate直接删除view
|
||||
windowManager.run { if (force) removeViewImmediate(frameLayout) else removeView(frameLayout) }
|
||||
} catch (e: Exception) {
|
||||
Logger.e(TAG, "浮窗关闭出现异常:$e")
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置浮窗的对齐方式,支持上下左右、居中、上中、下中、左中和右中,默认左上角
|
||||
* 支持手动设置的偏移量
|
||||
*/
|
||||
@SuppressLint("RtlHardcoded")
|
||||
private fun setGravity(view: View?) {
|
||||
if (config.locationPair != Pair(0, 0) || view == null) return
|
||||
val parentRect = Rect()
|
||||
// 获取浮窗所在的矩形
|
||||
windowManager.defaultDisplay.getRectSize(parentRect)
|
||||
val location = IntArray(2)
|
||||
// 获取在整个屏幕内的绝对坐标
|
||||
view.getLocationOnScreen(location)
|
||||
// 通过绝对高度和相对高度比较,判断包含顶部状态栏
|
||||
val statusBarHeight =
|
||||
if (location[1] > params.y) WindowUtils.getStatusBarHeight(view.context.applicationContext) else 0
|
||||
val parentBottom =
|
||||
WindowUtils.getScreenHeight(view.context.applicationContext) - statusBarHeight
|
||||
when (config.gravity) {
|
||||
// 右上
|
||||
Gravity.END, Gravity.END or Gravity.TOP, Gravity.RIGHT, Gravity.RIGHT or Gravity.TOP ->
|
||||
params.x = parentRect.right - view.width
|
||||
// 左下
|
||||
Gravity.START or Gravity.BOTTOM, Gravity.BOTTOM, Gravity.LEFT or Gravity.BOTTOM ->
|
||||
params.y = parentBottom - view.height
|
||||
// 右下
|
||||
Gravity.END or Gravity.BOTTOM, Gravity.RIGHT or Gravity.BOTTOM -> {
|
||||
params.x = parentRect.right - view.width
|
||||
params.y = parentBottom - view.height
|
||||
}
|
||||
// 居中
|
||||
Gravity.CENTER -> {
|
||||
params.x = (parentRect.right - view.width).shr(1)
|
||||
params.y = (parentBottom - view.height).shr(1)
|
||||
}
|
||||
// 上中
|
||||
Gravity.CENTER_HORIZONTAL, Gravity.TOP or Gravity.CENTER_HORIZONTAL ->
|
||||
params.x = (parentRect.right - view.width).shr(1)
|
||||
// 下中
|
||||
Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL -> {
|
||||
params.x = (parentRect.right - view.width).shr(1)
|
||||
params.y = parentBottom - view.height
|
||||
}
|
||||
// 左中
|
||||
Gravity.CENTER_VERTICAL, Gravity.START or Gravity.CENTER_VERTICAL, Gravity.LEFT or Gravity.CENTER_VERTICAL ->
|
||||
params.y = (parentBottom - view.height).shr(1)
|
||||
// 右中
|
||||
Gravity.END or Gravity.CENTER_VERTICAL, Gravity.RIGHT or Gravity.CENTER_VERTICAL -> {
|
||||
params.x = parentRect.right - view.width
|
||||
params.y = (parentBottom - view.height).shr(1)
|
||||
}
|
||||
// 其他情况,均视为左上
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置偏移量
|
||||
params.x += config.offsetPair.first
|
||||
params.y += config.offsetPair.second
|
||||
|
||||
if (config.immersionStatusBar) {
|
||||
if (config.showPattern != ShowPattern.CURRENT_ACTIVITY) {
|
||||
params.y -= statusBarHeight
|
||||
}
|
||||
} else {
|
||||
if (config.showPattern == ShowPattern.CURRENT_ACTIVITY) {
|
||||
params.y += statusBarHeight
|
||||
}
|
||||
}
|
||||
// 更新浮窗位置信息
|
||||
windowManager.updateViewLayout(view, params)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* @author xiaoyuzhou
|
||||
* @date 2021/8/5 5:17 下午
|
||||
*/
|
||||
internal object WarningFloatWindowManager {
|
||||
private const val TAG = "WarningFloat"
|
||||
private const val DEFAULT_TAG = "default"
|
||||
|
||||
private val windowMap = ConcurrentHashMap<String, WarningFloatWindowHelper>()
|
||||
|
||||
/**
|
||||
* 创建浮窗,tag不存在创建,tag存在创建失败
|
||||
* 创建结果通过tag添加到相应的map进行管理
|
||||
*/
|
||||
fun create(context: Context, config: WarningNotificationConfig) {
|
||||
if (!checkTag(config)) {
|
||||
val helper = WarningFloatWindowHelper(context, config)
|
||||
if (helper.createWindow()) windowMap[config.floatTag!!] = helper
|
||||
} else {
|
||||
Log.w(TAG, "存在相同的tag,延长弹窗时间")
|
||||
// 存在相同的tag,直接创建失败
|
||||
config.callbacks?.createdResult(false, "存在相同的tag,延长弹窗时间", null)
|
||||
windowMap[config.floatTag!!]?.resetDownTime()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭浮窗,执行浮窗的退出动画
|
||||
*/
|
||||
fun dismiss(tag: String? = null, force: Boolean = false) =
|
||||
getHelper(tag)?.run { if (force) remove(force) else exitAnim() }
|
||||
|
||||
/**
|
||||
* 移除当条浮窗信息,在退出完成后调用
|
||||
*/
|
||||
fun remove(floatTag: String?) = windowMap.remove(getTag(floatTag))
|
||||
|
||||
/**
|
||||
* 获取浮窗tag,为空则使用默认值
|
||||
*/
|
||||
private fun getTag(tag: String?) = tag ?: DEFAULT_TAG
|
||||
|
||||
/**
|
||||
* 检测浮窗的tag是否有效,不同的浮窗必须设置不同的tag
|
||||
*/
|
||||
private fun checkTag(config: WarningNotificationConfig): Boolean {
|
||||
// 如果未设置tag,设置默认tag
|
||||
config.floatTag = getTag(config.floatTag)
|
||||
return windowMap.containsKey(config.floatTag!!)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取具体的系统浮窗管理类
|
||||
*/
|
||||
fun getHelper(tag: String?) = windowMap[getTag(tag)]
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification
|
||||
|
||||
import android.view.View
|
||||
import com.mogo.eagle.core.function.hmi.notification.anim.DefaultAnimator
|
||||
import com.mogo.eagle.core.function.hmi.notification.enums.ShowPattern
|
||||
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
|
||||
import com.mogo.eagle.core.function.hmi.notification.interfaces.OnFloatAnimator
|
||||
import com.mogo.eagle.core.function.hmi.notification.interfaces.OnFloatCallbacks
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.WarningStatusListener
|
||||
|
||||
/**
|
||||
* @author xiaoyuzhou
|
||||
* @date 2021/8/5 3:02 下午
|
||||
* 预警通知 配置信息
|
||||
*/
|
||||
data class WarningNotificationConfig(
|
||||
// 浮窗的xml布局文件
|
||||
var layoutId: Int? = null,
|
||||
var layoutView: View? = null,
|
||||
|
||||
// 当前浮窗的tag
|
||||
var floatTag: String? = null,
|
||||
// 是否正在执行动画
|
||||
var isAnim: Boolean = false,
|
||||
// 是否显示
|
||||
var isShow: Boolean = false,
|
||||
// 状态栏沉浸
|
||||
var immersionStatusBar: Boolean = false,
|
||||
|
||||
// 浮窗的吸附方式(默认不吸附,拖到哪里是哪里)
|
||||
var sidePattern: SidePattern = SidePattern.DEFAULT,
|
||||
|
||||
// 浮窗显示类型(默认只在当前页显示)
|
||||
var showPattern: ShowPattern = ShowPattern.CURRENT_ACTIVITY,
|
||||
|
||||
// 倒计时关闭window,-1 表示不自动关闭, 单位毫秒。1s = 1000ms
|
||||
var countDownTime: Long = 0,
|
||||
|
||||
// 浮窗的摆放方式,使用系统的Gravity属性
|
||||
var gravity: Int = 0,
|
||||
// 坐标的偏移量
|
||||
var offsetPair: Pair<Int, Int> = Pair(0, 0),
|
||||
// 固定的初始坐标,左上角坐标
|
||||
var locationPair: Pair<Int, Int> = Pair(0, 0),
|
||||
// ps:优先使用固定坐标,若固定坐标不为原点坐标,gravity属性和offset属性无效
|
||||
|
||||
// 四周边界值
|
||||
var leftBorder: Int = 0,
|
||||
var topBorder: Int = -999,
|
||||
var rightBorder: Int = 9999,
|
||||
var bottomBorder: Int = 9999,
|
||||
|
||||
// 出入动画
|
||||
var floatAnimator: OnFloatAnimator? = DefaultAnimator(),
|
||||
|
||||
// 设置视图状态监听;show,dismiss
|
||||
var statusListenerMap: ArrayList< WarningStatusListener> = ArrayList(),
|
||||
|
||||
// Callbacks
|
||||
var callbacks: OnFloatCallbacks? = null,
|
||||
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification.anim
|
||||
|
||||
import android.animation.Animator
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import com.mogo.eagle.core.function.hmi.notification.WarningNotificationConfig
|
||||
|
||||
/**
|
||||
* @author: donghongyu
|
||||
* @function: App浮窗的出入动画管理类,只需传入具体的动画实现类(策略模式)
|
||||
* @date: 2019-07-22 16:44
|
||||
*/
|
||||
internal class AnimatorManager(
|
||||
private val view: View,
|
||||
private val params: WindowManager.LayoutParams,
|
||||
private val windowManager: WindowManager,
|
||||
private val config: WarningNotificationConfig
|
||||
) {
|
||||
|
||||
fun enterAnim(): Animator? =
|
||||
config.floatAnimator?.enterAnim(view, params, windowManager, config.sidePattern)
|
||||
|
||||
fun exitAnim(): Animator? =
|
||||
config.floatAnimator?.exitAnim(view, params, windowManager, config.sidePattern)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification.anim
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.ValueAnimator
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
|
||||
import com.mogo.eagle.core.function.hmi.notification.interfaces.OnFloatAnimator
|
||||
import com.mogo.utils.WindowUtils
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* @author: donghongyu
|
||||
* @function: 系统浮窗的默认效果,选择靠近左右侧的一边进行出入
|
||||
* @date: 2019-07-22 17:22
|
||||
*/
|
||||
open class DefaultAnimator : OnFloatAnimator {
|
||||
|
||||
override fun enterAnim(
|
||||
view: View,
|
||||
params: WindowManager.LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Animator? = getAnimator(view, params, windowManager, sidePattern, false)
|
||||
|
||||
override fun exitAnim(
|
||||
view: View,
|
||||
params: WindowManager.LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Animator? = getAnimator(view, params, windowManager, sidePattern, true)
|
||||
|
||||
private fun getAnimator(
|
||||
view: View,
|
||||
params: WindowManager.LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern,
|
||||
isExit: Boolean
|
||||
): Animator {
|
||||
val triple = initValue(view, params, windowManager, sidePattern)
|
||||
// 退出动画的起始值、终点值,与入场动画相反
|
||||
val start = if (isExit) triple.second else triple.first
|
||||
val end = if (isExit) triple.first else triple.second
|
||||
return ValueAnimator.ofInt(start, end).apply {
|
||||
addUpdateListener {
|
||||
try {
|
||||
val value = it.animatedValue as Int
|
||||
if (triple.third) params.x = value else params.y = value
|
||||
// 动画执行过程中页面关闭,出现异常
|
||||
windowManager.updateViewLayout(view, params)
|
||||
} catch (e: Exception) {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算边距,起始坐标等
|
||||
*/
|
||||
private fun initValue(
|
||||
view: View,
|
||||
params: WindowManager.LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Triple<Int, Int, Boolean> {
|
||||
val parentRect = Rect()
|
||||
windowManager.defaultDisplay.getRectSize(parentRect)
|
||||
// 浮窗各边到窗口边框的距离
|
||||
val leftDistance = params.x
|
||||
val rightDistance = parentRect.right - (leftDistance + view.right)
|
||||
val topDistance = params.y
|
||||
val bottomDistance = parentRect.bottom - (topDistance + view.bottom)
|
||||
// 水平、垂直方向的距离最小值
|
||||
val minX = min(leftDistance, rightDistance)
|
||||
val minY = min(topDistance, bottomDistance)
|
||||
|
||||
val isHorizontal: Boolean
|
||||
val endValue: Int
|
||||
val startValue: Int = when (sidePattern) {
|
||||
SidePattern.LEFT, SidePattern.RESULT_LEFT -> {
|
||||
// 从左侧到目标位置,右移
|
||||
isHorizontal = true
|
||||
endValue = params.x
|
||||
-view.right
|
||||
}
|
||||
SidePattern.RIGHT, SidePattern.RESULT_RIGHT -> {
|
||||
// 从右侧到目标位置,左移
|
||||
isHorizontal = true
|
||||
endValue = params.x
|
||||
parentRect.right
|
||||
}
|
||||
SidePattern.TOP, SidePattern.RESULT_TOP -> {
|
||||
// 从顶部到目标位置,下移
|
||||
isHorizontal = false
|
||||
endValue = params.y
|
||||
-view.bottom
|
||||
}
|
||||
SidePattern.BOTTOM, SidePattern.RESULT_BOTTOM -> {
|
||||
// 从底部到目标位置,上移
|
||||
isHorizontal = false
|
||||
endValue = params.y
|
||||
parentRect.bottom + getCompensationHeight(view, params)
|
||||
}
|
||||
|
||||
SidePattern.DEFAULT, SidePattern.AUTO_HORIZONTAL, SidePattern.RESULT_HORIZONTAL -> {
|
||||
// 水平位移,哪边距离屏幕近,从哪侧移动
|
||||
isHorizontal = true
|
||||
endValue = params.x
|
||||
if (leftDistance < rightDistance) -view.right else parentRect.right
|
||||
}
|
||||
SidePattern.AUTO_VERTICAL, SidePattern.RESULT_VERTICAL -> {
|
||||
// 垂直位移,哪边距离屏幕近,从哪侧移动
|
||||
isHorizontal = false
|
||||
endValue = params.y
|
||||
if (topDistance < bottomDistance) -view.bottom
|
||||
else parentRect.bottom + getCompensationHeight(view, params)
|
||||
}
|
||||
|
||||
else -> if (minX <= minY) {
|
||||
isHorizontal = true
|
||||
endValue = params.x
|
||||
if (leftDistance < rightDistance) -view.right else parentRect.right
|
||||
} else {
|
||||
isHorizontal = false
|
||||
endValue = params.y
|
||||
if (topDistance < bottomDistance) -view.bottom
|
||||
else parentRect.bottom + getCompensationHeight(view, params)
|
||||
}
|
||||
}
|
||||
return Triple(startValue, endValue, isHorizontal)
|
||||
}
|
||||
|
||||
/**
|
||||
* 单页面浮窗(popupWindow),坐标从顶部计算,需要加上状态栏的高度
|
||||
*/
|
||||
private fun getCompensationHeight(view: View, params: WindowManager.LayoutParams): Int {
|
||||
val location = IntArray(2)
|
||||
// 获取在整个屏幕内的绝对坐标
|
||||
view.getLocationOnScreen(location)
|
||||
// 绝对高度和相对高度相等,说明是单页面浮窗(popupWindow),计算底部动画时需要加上状态栏高度
|
||||
return if (location[1] == params.y) WindowUtils.getStatusBarHeight(view.context.applicationContext) else 0
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification.enums
|
||||
|
||||
/**
|
||||
* @author: donghongyu
|
||||
* @function: 浮窗显示类别
|
||||
* @date: 2019-07-08 17:05
|
||||
*/
|
||||
enum class ShowPattern {
|
||||
|
||||
// 只在当前Activity显示、仅应用前台时显示、仅应用后台时显示,一直显示(不分前后台)
|
||||
CURRENT_ACTIVITY, FOREGROUND, BACKGROUND, ALL_TIME
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification.enums
|
||||
|
||||
/**
|
||||
* @author: donghongyu
|
||||
* @function: 浮窗的贴边模式
|
||||
* @date: 2019-07-01 17:34
|
||||
*/
|
||||
enum class SidePattern {
|
||||
|
||||
// 默认不贴边,跟随手指移动
|
||||
DEFAULT,
|
||||
// 左、右、上、下四个方向固定(一直吸附在该方向边缘,只能在该方向的边缘移动)
|
||||
LEFT, RIGHT, TOP, BOTTOM,
|
||||
// 根据手指到屏幕边缘的距离,自动选择水平方向的贴边、垂直方向的贴边、四周方向的贴边
|
||||
AUTO_HORIZONTAL, AUTO_VERTICAL, AUTO_SIDE,
|
||||
// 拖拽时跟随手指移动,结束时贴边
|
||||
RESULT_LEFT, RESULT_RIGHT, RESULT_TOP, RESULT_BOTTOM,
|
||||
RESULT_HORIZONTAL, RESULT_VERTICAL, RESULT_SIDE
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification.interfaces
|
||||
|
||||
import android.animation.Animator
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
|
||||
|
||||
/**
|
||||
* @author: donghongyu
|
||||
* @function: 系统浮窗的出入动画
|
||||
* @date: 2019-07-22 16:40
|
||||
*/
|
||||
interface OnFloatAnimator {
|
||||
|
||||
fun enterAnim(
|
||||
view: View,
|
||||
params: WindowManager.LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Animator? = null
|
||||
|
||||
fun exitAnim(
|
||||
view: View,
|
||||
params: WindowManager.LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Animator? = null
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification.interfaces
|
||||
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
|
||||
/**
|
||||
* @author: donghongyu
|
||||
* @function: 浮窗的一些状态回调
|
||||
* @date: 2019-07-16 14:11
|
||||
*/
|
||||
interface OnFloatCallbacks {
|
||||
|
||||
/**
|
||||
* 浮窗的创建结果,是否创建成功
|
||||
*
|
||||
* @param isCreated 是否创建成功
|
||||
* @param msg 失败返回的结果
|
||||
* @param view 浮窗xml布局
|
||||
*/
|
||||
fun createdResult(isCreated: Boolean, msg: String?, view: View?)
|
||||
|
||||
/**
|
||||
* 显示
|
||||
*/
|
||||
fun show(view: View)
|
||||
|
||||
/**
|
||||
* 隐藏
|
||||
*/
|
||||
fun hide(view: View)
|
||||
|
||||
/**
|
||||
* 关闭
|
||||
*/
|
||||
fun dismiss()
|
||||
|
||||
/**
|
||||
* 触摸事件的回调
|
||||
*/
|
||||
fun touchEvent(view: View, event: MotionEvent)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.mogo.eagle.core.function.hmi.notification.widget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.FrameLayout
|
||||
import com.mogo.eagle.core.function.hmi.notification.WarningNotificationConfig
|
||||
|
||||
/**
|
||||
* @author: donghongyu
|
||||
* @function: 系统浮窗的父布局,对touch事件进行了重新分发
|
||||
* @date: 2019-07-10 14:16
|
||||
*/
|
||||
@SuppressLint("ViewConstructor")
|
||||
internal class ParentFrameLayout(
|
||||
context: Context,
|
||||
private val config: WarningNotificationConfig,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
var layoutListener: OnLayoutListener? = null
|
||||
private var isCreated = false
|
||||
|
||||
// 布局绘制完成的接口,用于通知外部做一些View操作,不然无法获取view宽高
|
||||
interface OnLayoutListener {
|
||||
fun onLayout()
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
// 初次绘制完成的时候,需要设置对齐方式、坐标偏移量、入场动画
|
||||
if (!isCreated) {
|
||||
isCreated = true
|
||||
layoutListener?.onLayout()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.mogo.eagle.core.function.hmi.receiver
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.alibaba.android.arouter.launcher.ARouter
|
||||
import com.mogo.eagle.core.function.hmi.WaringConst
|
||||
import com.mogo.service.IMogoServiceApis
|
||||
import com.mogo.service.MogoServicePaths
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider
|
||||
import com.mogo.utils.logger.Logger
|
||||
|
||||
/**
|
||||
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式 控制限速标志
|
||||
*
|
||||
* @author donghongyu
|
||||
*/
|
||||
class V2XLimitingVelocityBroadcastReceiver : BroadcastReceiver() {
|
||||
private var mContext: Context? = null
|
||||
|
||||
companion object {
|
||||
private const val TAG = "V2XLimitingVelocityBroadcastReceiver"
|
||||
private var mMogoServiceApis: IMogoServiceApis? = null
|
||||
private var mIMoGoWaringProvider: IMoGoWaringProvider? = null
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
mMogoServiceApis = ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS)
|
||||
.navigation(context) as IMogoServiceApis
|
||||
mIMoGoWaringProvider = mMogoServiceApis!!.waringProviderApi
|
||||
mContext = context
|
||||
|
||||
val limitingVelocityIsShow =
|
||||
intent.getBooleanExtra(
|
||||
WaringConst.BROADCAST_V2X_LIMITING_VELOCITY_IS_SHOW__EXTRA_KEY,
|
||||
false
|
||||
)
|
||||
val limitingVelocitySpeed =
|
||||
intent.getIntExtra(
|
||||
WaringConst.BROADCAST_V2X_LIMITING_VELOCITY_SPEED__EXTRA_KEY,
|
||||
0
|
||||
)
|
||||
|
||||
Logger.d(
|
||||
TAG,
|
||||
"limitingVelocityIsShow:$limitingVelocityIsShow limitingVelocitySpeed:$limitingVelocitySpeed"
|
||||
)
|
||||
if (limitingVelocityIsShow) {
|
||||
// 分发场景
|
||||
dispatchShowWaring(limitingVelocitySpeed)
|
||||
} else {
|
||||
dispatchCloseWaring()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 展示限速标志
|
||||
*
|
||||
* @param limitingVelocitySpeed 限速速度
|
||||
*/
|
||||
private fun dispatchShowWaring(limitingVelocitySpeed: Int) {
|
||||
mIMoGoWaringProvider!!.showLimitingVelocity(limitingVelocitySpeed)
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭限速标志
|
||||
*/
|
||||
private fun dispatchCloseWaring() {
|
||||
mIMoGoWaringProvider!!.disableLimitingVelocity()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.mogo.eagle.core.function.hmi.receiver
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.alibaba.android.arouter.launcher.ARouter
|
||||
import com.mogo.eagle.core.function.hmi.WaringConst
|
||||
import com.mogo.service.IMogoServiceApis
|
||||
import com.mogo.service.MogoServicePaths
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider
|
||||
import com.mogo.utils.logger.Logger
|
||||
|
||||
/**
|
||||
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式 触发红绿灯场景
|
||||
*
|
||||
* @author donghongyu
|
||||
*/
|
||||
class V2XTrafficLightBroadcastReceiver : BroadcastReceiver() {
|
||||
private var mContext: Context? = null
|
||||
|
||||
companion object {
|
||||
private const val TAG = "V2XTrafficLightBroadcastReceiver"
|
||||
private var mMogoServiceApis: IMogoServiceApis? = null
|
||||
private var mIMoGoWaringProvider: IMoGoWaringProvider? = null
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
mMogoServiceApis = ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS)
|
||||
.navigation(context) as IMogoServiceApis
|
||||
mIMoGoWaringProvider = mMogoServiceApis!!.waringProviderApi
|
||||
mContext = context
|
||||
|
||||
val trafficLightIsShow =
|
||||
intent.getBooleanExtra(
|
||||
WaringConst.BROADCAST_V2X_TRAFFIC_LIGHT_IS_SHOW__EXTRA_KEY,
|
||||
false
|
||||
)
|
||||
val trafficLightCheckType =
|
||||
intent.getIntExtra(
|
||||
WaringConst.BROADCAST_V2X_TRAFFIC_LIGHT_CHECK_TYPE_EXTRA_KEY,
|
||||
0
|
||||
)
|
||||
|
||||
Logger.d(
|
||||
TAG,
|
||||
"trafficLightIsShow:$trafficLightIsShow trafficLightCheckType:$trafficLightCheckType"
|
||||
)
|
||||
if (trafficLightIsShow) {
|
||||
// 分发场景
|
||||
dispatchShowWaring(trafficLightCheckType)
|
||||
} else {
|
||||
dispatchCloseWaring()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 展示交通灯
|
||||
*
|
||||
* @param trafficLightCheckType 选中的交通的灯 0-都是默认,1-红,2-黄,3-绿
|
||||
*/
|
||||
private fun dispatchShowWaring(trafficLightCheckType: Int) {
|
||||
mIMoGoWaringProvider!!.showWarningTrafficLight(trafficLightCheckType)
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭交通灯
|
||||
*/
|
||||
private fun dispatchCloseWaring() {
|
||||
mIMoGoWaringProvider!!.disableWarningTrafficLight()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.mogo.eagle.core.function.hmi.receiver
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.alibaba.android.arouter.launcher.ARouter
|
||||
import com.mogo.module.common.enums.EventTypeEnum
|
||||
import com.mogo.eagle.core.function.hmi.WaringConst
|
||||
import com.mogo.service.IMogoServiceApis
|
||||
import com.mogo.service.MogoServicePaths
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider
|
||||
import com.mogo.utils.logger.Logger
|
||||
|
||||
/**
|
||||
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式弹出预警提示框
|
||||
*
|
||||
* @author donghongyu
|
||||
*/
|
||||
class V2XWarningBroadcastReceiver : BroadcastReceiver() {
|
||||
private var mContext: Context? = null
|
||||
|
||||
companion object {
|
||||
private const val TAG = "V2XWarningBroadcastReceiver"
|
||||
private var mMogoServiceApis: IMogoServiceApis? = null
|
||||
private var mIMoGoWaringProvider: IMoGoWaringProvider? = null
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
try {
|
||||
mMogoServiceApis = ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS)
|
||||
.navigation(context) as IMogoServiceApis
|
||||
mIMoGoWaringProvider = mMogoServiceApis!!.waringProviderApi
|
||||
mContext = context
|
||||
val v2xIsShow =
|
||||
intent.getBooleanExtra(WaringConst.BROADCAST_V2X_IS_SHOW_EXTRA_KEY, false)
|
||||
val v2xType =
|
||||
intent.getIntExtra(WaringConst.BROADCAST_V2X_TYPE_EXTRA_KEY, 0)
|
||||
val alertContent =
|
||||
intent.getStringExtra(WaringConst.BROADCAST_V2X_ALERT_CONTENT_EXTRA_KEY)
|
||||
val ttsContent =
|
||||
intent.getStringExtra(WaringConst.BROADCAST_V2X_TTS_CONTENT_EXTRA_KEY)
|
||||
val tag =
|
||||
intent.getStringExtra(WaringConst.BROADCAST_V2X_TAG_EXTRA_KEY)
|
||||
Logger.d(
|
||||
TAG,
|
||||
"v2xType:$v2xType alertContent:$alertContent ttsContent:$ttsContent tag:$tag"
|
||||
)
|
||||
if (v2xIsShow) {
|
||||
// 分发场景
|
||||
dispatchShowWaring(v2xType, alertContent, ttsContent, tag)
|
||||
} else {
|
||||
dispatchCloseWaring(tag)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分发处理关闭场景
|
||||
*/
|
||||
private fun dispatchCloseWaring(tag: String?) {
|
||||
mIMoGoWaringProvider!!.disableWarningV2X(
|
||||
tag
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 分发处理展示场景
|
||||
*
|
||||
* @param v2xType V2X类型
|
||||
* @param alertContent 提醒文本
|
||||
* @param ttsContent tts语音播报消息
|
||||
* @param tag tag绑定弹窗的标志
|
||||
*/
|
||||
private fun dispatchShowWaring(
|
||||
v2xType: Int,
|
||||
alertContent: String?,
|
||||
ttsContent: String?,
|
||||
tag: String?
|
||||
) {
|
||||
if (EventTypeEnum.TYPE_USECASE_ID_IVP.poiType == v2xType.toString()) {
|
||||
mIMoGoWaringProvider!!.showLimitingVelocity(1)
|
||||
}
|
||||
mIMoGoWaringProvider!!.showWarningV2X(
|
||||
v2xType,
|
||||
alertContent,
|
||||
ttsContent,
|
||||
tag,
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui
|
||||
|
||||
import com.mogo.commons.mvp.IView
|
||||
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.WarningStatusListener
|
||||
|
||||
|
||||
/**
|
||||
*@author xiaoyuzhou
|
||||
*@date 2021/8/4 3:38 下午
|
||||
*/
|
||||
interface MoGoWarningContract {
|
||||
|
||||
interface View : IView {
|
||||
|
||||
/**
|
||||
* 展示VR下V2X预警
|
||||
*
|
||||
* @param v2xType V2X类型
|
||||
* @param alertContent 提醒文本
|
||||
* @param ttsContent tts语音播报消息
|
||||
* @param tag tag绑定弹窗的标志
|
||||
*/
|
||||
fun showWarningV2X(
|
||||
v2xType: Int,
|
||||
alertContent: String?,
|
||||
ttsContent: String?,
|
||||
tag: String?,
|
||||
listener: WarningStatusListener?
|
||||
)
|
||||
|
||||
/**
|
||||
* 关闭指定floatTag 的 VR下V2X预警弹窗
|
||||
* @param tag 弹窗标识
|
||||
*/
|
||||
fun disableWarningV2X(tag: String)
|
||||
|
||||
/**
|
||||
* 展示红绿灯预警
|
||||
*
|
||||
* @param checkLightId 0-都是默认,1-红,2-黄,3-绿
|
||||
*/
|
||||
fun showWarningTrafficLight(checkLightId: Int)
|
||||
|
||||
/**
|
||||
* 关闭红绿灯预警
|
||||
*/
|
||||
fun disableWarningTrafficLight()
|
||||
|
||||
/**
|
||||
* 修改红灯倒计时
|
||||
*/
|
||||
fun changeCountdownRed(redNum: Int)
|
||||
|
||||
/**
|
||||
* 修改黄灯倒计时
|
||||
*/
|
||||
fun changeCountdownYellow(yellowNum: Int)
|
||||
|
||||
/**
|
||||
* 修改绿灯倒计时
|
||||
*/
|
||||
fun changeCountdownGreen(greenNum: Int)
|
||||
|
||||
/**
|
||||
* @param readNum 红灯倒计时
|
||||
* @param yellowNum 黄灯倒计时
|
||||
* @param greenNum 绿灯倒计时
|
||||
*/
|
||||
fun changeCountdownTrafficLightNum(readNum: Int, yellowNum: Int, greenNum: Int)
|
||||
|
||||
/**
|
||||
* 展示限速预警
|
||||
*
|
||||
* @param limitingSpeed 限速速度
|
||||
*/
|
||||
fun showLimitingVelocity(limitingSpeed: Int)
|
||||
|
||||
/**
|
||||
* 关闭限速预警
|
||||
*/
|
||||
fun disableLimitingVelocity()
|
||||
|
||||
/**
|
||||
* 展示指定方位上的红框预警
|
||||
* @param direction
|
||||
* @see WarningDirectionEnum
|
||||
*/
|
||||
fun showWarning(direction: WarningDirectionEnum)
|
||||
|
||||
/**
|
||||
* 展示指定方位上的红框预警
|
||||
* @param direction
|
||||
* @see WarningDirectionEnum
|
||||
* @param closeTime 倒计时
|
||||
*/
|
||||
fun showWarning(direction: WarningDirectionEnum, closeTime: Long)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui
|
||||
|
||||
import android.animation.Animator
|
||||
import android.text.TextUtils
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import com.mogo.commons.mvp.MvpFragment
|
||||
import com.mogo.commons.voice.AIAssist
|
||||
import com.mogo.module.common.enums.EventTypeEnum
|
||||
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
|
||||
import com.mogo.eagle.core.function.hmi.notification.WarningFloat
|
||||
import com.mogo.eagle.core.function.hmi.notification.anim.DefaultAnimator
|
||||
import com.mogo.eagle.core.function.hmi.notification.enums.SidePattern
|
||||
import com.mogo.eagle.core.function.hmi.ui.widget.V2XNotificationView
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.WarningStatusListener
|
||||
import com.mogo.eagle.core.function.hmi.R
|
||||
import com.mogo.utils.logger.Logger
|
||||
import kotlinx.android.synthetic.main.fragment_warning.*
|
||||
|
||||
/**
|
||||
* @author xiaoyuzhou
|
||||
* @date 2021/8/3 2:40 下午
|
||||
* 预警图层
|
||||
*/
|
||||
class MoGoWarningFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>(),
|
||||
MoGoWarningContract.View {
|
||||
|
||||
var mWarningFloat: WarningFloat.Builder? = null
|
||||
|
||||
override fun initViews() {}
|
||||
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.fragment_warning
|
||||
}
|
||||
|
||||
override fun createPresenter(): WaringPresenter {
|
||||
return WaringPresenter(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示VR下V2X预警
|
||||
*
|
||||
* @param v2xType V2X类型
|
||||
* @param alertContent 提醒文本
|
||||
* @param ttsContent tts语音播报消息
|
||||
* @param tag tag绑定弹窗的标志
|
||||
*/
|
||||
override fun showWarningV2X(
|
||||
v2xType: Int,
|
||||
alertContent: String?,
|
||||
ttsContent: String?,
|
||||
tag: String?,
|
||||
listener: WarningStatusListener?
|
||||
) {
|
||||
|
||||
activity?.let {
|
||||
|
||||
val notificationView = V2XNotificationView(it)
|
||||
notificationView.setWarningIcon(EventTypeEnum.getWarningIcon(v2xType.toString()))
|
||||
|
||||
notificationView.setWarningContent(
|
||||
alertContent ?: EventTypeEnum.getWarningContent(
|
||||
v2xType.toString()
|
||||
)
|
||||
)
|
||||
|
||||
if (mWarningFloat != null && mWarningFloat!!.config.floatTag != tag) {
|
||||
WarningFloat.dismiss(mWarningFloat!!.config.floatTag, true)
|
||||
}
|
||||
|
||||
mWarningFloat = WarningFloat.with(it)
|
||||
.setTag(tag)
|
||||
.setLayout(notificationView)
|
||||
.setSidePattern(SidePattern.TOP)
|
||||
.setCountDownTime(10000)
|
||||
.setGravity(Gravity.CENTER_HORIZONTAL, offsetY = 110)
|
||||
.setImmersionStatusBar(true)
|
||||
.addWarningStatusListener(listener)
|
||||
.addWarningStatusListener(object : WarningStatusListener {
|
||||
override fun onShow() {
|
||||
// 创建弹窗成功才进行TTS播报
|
||||
Logger.d(
|
||||
"MoGoWarningFragment",
|
||||
"mWarningFloat = $mWarningFloat---ttsContent = $ttsContent"
|
||||
)
|
||||
if (mWarningFloat != null && !TextUtils.isEmpty(ttsContent)) {
|
||||
Logger.d("MoGoWarningFragment", "---> ttsContent = $ttsContent")
|
||||
AIAssist.getInstance(activity)
|
||||
.speakTTSVoice(ttsContent)
|
||||
}
|
||||
}
|
||||
})
|
||||
.setAnimator(object : DefaultAnimator() {
|
||||
override fun enterAnim(
|
||||
view: View,
|
||||
params: WindowManager.LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Animator? =
|
||||
super.enterAnim(view, params, windowManager, sidePattern)?.apply {
|
||||
interpolator = OvershootInterpolator()
|
||||
}
|
||||
|
||||
override fun exitAnim(
|
||||
view: View,
|
||||
params: WindowManager.LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Animator? =
|
||||
super.exitAnim(view, params, windowManager, sidePattern)?.setDuration(200)
|
||||
})
|
||||
.show()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭指定floatTag 的 VR下V2X预警弹窗
|
||||
* @param tag 弹窗标识
|
||||
*/
|
||||
override fun disableWarningV2X(tag: String) {
|
||||
activity?.let {
|
||||
WarningFloat.dismiss(tag)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示红绿灯预警
|
||||
*
|
||||
* @param checkLightId 0-都是默认,1-红,2-黄,3-绿
|
||||
*/
|
||||
override fun showWarningTrafficLight(checkLightId: Int) {
|
||||
viewTrafficLightVr.showWarningTrafficLight(checkLightId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭红绿灯预警展示,并重制灯态
|
||||
*/
|
||||
override fun disableWarningTrafficLight() {
|
||||
viewTrafficLightVr.disableWarningTrafficLight()
|
||||
}
|
||||
|
||||
override fun changeCountdownRed(redNum: Int) {
|
||||
viewTrafficLightVr.changeCountdownRed(redNum)
|
||||
}
|
||||
|
||||
override fun changeCountdownYellow(yellowNum: Int) {
|
||||
viewTrafficLightVr.changeCountdownYellow(yellowNum)
|
||||
}
|
||||
|
||||
override fun changeCountdownGreen(greenNum: Int) {
|
||||
viewTrafficLightVr.changeCountdownGreen(greenNum)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param readNum 红灯倒计时
|
||||
* @param yellowNum 黄灯倒计时
|
||||
* @param greenNum 绿灯倒计时
|
||||
*/
|
||||
override fun changeCountdownTrafficLightNum(readNum: Int, yellowNum: Int, greenNum: Int) {
|
||||
viewTrafficLightVr.changeCountdownTrafficLightNum(readNum, yellowNum, greenNum)
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制展示限速标志及内容
|
||||
*/
|
||||
override fun showLimitingVelocity(limitingSpeed: Int) {
|
||||
if (limitingSpeed > 0) {
|
||||
tvLimitingVelocity.visibility = View.VISIBLE
|
||||
tvLimitingVelocity.text = "$limitingSpeed"
|
||||
} else {
|
||||
tvLimitingVelocity.visibility = View.INVISIBLE
|
||||
tvLimitingVelocity.text = "0"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制关闭限速标志及内容
|
||||
*/
|
||||
override fun disableLimitingVelocity() {
|
||||
tvLimitingVelocity.visibility = View.GONE
|
||||
tvLimitingVelocity.text = "0"
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示指定方位上的红框预警
|
||||
* @param direction
|
||||
* @see WarningDirectionEnum
|
||||
*/
|
||||
override fun showWarning(direction: WarningDirectionEnum) {
|
||||
flV2XWarningView.showWarning(direction)
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示指定方位上的红框预警
|
||||
* @param direction
|
||||
* @see WarningDirectionEnum
|
||||
* @param closeTime 倒计时
|
||||
*/
|
||||
override fun showWarning(direction: WarningDirectionEnum, closeTime: Long) {
|
||||
flV2XWarningView.showWarning(direction, closeTime)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui
|
||||
|
||||
import com.mogo.commons.mvp.Presenter
|
||||
|
||||
/**
|
||||
* @author xiaoyuzhou
|
||||
* @date 2021/8/3 3:55 下午
|
||||
*/
|
||||
class WaringPresenter(view: MoGoWarningContract.View?) :
|
||||
Presenter<MoGoWarningContract.View?>(view) {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui.widget;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Typeface;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.mogo.eagle.core.function.hmi.R;
|
||||
|
||||
|
||||
/**
|
||||
* created by wujifei on 2021/3/24 16:20
|
||||
* describe:
|
||||
*/
|
||||
public class SpeedChartView extends View {
|
||||
|
||||
//中心的文字描述
|
||||
private String mDes = "KM/H";
|
||||
//根据数据显示的圆弧Paint
|
||||
private Paint mArcPaint;
|
||||
//圆弧颜色
|
||||
private int mArcColor;
|
||||
//圆弧的画笔的宽度
|
||||
private float mStrokeWith = getResources().getDimension(R.dimen.module_ext_arcView_stroke_with);
|
||||
//文字描述的paint
|
||||
private Paint mTextPaint;
|
||||
|
||||
//当前进度夹角大小
|
||||
private float mIncludedAngle = 0;
|
||||
//当前数据
|
||||
private int currentValue;
|
||||
//最大数据
|
||||
private int maxValue = 240;
|
||||
//圆弧背景的开始和结束间的夹角大小
|
||||
private float mAngle = 270;
|
||||
//上次绘制圆弧夹角
|
||||
private float lastAngle = 0;
|
||||
|
||||
public SpeedChartView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SpeedChartView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SpeedChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
//初始化paint
|
||||
initPaint();
|
||||
//绘制弧度
|
||||
drawArc(canvas);
|
||||
//绘制文本
|
||||
drawText(canvas);
|
||||
}
|
||||
|
||||
private void drawText(Canvas canvas) {
|
||||
Rect mRect = new Rect();
|
||||
String mValue = String.valueOf(currentValue);
|
||||
mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||
//绘制中心的数值
|
||||
mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
|
||||
canvas.drawText(mValue, getWidth() / 2, getHeight() / 2 + mRect.height() / 2 - 10, mTextPaint);
|
||||
|
||||
mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
|
||||
//绘制中心文字描述
|
||||
mTextPaint.setTextSize(getResources().getDimension(R.dimen.module_ext_arcView_des_text_size));
|
||||
mTextPaint.getTextBounds(mDes, 0, mDes.length(), mRect);
|
||||
canvas.drawText(mDes, getWidth() / 2, getHeight() * 17 / 20 + mRect.height() / 2, mTextPaint);
|
||||
}
|
||||
|
||||
private void drawArc(Canvas canvas) {
|
||||
//绘制圆弧背景
|
||||
RectF mRectF = new RectF(mStrokeWith, mStrokeWith, getWidth() - mStrokeWith, getHeight() - mStrokeWith);
|
||||
canvas.drawArc(mRectF, 135, mAngle, false, mArcPaint);
|
||||
|
||||
//绘制当前数值对应的圆弧
|
||||
mArcPaint.setColor(mArcColor);
|
||||
//根据当前数据绘制对应的圆弧
|
||||
canvas.drawArc(mRectF, 135, mIncludedAngle, false, mArcPaint);
|
||||
}
|
||||
|
||||
private void initPaint() {
|
||||
//圆弧的paint
|
||||
mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
//抗锯齿
|
||||
mArcPaint.setAntiAlias(true);
|
||||
mArcPaint.setColor(Color.parseColor("#151D4C"));
|
||||
//设置透明度(数值为0-255)
|
||||
mArcPaint.setAlpha(100);
|
||||
//设置画笔的画出的形状
|
||||
mArcPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
//设置画笔类型
|
||||
mArcPaint.setStyle(Paint.Style.STROKE);
|
||||
//画笔宽度
|
||||
mArcPaint.setStrokeWidth(mStrokeWith);
|
||||
|
||||
//中心文字的paint
|
||||
mTextPaint = new Paint();
|
||||
mTextPaint.setAntiAlias(true);
|
||||
mTextPaint.setColor(Color.parseColor("#FFFFFF"));
|
||||
//设置文本的对齐方式
|
||||
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||
//mTextPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.dp_12));
|
||||
mTextPaint.setTextSize(getResources().getDimension(R.dimen.module_ext_arcView_center_text_size));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 为绘制弧度及数据设置动画
|
||||
*
|
||||
* @param startAngle 开始的弧度
|
||||
* @param currentAngle 需要绘制的弧度
|
||||
* @param time 动画执行的时长
|
||||
*/
|
||||
private void setAnimation(float startAngle, float currentAngle, int time) {
|
||||
//绘制当前数据对应的圆弧的动画效果
|
||||
ValueAnimator progressAnimator = ValueAnimator.ofFloat(startAngle, currentAngle);
|
||||
progressAnimator.setDuration(time);
|
||||
progressAnimator.setTarget(mIncludedAngle);
|
||||
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
mIncludedAngle = (float) animation.getAnimatedValue();
|
||||
//重新绘制,不然不会出现效果
|
||||
postInvalidate();
|
||||
}
|
||||
});
|
||||
//开始执行动画
|
||||
progressAnimator.start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置弧形颜色
|
||||
*
|
||||
* @param value 颜色值
|
||||
*/
|
||||
public void setArcColor(int value) {
|
||||
mArcColor = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据
|
||||
*
|
||||
* @param value 当前绘制的值
|
||||
*/
|
||||
public void setValues(int value) {
|
||||
//完全覆盖
|
||||
if (value > maxValue) {
|
||||
value = maxValue;
|
||||
}
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
currentValue = value;
|
||||
//计算弧度比重
|
||||
float scale = (float) currentValue / maxValue;
|
||||
//计算弧度
|
||||
float currentAngle = scale * mAngle;
|
||||
//开始执行动画
|
||||
setAnimation(lastAngle, currentAngle, 1000);
|
||||
lastAngle = currentAngle;
|
||||
//重新绘制
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.location.Location
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import com.alibaba.android.arouter.launcher.ARouter
|
||||
import com.mogo.commons.debug.DebugConfig
|
||||
import com.mogo.eagle.core.function.hmi.R
|
||||
import com.mogo.map.MogoLatLng
|
||||
import com.mogo.map.navi.IMogoCarLocationChangedListener2
|
||||
import com.mogo.module.common.MogoApisHandler
|
||||
import com.mogo.service.IMogoServiceApis
|
||||
import com.mogo.service.MogoServicePaths
|
||||
import com.mogo.service.statusmanager.IMogoStatusChangedListener
|
||||
import com.mogo.service.statusmanager.StatusDescriptor
|
||||
|
||||
/**
|
||||
* @author xiaoyuzhou
|
||||
* @date 2021/8/25 8:25 下午
|
||||
*/
|
||||
class SpeedPanelView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr),
|
||||
IMogoCarLocationChangedListener2,
|
||||
IMogoStatusChangedListener {
|
||||
val TAG = "SpeedPanelView"
|
||||
private var mMogoServiceApis: IMogoServiceApis =
|
||||
ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS)
|
||||
.navigation(context) as IMogoServiceApis
|
||||
|
||||
var mContext: Context
|
||||
var mSpeedChartView: SpeedChartView
|
||||
|
||||
init {
|
||||
setBackgroundResource(R.drawable.yi_biao_pan_bg_nor)
|
||||
mContext = context
|
||||
mSpeedChartView = SpeedChartView(context)
|
||||
|
||||
val layoutParams = LayoutParams(
|
||||
resources.getDimension(R.dimen.module_ext_arcView_width).toInt(),
|
||||
resources.getDimension(R.dimen.module_ext_arcView_height).toInt()
|
||||
)
|
||||
layoutParams.gravity = Gravity.CENTER
|
||||
mSpeedChartView.layoutParams = layoutParams
|
||||
addView(mSpeedChartView)
|
||||
|
||||
if (DebugConfig.isDebug()) {
|
||||
mSpeedChartView.isLongClickable = true
|
||||
mSpeedChartView.setOnLongClickListener { v ->
|
||||
Log.d(TAG, "长按显示状态工具栏")
|
||||
val intent = Intent()
|
||||
intent.putExtra("oper", 52)
|
||||
MogoApisHandler.getInstance().apis.intentManagerApi
|
||||
.invoke("com.mogo.mock", intent)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// 注册位置回调
|
||||
mMogoServiceApis.registerCenterApi
|
||||
.registerCarLocationChangedListener(TAG, this)
|
||||
// 注册VR模式回调
|
||||
mMogoServiceApis.statusManagerApi
|
||||
.registerStatusChangedListener(TAG, StatusDescriptor.VR_MODE, this)
|
||||
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
// 解除注册
|
||||
mMogoServiceApis.registerCenterApi
|
||||
.unregisterMogoLocationListener(TAG)
|
||||
mMogoServiceApis.statusManagerApi
|
||||
.unregisterStatusChangedListener(TAG, StatusDescriptor.VR_MODE, this)
|
||||
}
|
||||
|
||||
|
||||
override fun onCarLocationChanged(latLng: MogoLatLng?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onCarLocationChanged2(latLng: Location) {
|
||||
val speed = (latLng.speed * 3.6f).toInt()
|
||||
mSpeedChartView.setArcColor(Color.parseColor(if (speed > 60) "#DB3137" else "#3E77F6"))
|
||||
mSpeedChartView.setValues(speed)
|
||||
setBackgroundResource(if (speed > 60) R.drawable.yi_biao_pan_bg_speeding else R.drawable.yi_biao_pan_bg_nor)
|
||||
}
|
||||
|
||||
override fun onStatusChanged(descriptor: StatusDescriptor?, isTrue: Boolean) {
|
||||
if (descriptor == StatusDescriptor.VR_MODE) {
|
||||
try {
|
||||
visibility = if (isTrue) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.mogo.eagle.core.function.hmi.R
|
||||
import kotlinx.android.synthetic.main.view_traffic_light_vr.view.*
|
||||
|
||||
/**
|
||||
* @author xiaoyuzhou
|
||||
* @date 2021/8/4 3:16 下午
|
||||
* 红绿灯控件
|
||||
*/
|
||||
class TrafficLightView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_traffic_light_vr, this, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示红绿灯预警
|
||||
*
|
||||
* @param checkLightId 0-都是默认,1-红,2-黄,3-绿
|
||||
*/
|
||||
fun showWarningTrafficLight(checkLightId: Int) {
|
||||
visibility = View.VISIBLE
|
||||
when (checkLightId) {
|
||||
0 -> {
|
||||
ctvRedTrafficLight.isChecked = false
|
||||
ctvYellowTrafficLight.isChecked = false
|
||||
ctvGreenTrafficLight.isChecked = false
|
||||
}
|
||||
1 -> {
|
||||
ctvRedTrafficLight.isChecked = true
|
||||
ctvYellowTrafficLight.isChecked = false
|
||||
ctvGreenTrafficLight.isChecked = false
|
||||
}
|
||||
2 -> {
|
||||
ctvRedTrafficLight.isChecked = false
|
||||
ctvYellowTrafficLight.isChecked = true
|
||||
ctvGreenTrafficLight.isChecked = false
|
||||
}
|
||||
3 -> {
|
||||
ctvRedTrafficLight.isChecked = false
|
||||
ctvYellowTrafficLight.isChecked = false
|
||||
ctvGreenTrafficLight.isChecked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭红绿灯预警展示,并重制灯态
|
||||
*/
|
||||
fun disableWarningTrafficLight() {
|
||||
visibility = View.GONE
|
||||
ctvRedTrafficLight.isChecked = false
|
||||
ctvYellowTrafficLight.isChecked = false
|
||||
ctvGreenTrafficLight.isChecked = false
|
||||
ctvRedTrafficLight.text = ""
|
||||
ctvYellowTrafficLight.text = ""
|
||||
ctvGreenTrafficLight.text = ""
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param readNum 红灯倒计时
|
||||
* @param yellowNum 黄灯倒计时
|
||||
* @param greenNum 绿灯倒计时
|
||||
*/
|
||||
fun changeCountdownTrafficLightNum(readNum: Int, yellowNum: Int, greenNum: Int) {
|
||||
changeCountdownGreen(readNum)
|
||||
changeCountdownYellow(yellowNum)
|
||||
changeCountdownRed(greenNum)
|
||||
}
|
||||
|
||||
fun changeCountdownGreen(greenNum: Int) {
|
||||
if (greenNum > 0) {
|
||||
ctvGreenTrafficLight.text = "$greenNum"
|
||||
} else {
|
||||
ctvGreenTrafficLight.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCountdownYellow(yellowNum: Int) {
|
||||
if (yellowNum > 0) {
|
||||
ctvYellowTrafficLight.text = "$yellowNum"
|
||||
} else {
|
||||
ctvYellowTrafficLight.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCountdownRed(redNum: Int) {
|
||||
if (redNum > 0) {
|
||||
ctvRedTrafficLight.text = "$redNum"
|
||||
} else {
|
||||
ctvRedTrafficLight.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.mogo.eagle.core.function.hmi.R
|
||||
import kotlinx.android.synthetic.main.notification_v2x_msg_vr.view.*
|
||||
|
||||
/**
|
||||
*@author xiaoyuzhou
|
||||
*@date 2021/8/6 12:25 下午
|
||||
*/
|
||||
class V2XNotificationView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.notification_v2x_msg_vr, this, true)
|
||||
}
|
||||
|
||||
fun setWarningIcon(@DrawableRes warningIcon: Int) {
|
||||
ivWaringIcon.setImageResource(warningIcon)
|
||||
}
|
||||
|
||||
fun setWarningIcon(@Nullable drawable: Drawable) {
|
||||
ivWaringIcon.setImageDrawable(drawable)
|
||||
}
|
||||
|
||||
fun setWarningContent(@Nullable warningContent: String) {
|
||||
tvWaringContent.text = warningContent
|
||||
}
|
||||
|
||||
fun setWarningContent(@StringRes warningContentId: Int) {
|
||||
tvWaringContent.setText(warningContentId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.RelativeLayout
|
||||
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
|
||||
import com.mogo.eagle.core.function.hmi.R
|
||||
import com.mogo.utils.logger.Logger
|
||||
import kotlinx.android.synthetic.main.module_hmi_warning_v2x.view.*
|
||||
|
||||
/**
|
||||
*@author xiaoyuzhou
|
||||
*@date 2021/9/10 7:44 下午
|
||||
*/
|
||||
class V2XWarningView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private val ALL_CLOSE_TIMER = 10000L
|
||||
|
||||
private val closeWarningTask: Runnable = Runnable {
|
||||
Logger.d("V2XWarningView", "预警红边:倒计时结束")
|
||||
showWarning(WarningDirectionEnum.ALERT_WARNING_NON)
|
||||
}
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.module_hmi_warning_v2x, this, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示指定方位上的红框预警
|
||||
* @param direction
|
||||
* @see WarningDirectionEnum
|
||||
*/
|
||||
fun showWarning(direction: WarningDirectionEnum) {
|
||||
showWarning(direction, ALL_CLOSE_TIMER)
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示指定方位上的红框预警
|
||||
* @param direction
|
||||
* @see WarningDirectionEnum
|
||||
* @param closeTime 倒计时
|
||||
*/
|
||||
fun showWarning(direction: WarningDirectionEnum, closeTime: Long) {
|
||||
Logger.d("V2XWarningView", "预警红边:预警方向->$direction 预警倒计时->$closeTime")
|
||||
// 如果传入的不是关闭显示,则设置倒计时,定时关闭红框警示
|
||||
if (direction != WarningDirectionEnum.ALERT_WARNING_NON) {
|
||||
removeCallbacks(closeWarningTask)
|
||||
postDelayed(closeWarningTask, closeTime)
|
||||
}
|
||||
when (direction) {
|
||||
WarningDirectionEnum.ALERT_WARNING_NON -> {
|
||||
removeCallbacks(closeWarningTask)
|
||||
hmiWarningTopImg.visibility = View.GONE
|
||||
hmiWarningRightImg.visibility = View.GONE
|
||||
hmiWarningBottomImg.visibility = View.GONE
|
||||
hmiWarningLeftImg.visibility = View.GONE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_TOP -> {
|
||||
hmiWarningTopImg.visibility = View.VISIBLE
|
||||
hmiWarningRightImg.visibility = View.GONE
|
||||
hmiWarningBottomImg.visibility = View.GONE
|
||||
hmiWarningLeftImg.visibility = View.GONE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_RIGHT -> {
|
||||
hmiWarningTopImg.visibility = View.GONE
|
||||
hmiWarningRightImg.visibility = View.VISIBLE
|
||||
hmiWarningBottomImg.visibility = View.GONE
|
||||
hmiWarningLeftImg.visibility = View.GONE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_BOTTOM -> {
|
||||
hmiWarningTopImg.visibility = View.GONE
|
||||
hmiWarningRightImg.visibility = View.GONE
|
||||
hmiWarningBottomImg.visibility = View.VISIBLE
|
||||
hmiWarningLeftImg.visibility = View.GONE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_LEFT -> {
|
||||
hmiWarningTopImg.visibility = View.GONE
|
||||
hmiWarningRightImg.visibility = View.GONE
|
||||
hmiWarningBottomImg.visibility = View.GONE
|
||||
hmiWarningLeftImg.visibility = View.VISIBLE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_TOP_RIGHT -> {
|
||||
hmiWarningTopImg.visibility = View.VISIBLE
|
||||
hmiWarningRightImg.visibility = View.VISIBLE
|
||||
hmiWarningBottomImg.visibility = View.GONE
|
||||
hmiWarningLeftImg.visibility = View.GONE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_BOTTOM_RIGHT -> {
|
||||
hmiWarningTopImg.visibility = View.GONE
|
||||
hmiWarningRightImg.visibility = View.VISIBLE
|
||||
hmiWarningBottomImg.visibility = View.VISIBLE
|
||||
hmiWarningLeftImg.visibility = View.GONE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_BOTTOM_LEFT -> {
|
||||
hmiWarningTopImg.visibility = View.GONE
|
||||
hmiWarningRightImg.visibility = View.GONE
|
||||
hmiWarningBottomImg.visibility = View.VISIBLE
|
||||
hmiWarningLeftImg.visibility = View.VISIBLE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_TOP_LEFT -> {
|
||||
hmiWarningTopImg.visibility = View.VISIBLE
|
||||
hmiWarningRightImg.visibility = View.GONE
|
||||
hmiWarningBottomImg.visibility = View.GONE
|
||||
hmiWarningLeftImg.visibility = View.VISIBLE
|
||||
}
|
||||
WarningDirectionEnum.ALERT_WARNING_ALL -> {
|
||||
hmiWarningTopImg.visibility = View.VISIBLE
|
||||
hmiWarningRightImg.visibility = View.VISIBLE
|
||||
hmiWarningBottomImg.visibility = View.VISIBLE
|
||||
hmiWarningLeftImg.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.mogo.eagle.core.function.hmi.warning;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.mogo.eagle.core.data.enums.WarningDirectionEnum;
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider;
|
||||
import com.mogo.eagle.core.function.api.hmi.warning.WarningStatusListener;
|
||||
import com.mogo.eagle.core.function.hmi.WaringConst;
|
||||
import com.mogo.eagle.core.function.hmi.ui.MoGoWarningFragment;
|
||||
import com.mogo.service.MogoServicePaths;
|
||||
import com.mogo.utils.logger.Logger;
|
||||
|
||||
/**
|
||||
* @author xiaoyuzhou
|
||||
* @date 2021/8/2 5:52 下午
|
||||
* 预警模块
|
||||
*/
|
||||
@Route(path = MogoServicePaths.PATH_V2X_WARNING)
|
||||
public class MoGoWarningProvider implements IMoGoWaringProvider {
|
||||
private String TAG = "MoGoWarningProvider";
|
||||
|
||||
private MoGoWarningFragment mMoGoWarningFragment;
|
||||
private Context mContext;
|
||||
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
Logger.d(TAG, "初始化蘑菇预警模块 ……");
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment createCoverage(Context context, Bundle data) {
|
||||
Logger.d(TAG, "初始化蘑菇预警模块 Fragment……");
|
||||
mMoGoWarningFragment = new MoGoWarningFragment();
|
||||
return mMoGoWarningFragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getFunctionName() {
|
||||
return WaringConst.MODULE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showWarningTrafficLight(int checkLightId) {
|
||||
mMoGoWarningFragment.showWarningTrafficLight(checkLightId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableWarningTrafficLight() {
|
||||
mMoGoWarningFragment.disableWarningTrafficLight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLimitingVelocity(int limitingSpeed) {
|
||||
mMoGoWarningFragment.showLimitingVelocity(limitingSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableLimitingVelocity() {
|
||||
mMoGoWarningFragment.disableLimitingVelocity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeCountdownRed(int redNum) {
|
||||
mMoGoWarningFragment.changeCountdownRed(redNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeCountdownYellow(int yellowNum) {
|
||||
mMoGoWarningFragment.changeCountdownYellow(yellowNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeCountdownGreen(int greenNum) {
|
||||
mMoGoWarningFragment.changeCountdownGreen(greenNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeCountdownTrafficLightNum(int readNum, int yellowNum, int greenNum) {
|
||||
mMoGoWarningFragment.changeCountdownTrafficLightNum(readNum, yellowNum, greenNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showWarningV2X(int v2xType, @Nullable String alertContent,
|
||||
@Nullable String ttsContent, @Nullable String tag,
|
||||
@Nullable WarningStatusListener listener) {
|
||||
mMoGoWarningFragment.showWarningV2X(v2xType, alertContent, ttsContent, tag, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableWarningV2X(String tag) {
|
||||
mMoGoWarningFragment.disableWarningV2X(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showWarning(@NonNull WarningDirectionEnum direction) {
|
||||
mMoGoWarningFragment.showWarning(direction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showWarning(@NonNull WarningDirectionEnum direction, long closeTime) {
|
||||
mMoGoWarningFragment.showWarning(direction, closeTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.mogo.eagle.core.function.hmi.ui.widget.V2XWarningView
|
||||
android:id="@+id/flV2XWarningView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.mogo.eagle.core.function.hmi.ui.widget.SpeedPanelView
|
||||
android:id="@+id/flSpeedChartView"
|
||||
android:layout_width="@dimen/module_ext_speed_width"
|
||||
android:layout_height="@dimen/module_ext_speed_height"
|
||||
android:layout_marginLeft="@dimen/dp_40"
|
||||
android:layout_marginTop="@dimen/dp_40"
|
||||
android:elevation="@dimen/dp_10"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.mogo.eagle.core.function.hmi.ui.widget.TrafficLightView
|
||||
android:id="@+id/viewTrafficLightVr"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40px"
|
||||
android:layout_marginRight="40px"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLimitingVelocity"
|
||||
android:layout_width="130px"
|
||||
android:layout_height="130px"
|
||||
android:layout_marginTop="30px"
|
||||
android:background="@drawable/bg_waring_limiting_velocity"
|
||||
android:gravity="center"
|
||||
android:text="60"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="74px"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@+id/viewTrafficLightVr"
|
||||
app:layout_constraintTop_toBottomOf="@+id/viewTrafficLightVr"
|
||||
app:layout_goneMarginEnd="40px"
|
||||
app:layout_goneMarginTop="40px"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<!--四个方向,碰撞预警-->
|
||||
<ImageView
|
||||
android:id="@+id/hmiWarningTopImg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_290"
|
||||
android:background="@drawable/module_hmi_warning_bkg_top"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/hmiWarningLeftImg"
|
||||
android:layout_width="@dimen/dp_290"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/module_hmi_warning_bkg_left"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/hmiWarningRightImg"
|
||||
android:layout_width="@dimen/dp_290"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end"
|
||||
android:background="@drawable/module_hmi_warning_bkg_right"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/hmiWarningBottomImg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_290"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="@drawable/module_hmi_warning_bkg_bottom"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/bg_warning_bg"
|
||||
android:paddingStart="38px"
|
||||
android:paddingTop="27px"
|
||||
android:paddingEnd="38px"
|
||||
android:paddingBottom="27px">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivWaringIcon"
|
||||
android:layout_width="132px"
|
||||
android:layout_height="132px"
|
||||
android:src="@drawable/icon_warning_v2x_abnormal_vehicle"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvWaringContent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="26px"
|
||||
android:text="预警信息"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="42px"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/ivWaringIcon"
|
||||
app:layout_constraintLeft_toRightOf="@+id/ivWaringIcon"
|
||||
app:layout_constraintTop_toTopOf="@+id/ivWaringIcon"
|
||||
tools:text="前车碰撞预警" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
||||
android:id="@+id/clTrafficLight"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_waring_traffic_light_vr"
|
||||
android:paddingStart="10px"
|
||||
android:paddingTop="10px"
|
||||
android:paddingEnd="10px"
|
||||
android:paddingBottom="10px"
|
||||
tools:visibility="visible">
|
||||
|
||||
<CheckedTextView
|
||||
android:id="@+id/ctvRedTrafficLight"
|
||||
android:layout_width="110px"
|
||||
android:layout_height="110px"
|
||||
android:background="@drawable/icon_waring_traffic_light_red_vr"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/ctvYellowTrafficLight"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:checked="true"
|
||||
tools:text="99" />
|
||||
|
||||
<CheckedTextView
|
||||
android:id="@+id/ctvYellowTrafficLight"
|
||||
android:layout_width="110px"
|
||||
android:layout_height="110px"
|
||||
android:layout_marginStart="90px"
|
||||
android:background="@drawable/icon_waring_traffic_light_yellow_vr"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/ctvRedTrafficLight"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:checked="true"
|
||||
tools:text="99" />
|
||||
|
||||
<CheckedTextView
|
||||
android:id="@+id/ctvGreenTrafficLight"
|
||||
android:layout_width="110px"
|
||||
android:layout_height="110px"
|
||||
android:layout_marginStart="90px"
|
||||
android:background="@drawable/icon_waring_traffic_light_green_vr"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="38px"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/ctvYellowTrafficLight"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:checked="true"
|
||||
tools:text="99" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Reference in New Issue
Block a user