Merge branch 'dev_robotaxi-d-app-module_251_220125_2.5.1' of gitlab.zhidaoauto.com:zhjt/AndroidApp/MoGoEagleEye into dev_robotaxi-d-app-module_251_220125_2.5.1

# Conflicts:
#	settings.gradle
This commit is contained in:
wangmingjun
2022-01-25 16:35:46 +08:00
1622 changed files with 37992 additions and 18685 deletions

View File

@@ -63,13 +63,15 @@ dependencies {
implementation rootProject.ext.dependencies.mogo_core_data
implementation rootProject.ext.dependencies.mogo_core_function_call
implementation rootProject.ext.dependencies.mogo_core_function_smp
implementation rootProject.ext.dependencies.mogo_core_function_v2x
}else {
implementation project(":core:mogo-core-utils")
implementation project(":foudations:mogo-commons")
implementation project(':modules:mogo-module-common')
implementation project(':core:mogo-core-data')
implementation project(':core:mogo-core-function-call')
api project(':core:function-impl:mogo-core-function-smp')
implementation project(':core:function-impl:mogo-core-function-smp')
implementation project(':core:function-impl:mogo-core-function-v2x')
}
}

View File

@@ -6,13 +6,13 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.och.taxi.constant.OCHTaxiConst;
import com.mogo.och.taxi.ui.OCHTaxiFragment;
import com.mogo.service.statusmanager.IMogoStatusChangedListener;
import com.mogo.service.statusmanager.StatusDescriptor;
import com.mogo.utils.UiThreadHandler;
import com.mogo.utils.logger.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@@ -4,13 +4,12 @@ import android.content.Context;
import com.mogo.eagle.core.data.map.MogoLatLng;
import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.v2x.events.utils.LocationUtils;
import com.mogo.eagle.core.utilcode.util.ColorUtils;
import com.mogo.map.overlay.IMogoOverlayManager;
import com.mogo.map.overlay.IMogoPolyline;
import com.mogo.map.overlay.MogoPolylineOptions;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.module.common.utils.LocationUtils;
import com.mogo.utils.ColorUtils;
import java.util.ArrayList;
import java.util.List;

View File

@@ -24,7 +24,10 @@ import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotPlanningListenerManager;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr;
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast;
import com.mogo.eagle.core.utilcode.util.NetworkUtils;
import com.mogo.map.navi.IMogoCarLocationChangedListener2;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.och.taxi.callback.IOCHTaxiAutopilotPlanningCallback;
@@ -53,9 +56,6 @@ import com.mogo.service.cloud.socket.IMogoLifecycleListener;
import com.mogo.service.intent.IMogoIntentListener;
import com.mogo.service.statusmanager.IMogoStatusChangedListener;
import com.mogo.service.statusmanager.StatusDescriptor;
import com.mogo.utils.NetworkUtils;
import com.mogo.utils.logger.Logger;
import com.mogo.utils.storage.SharedPrefsMgr;
import org.jetbrains.annotations.NotNull;

View File

@@ -24,9 +24,9 @@ import com.amap.api.navi.model.NaviInfo;
import com.amap.api.navi.model.NaviLatLng;
import com.autonavi.tbt.TrafficFacilityInfo;
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast;
import com.mogo.eagle.core.utilcode.util.NetworkUtils;
import com.mogo.och.taxi.callback.IOCHTaxiNaviChangedCallback;
import com.mogo.och.taxi.utils.PermissionUtil;
import com.mogo.utils.NetworkUtils;
import java.util.ArrayList;
import java.util.List;

View File

@@ -1,7 +1,7 @@
package com.mogo.och.taxi.model;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.och.taxi.constant.OCHTaxiConst;
import com.mogo.utils.logger.Logger;
import java.util.concurrent.TimeUnit;

View File

@@ -6,6 +6,7 @@ import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.eagle.core.data.BaseData;
import com.mogo.eagle.core.network.RequestOptions;
import com.mogo.eagle.core.network.SubscribeImpl;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.och.taxi.BuildConfig;
import com.mogo.och.taxi.bean.CarHeartbeatReqBean;
@@ -25,7 +26,6 @@ import com.mogo.och.taxi.bean.OrdersInServiceQueryRespBean;
import com.mogo.och.taxi.bean.OrdersListQueryReqBean;
import com.mogo.och.taxi.bean.OrdersListQueryRespBean;
import com.mogo.och.taxi.bean.OrdersNewBookingQueryRespBean;
import com.mogo.utils.logger.Logger;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

View File

@@ -13,6 +13,8 @@ import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.mvp.Presenter;
import com.mogo.eagle.core.data.autopilot.AutopilotRouteInfo;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.taxi.callback.IOCHTaxiAutopilotPlanningCallback;
import com.mogo.och.taxi.constant.OrderStatusEnum;
import com.mogo.och.taxi.bean.OrderQueryRespBean;
@@ -23,8 +25,6 @@ import com.mogo.och.taxi.callback.IOCHTaxiControllerStatusCallback;
import com.mogo.och.taxi.callback.IOCHTaxiOrderStatusCallback;
import com.mogo.och.taxi.model.MogoOCHTaxiModelNew;
import com.mogo.och.taxi.ui.OCHTaxiFragment;
import com.mogo.utils.UiThreadHandler;
import com.mogo.utils.logger.Logger;
import org.jetbrains.annotations.NotNull;

View File

@@ -29,14 +29,14 @@ import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.function.call.map.CallerSmpManager;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.map.listener.IMogoMapListener;
import com.mogo.map.uicontroller.VisualAngleMode;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.module.common.constants.DataTypes;
import com.mogo.module.common.view.OnPreventFastClickListener;
import com.mogo.och.taxi.R;
import com.mogo.och.taxi.constant.OrderStatusEnum;
import com.mogo.utils.logger.Logger;
/**
* 网约车基础Fragment主要负责布局通用界面处理站点面板和通话面板互斥情况

View File

@@ -18,7 +18,10 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import com.amap.api.navi.model.NaviLatLng;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.commons.voice.AIAssist;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast;
import com.mogo.eagle.core.utilcode.util.DateTimeUtils;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.taxi.callback.IOCHTaxiNaviChangedCallback;
import com.mogo.och.taxi.constant.OrderStatusEnum;
import com.mogo.och.taxi.R;
@@ -27,9 +30,6 @@ import com.mogo.och.taxi.bean.OrderQueryRouteInfoRespBean;
import com.mogo.och.taxi.model.MogoOCHTaxiModelNew;
import com.mogo.och.taxi.model.NaviToDestinationModel;
import com.mogo.och.taxi.utils.OchTaxiUtils;
import com.mogo.utils.DateTimeUtils;
import com.mogo.utils.UiThreadHandler;
import com.mogo.utils.logger.Logger;
import java.text.DecimalFormat;
import java.util.Calendar;

View File

@@ -11,6 +11,7 @@ import androidx.fragment.app.FragmentTransaction;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.eagle.core.data.autopilot.AutopilotRouteInfo;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.och.taxi.R;
import com.mogo.och.taxi.constant.OrderStatusEnum;
@@ -19,7 +20,6 @@ import com.mogo.och.taxi.bean.OrderQueryRouteInfoRespBean;
import com.mogo.och.taxi.model.MogoOCHTaxiModelNew;
import com.mogo.och.taxi.presenter.OCHTaxiPresenter;
import com.mogo.och.taxi.utils.PinYinUtil;
import com.mogo.utils.logger.Logger;
import java.lang.ref.WeakReference;
import java.util.List;

View File

@@ -9,10 +9,11 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.taxi.R;
import com.mogo.och.taxi.bean.OrderQueryRespBean;
import com.mogo.och.taxi.utils.OchTaxiUtils;
import com.mogo.utils.UiThreadHandler;
import java.util.Calendar;

View File

@@ -18,10 +18,10 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.och.taxi.R;
import com.mogo.och.taxi.bean.OrderQueryRespBean;
import com.mogo.och.taxi.bean.OrderQueryRouteInfoRespBean;
import com.mogo.utils.logger.Logger;
import java.util.ArrayList;
import java.util.List;

View File

@@ -1,6 +1,6 @@
package com.mogo.och.taxi.utils;
import com.mogo.utils.DateTimeUtils;
import com.mogo.eagle.core.utilcode.util.DateTimeUtils;
import java.text.SimpleDateFormat;
import java.util.Calendar;

View File

@@ -4,16 +4,84 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'android-aspectjx'
apply plugin: 'bugly'
apply plugin: 'apm-plugin'
if (!isAndroidTestBuild()) {
apply plugin: 'apm-plugin'
}
//apply ByteX宿主
apply plugin: 'bytex'
ByteX {
enable true
enableInDebug true
logLevel "DEBUG"
if (!isAndroidTestBuild()) {
apply plugin: 'bytex'
ByteX {
enable true
enableInDebug true
logLevel "DEBUG"
}
}
if (!isAndroidTestBuild()) {
apply plugin: 'bytex.threadOpt'
thread_opt {
enable true
enableInDebug true
rxJavaIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
rxJavaComputationReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
coroutineIoReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getIoPool@@()Ljava/util/concurrent/ExecutorService;'
coroutineDefaultReplacer 'com/mogo/eagle/core/utilcode/util/ThreadUtils@@getCpuPool@@()Ljava/util/concurrent/ExecutorService;'
}
/**
* 方便使用systrace工具在工程侧打点便于分析工程侧性能问题
*/
apply plugin: 'bytex.systrace'
systrace {
/**
* 交付时要关闭,会有性能损耗
*/
enable false
enableInDebug false
/**
* - 是否使用[Trace.beginAsyncSection(String, int)/Trace.endAsyncSection(String, int)]进行打点
* - 默认使用[Trace.beginSection(String)/Trace.endSection()]进行打点
*/
isTraceAsync false
/**
* - 是否在运行时只针对主线程打点,其它线程不打
*/
isOnlyMainThread false
/**
* - 是否忽略对类的静态构造方法打点
* - 默认不忽略
*/
isIgnoreClinitMethod false
/**
* - 是否忽略对类中的简单方法打点
* 简单方法定义:
* - 空方法
* - get/set 方法
* - 单独的方法,方法体内没有调用其它方法
* - 默认不忽略
*/
isIgnoreSampleMethod false
/**
* - 针对特定类集合,配置打点白名单,在此集合中的类中的所有方法不打点
* - 支持正则表达式
*/
whiteListForClass = []
/**
* - 针对特定包名集合,配置打点白名单,所有类以此包名为前缀的类不打点
* - 支持正则表达式
*/
whiteListForPackage = []
}
}
/*apply plugin: 'chain.log.hook'
hooklog{
enableLoggerToServer true
@@ -87,6 +155,10 @@ android {
targetCompatibility 1.8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
@@ -140,6 +212,10 @@ android {
packagingOptions {
exclude 'META-INF/io.netty.versions.properties'
}
useLibrary 'android.test.runner'
useLibrary 'android.test.base'
useLibrary 'android.test.mock'
}
repositories {
@@ -152,29 +228,7 @@ aspectjx {
include "com.mogo.chat"
}
ApmPlugin {
// 是否进行插桩
enable true
// 是否在Debug包插桩默认不插桩
enableInDebug true
// DEBUG("DEBUG"), INFO("INFO"), WARN("WARN"), ERROR("ERROR");
// INFO 级别Log会汇总所有被插桩处理的类供查看路径 app/build/ByteX/ApmPlugin
logLevel "DEBUG"
// 启动分析开关监控App启动耗时需要同时开启pageLoadSwitch
startSwitch = true
// 页面响应开关监控Activity的生命周期耗时
pageLoadSwitch = true
// 网络监控开关监控okhttp3的网络请求
okHttp3Switch = true
// 白名单下的包进行插桩,需要填写要插装类所在的包名,支持前缀配置
whiteList = [
"com.mogo"
]
// 黑名单包下类不进行插桩,可以配置包名和类名,没有可以填空
blackList = [
]
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
@@ -184,9 +238,6 @@ dependencies {
implementation rootProject.ext.dependencies.arouter
implementation rootProject.ext.dependencies.boostmultidex
implementation 'com.volcengine:apm_insight:1.4.4.cn'
implementation 'com.volcengine:apm_insight_crash:1.4.2'
compileOnly rootProject.ext.dependencies.adasapi
compileOnly rootProject.ext.dependencies.adasconfigapi
@@ -202,21 +253,39 @@ dependencies {
apply from: "./functions/tts.gradle"
apply from: "./functions/och.gradle"
// implementation group: "com.tencent.matrix", name: "matrix-android-lib", version: '0.6.6', changing: true
// implementation group: "com.tencent.matrix", name: "matrix-android-commons", version: '0.6.6', changing: true
// implementation group: "com.tencent.matrix", name: "matrix-trace-canary", version:'0.6.6', changing: true
// implementation group: "com.tencent.matrix", name: "matrix-io-canary", version: '0.6.6', changing: true
androidTestImplementation rootProject.ext.dependencies.androidx_test_core
androidTestImplementation rootProject.ext.dependencies.androidx_test_core_ktx
androidTestImplementation rootProject.ext.dependencies.androidx_unit_ext
androidTestImplementation rootProject.ext.dependencies.androidx_unit_ext_ktx
androidTestImplementation rootProject.ext.dependencies.androidx_runner
androidTestImplementation rootProject.ext.dependencies.androidx_espresso_core
}
//apply plugin: 'com.tencent.matrix-plugin'
//matrix {
// trace {
// enable = true //if you don't want to use trace canary, set false
// baseMethodMapFile = "${project.projectDir}/matrixOutput/Debug.methodmap"
// blackListFile = "${project.projectDir}/matrixTrace/blackMethodList.txt"
// }
//}
if (!isAndroidTestBuild()) {
ApmPlugin {
// 是否进行插桩
enable true
// 是否在Debug包插桩默认不插桩
enableInDebug true
// DEBUG("DEBUG"), INFO("INFO"), WARN("WARN"), ERROR("ERROR");
// INFO 级别Log会汇总所有被插桩处理的类供查看路径 app/build/ByteX/ApmPlugin
logLevel "DEBUG"
// 启动分析开关监控App启动耗时需要同时开启pageLoadSwitch
startSwitch = true
// 页面响应开关监控Activity的生命周期耗时
pageLoadSwitch = true
// 网络监控开关监控okhttp3的网络请求
okHttp3Switch = true
// 白名单下的包进行插桩,需要填写要插装类所在的包名,支持前缀配置
whiteList = [
"com.mogo"
]
// 黑名单包下类不进行插桩,可以配置包名和类名,没有可以填空
blackList = [
]
}
}
android.applicationVariants.all { variant ->
def buildTime = new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08:00"))
@@ -253,10 +322,20 @@ def getWorkingBranchName() {
*/
def getWorkingBranchHash() {
def workingBranchHash = ""
def proc = "git log -n1 --format=format:\"%h\"".execute()
def proc = "git log -n1 --format=format:%h".execute()
proc.in.eachLine { line -> workingBranchHash = line }
proc.err.eachLine { line -> println line }
proc.waitFor()
println "Working branch hash: " + workingBranchHash
return workingBranchHash
return "\"${workingBranchHash}\""
}
boolean isAndroidTestBuild() {
for (String s : gradle.startParameter.taskNames) {
if (s.contains("AndroidTest")) {
return true
}
}
return false
}

View File

@@ -0,0 +1,145 @@
package com.mogo.functions.test
import androidx.lifecycle.lifecycleScope
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.function.hmi.ui.MoGoHmiFragment
import com.mogo.eagle.core.function.main.MainLauncherActivity
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@LargeTest
class AutoPilotBadCaseTest {
lateinit var launch: ActivityScenario<MainLauncherActivity>
@Before
fun launch() {
launch = ActivityScenario.launch(MainLauncherActivity::class.java)
}
@ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance1(): Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow()
var index = 0
(1 until 50)
.map { it }
.asFlow()
.onEach {
delay(TimeUnit.SECONDS.toMillis(5))
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index
it.key = "yyy_$index"
it.stat = 100
it.type = 1
it.timestamp = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())
index++
})
}
.flowOn(Dispatchers.Default)
.collect()
delay(TimeUnit.HOURS.toMillis(2))
}
@ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance2():Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow()
var index = 0
(1 until 50)
.map { it }
.asFlow()
.onEach {
if (index in 1..4) {
delay(TimeUnit.SECONDS.toMillis(15))
} else {
delay(Random(20).nextLong())
}
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index
it.key = "yyy_$index"
it.stat = 100
it.type = 1
it.timestamp = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())
index++
})
if (index == 5) {
f.lifecycleScope.cancel()
}
}
.flowOn(Dispatchers.Default)
.collect()
delay(TimeUnit.HOURS.toMillis(2))
}
@ExperimentalCoroutinesApi @Test
fun showBadCaseEntrance3(): Unit = runBlocking(Dispatchers.Main) {
val f = ensureMoGoHmiFragmentShow()
var index = 0
(1 until 50)
.map { it }
.asFlow()
.onEach {
delay(TimeUnit.SECONDS.toMillis(20))
f.onAutopilotRecordResult(AutoPilotRecordResult().also {
it.diskFree = 100 + index
it.duration = 60.0
it.fileName = "/user/general/record_$index.log"
it.id = 10 + index
it.key = "yyy_$index"
it.stat = 100
it.type = 1
it.timestamp = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Date())
index++
})
}
.flowOn(Dispatchers.Default)
.collect()
delay(TimeUnit.HOURS.toMillis(2))
}
private suspend fun ensureMoGoHmiFragmentShow(): MoGoHmiFragment = suspendCancellableCoroutine {
launch.onActivity { itx ->
val executor = Executors.newSingleThreadScheduledExecutor()
executor.scheduleAtFixedRate({
var find =
itx.supportFragmentManager.fragments.find { it is MoGoHmiFragment } as? MoGoHmiFragment
while (find == null) {
find =
itx.supportFragmentManager.fragments.find { it is MoGoHmiFragment } as? MoGoHmiFragment
}
while (!find.isResumed) {
Thread.sleep(500)
}
it.resumeWith(Result.success(find))
try {
Thread.sleep(500)
executor.shutdownNow()
} catch (e: Throwable) {
e.printStackTrace()
}
}, 50, 500, TimeUnit.MILLISECONDS)
}
}
}

View File

@@ -1,29 +1,16 @@
package com.mogo.launcher;
import com.apm.insight.AttachUserData;
import com.apm.insight.CrashType;
import com.apm.insight.MonitorCrash;
import com.apm.insight.log.VLog;
import com.auto.zhidao.logsdk.CrashSystem;
import com.bytedance.apm.insight.ApmInsight;
import com.bytedance.apm.insight.ApmInsightInitConfig;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.eagle.core.data.app.AppConfigInfo;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.config.HdMapBuildConfig;
import com.mogo.eagle.core.data.config.HmiBuildConfig;
import com.mogo.eagle.core.function.main.MainMoGoApplication;
import com.mogo.module.v2x.utils.ObuConfig;
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr;
import com.mogo.test.crashreport.CrashReportConstants;
import com.mogo.utils.AppUtils;
import com.mogo.utils.CommonUtils;
import com.mogo.utils.logger.LogLevel;
import com.mogo.utils.logger.Logger;
import com.mogo.utils.storage.SharedPrefsMgr;
import java.util.HashMap;
import java.util.Map;
/**
* @author congtaowang
@@ -32,14 +19,12 @@ import java.util.Map;
* Launcher application
*/
public class MogoApplication extends MainMoGoApplication {
private static final String BYTEAMP_APPID = "302368";
@Override
protected void initCrashConfig() {
CrashSystem crashSystem = CrashSystem.getInstance(this);
crashSystem.init();
initCrash();
initApmInsight();
//设置debug模式日志不上传
crashSystem.setDebug(BuildConfig.DEBUG);
}
@@ -50,91 +35,6 @@ public class MogoApplication extends MainMoGoApplication {
Logger.init(BuildConfig.DEBUG ? LogLevel.DEBUG : LogLevel.OFF);
}
private void initCrash() {
MonitorCrash crash = MonitorCrash.init(this, BYTEAMP_APPID, CommonUtils.getVersionCode(this), CommonUtils.getVersionName(this))
.setCustomDataCallback(new AttachUserData() {
@Override
public Map<? extends String, ? extends String> getUserData(CrashType type) {
HashMap<String, String> map = new HashMap<>();
// map.put("app_custom", "app_value");
//SN
map.put("serial", MoGoAiCloudClientConfig.getInstance().getSn());
//地图版本
String mapSDKVersion = AppUtils.getCustomMapSDKVersion(getApplicationContext());
map.put("MAP_SDK_VERSION", mapSDKVersion);
return map;
}
});
crash.config().setChannel("eagle");
// crash.config().setDeviceId("did");//可选可以设置自定义did,不设置会使用内部默认的
// crash.setReportUrl("www.xxx.com"); // 私有化部署:私有化部署才配置上报地址
// crash.addTags("key", "value"); // 自定义筛选tag, 按需添加、可多次覆盖
}
/**
* ApmInsight性能监控初始化
*/
private void initApmInsight() {
ApmInsightInitConfig.Builder builder = ApmInsightInitConfig.builder();
//设置分配的appid
builder.aid(BYTEAMP_APPID);
//是否开启卡顿功能
builder.blockDetect(true);
//是否开启严重卡顿功能
builder.seriousBlockDetect(true);
//是否开启流畅性和丢帧
builder.fpsMonitor(true);
//控制是否打开WebVeiw监控
builder.enableWebViewMonitor(true);
//控制是否打开内存监控
builder.memoryMonitor(true);
//控制是否打开电量监控
builder.batteryMonitor(true);
//是否打印日志线上release版本要配置为false
builder.debugMode(true);
//支持用户自定义user_id把平台数据和自己用户关联起来可以不配置
// builder.userId("user_id");
//私有化部署:配置数据上报的域名 私有化部署才需要配置内部有默认域名测试支持设置http://www.xxx.com 默认是https协议
// builder.defaultReportDomain("www.xxx.com");
//设置渠道。1.3.16版本增加接口
builder.channel("local");
//打开自定义日志回捞能力1.4.1版本新增接口
builder.enableLogRecovery(true);
//设置数据和Rangers Applog数据打通设备标识did必填。1.3.16版本增加接口
// builder.setDynamicParams(new IDynamicParams() {
// @Override
// public String getUserUniqueID() {
// //可选。依赖AppLog可以通过AppLog.getUserUniqueID()获取否则可以返回null。
// return null;
// }
//
// @Override
// public String getAbSdkVersion() {
// //可选。如果依赖AppLog可以通过AppLog.getAbSdkVersion()获取否则可以返回null。
// return null;
// }
//
// @Override
// public String getSsid() {
// //可选。依赖AppLog可以通过AppLog.getSsid()获取否则可以返回null。
// return null;
// }
//
// @Override
// public String getDid() {
// //1.4.0版本及以上可选其他版本必填。设备的唯一标识如果依赖AppLog可以通过 AppLog.getDid() 获取,也可以自己生成。
// return AppLog.getDid();
// }
// });
ApmInsight.getInstance().init(this, builder.build());
//初始化自定日志配置自定义日志最大占用磁盘内部一般配置20,代表最大20M磁盘占用。1.4.1版本开始存在这个api
VLog.init(this, 20);
}
@Override
protected void initCloudClientConfig() {
// todo 使用旧Socket链路 true = 旧链路false = 新链路
@@ -165,7 +65,6 @@ public class MogoApplication extends MainMoGoApplication {
AppConfigInfo.INSTANCE.setWorkingBranchName(BuildConfig.WORKING_BRANCH_NAME);
AppConfigInfo.INSTANCE.setWorkingBranchHash(BuildConfig.WORKING_BRANCH_HASH);
ObuConfig.useObuLocation = false;
// 使用与渠道配置一样的gps提供者提供的数据,app/productFlavors/fPadLenovo.gradle GPS_PROVIDER 0-Android系统1-工控机2-OBU
FunctionBuildConfig.gpsProvider = BuildConfig.GPS_PROVIDER;
// 配置BuglyAppID

View File

@@ -4,6 +4,7 @@ apply from: "javadoc.gradle"
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
maven { url "https://artifact.bytedance.com/repository/byteX/" }
maven { url 'http://nexus.zhidaoauto.com/repository/maven-releases/' }
@@ -32,6 +33,9 @@ buildscript {
classpath "com.bytedance.android.byteX:base-plugin:0.3.0"
classpath "com.mogo.cloud:hook:${HOOK_LOG_VERSION}"
classpath 'com.volcengine:apm_insight_plugin:1.4.1'
classpath 'com.mogo.cloud:thread_opt:1.0.0'
classpath 'com.mogo.cloud:systrace:1.0.1'
// classpath "com.bytedance.android.byteX:base-plugin:0.3.0"
// classpath "com.mogo.cloud:hook:${HOOK_LOG_VERSION}"

View File

@@ -114,7 +114,7 @@ ext {
obusdk : "com.zhidao.enterprise.smartv2x:smartv2x:1.0.0.3",
mogoobu : 'com.zhidao.support.obu:mogoobu:1.0.0.19',
mogoami : 'com.zhidao.support.obu.ami:mogoami:1.0.0.10',
adasHigh : 'com.zhidao.support.adas:high:1.2.1.2',
adasHigh : 'com.zhidao.support.adas:high:1.2.1.2_bate11',
// google
googlezxing : "com.google.zxing:core:3.3.3",
@@ -195,6 +195,9 @@ ext {
skinsupportcardview : "com.mogo.skin:skin-support-cardview:${SKIN_SUPPORT_CARDVIEW_VERSION}",
skinsupportconstraintlayout : "com.mogo.skin:skin-support-constraint-layout:${SKIN_SUPPORT_CONSTRAINT_LAYOUT_VERSION}",
skinsupportdesign : "com.mogo.skin:skin-support-design:${SKIN_SUPPORT_DESIGN_VERSION}",
apm_insight : 'com.volcengine:apm_insight:1.4.4.cn',
apm_insight_crash : 'com.volcengine:apm_insight_crash:1.4.2',
//========================= TTS语音 Maven 版本管理 =========================
ttsbase : "com.mogo.tts:tts-base:${TTS_BASE_VERSION}",
ttsdi : "com.mogo.tts:tts-di:${TTS_DI_VERSION}",
@@ -238,6 +241,21 @@ ext {
mogo_core_utils : "com.mogo.eagle.core:utils:${MOGO_CORE_UTILS_VERSION}",
mogo_core_network : "com.mogo.eagle.core:network:${MOGO_CORE_NETWORK_VERSION}",
//========================= V2X SDK =========================
mogo_v2x : "com.mogo.v2x:v2x:${MOGO_V2X_SDK_VERSION}",
life_cycle_scope : "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0",
view_model_scope : "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0",
live_data_scope : "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0",
//========================== Unit Test ======================
androidx_test_core : "androidx.test:core:1.2.1",
androidx_test_core_ktx : "androidx.test:core-ktx:1.2.0",
androidx_unit_ext : "androidx.test.ext:junit:1.1.2",
androidx_unit_ext_ktx : "androidx.test.ext:junit-ktx:1.1.2",
androidx_runner : "androidx.test:runner:1.3.0",
androidx_espresso_core : "androidx.test.espresso:espresso-core:3.3.0",
]
}

View File

@@ -2,13 +2,16 @@
本模块用来编写鹰眼核型功能
- function-impl 目录下编写的都是对mogo-core-function-api定的功能实现,
- mogo-core-function-check 程序及车辆检测模块
- mogo-core-function-hmi UI呈现及交互模块
- mogo-core-function-map 地图相关的模块
- mogo-core-function-notice 云端公告、调度相关模块
- mogo-core-function-obu-mogo 自研OBU预警模块
- mogo-core-function-smp 小地图模块
- mogo-core-function-v2x 自车+云端预警模块
- mogo-core-function-check 程序及车辆检测模块
- mogo-core-function-devatools 开发工具模块
- mogo-core-function-hmi UI呈现及交互模块
- mogo-core-function-main 主入口
- mogo-core-function-map 地图相关的模块
- mogo-core-function-monitoring 远距离监控查看,路测摄像头、前车直播
- mogo-core-function-notice 云端公告、调度相关模块
- mogo-core-function-obu-mogo 自研OBU预警模块
- mogo-core-function-smp 小地图模块
- mogo-core-function-v2x 自车+云端预警模块
- mogo-core-data定义基础业务所需要的数据结构

View File

@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit
*/
@Route(path = MogoServicePaths.PATH_AUTO_PILOT)
class MoGoAutopilotProvider :
IMoGoAutopilotProvider {
IMoGoAutopilotProvider {
private val TAG = "MoGoAutoPilotProvider"
override val functionName: String
@@ -82,7 +82,8 @@ class MoGoAutopilotProvider :
}
override fun recordPackage(): Boolean {
return AdasManager.getInstance().recordPackage(1, (System.currentTimeMillis() / 1000).toInt())
return AdasManager.getInstance()
.recordPackage(1, (System.currentTimeMillis() / 1000).toInt())
}
override fun setEnableLog(isEnableLog: Boolean) {
@@ -100,4 +101,16 @@ class MoGoAutopilotProvider :
override fun setAutoPilotSpeed(speed: Int): Boolean {
return AdasManager.getInstance().setSpeed(speed)
}
override fun setIPCShutDown() {
AdasManager.getInstance().shutdownIPC()
}
override fun setIPCReboot() {
AdasManager.getInstance().rebootIPC()
}
override fun recordCause(key: String?, name: String?, id: String?, reason: String?) {
AdasManager.getInstance().recordCause(key, name, id, reason)
}
}

View File

@@ -12,6 +12,7 @@ import com.mogo.eagle.core.function.check.net.CheckNetWork.checkNetWork
import com.mogo.eagle.core.function.check.net.CheckResultData
import com.mogo.eagle.core.function.check.view.CheckActivity
import com.mogo.eagle.core.function.check.view.CheckDialog
import com.mogo.eagle.core.utilcode.util.ActivityLifecycleManager
import com.mogo.eagle.core.utilcode.util.ActivityUtils
import com.mogo.eagle.core.utilcode.util.AppUtils
import com.mogo.eagle.core.utilcode.util.LogUtils
@@ -19,7 +20,6 @@ import com.mogo.module.common.MogoApisHandler
import com.mogo.module.service.receiver.MogoReceiver
import com.mogo.service.statusmanager.IMogoStatusChangedListener
import com.mogo.service.statusmanager.StatusDescriptor
import com.mogo.utils.ActivityLifecycleManager
import java.util.concurrent.ConcurrentHashMap
/**

View File

@@ -9,13 +9,13 @@ import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsListenerManager
import com.mogo.eagle.core.network.NetConfig
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.TimeUtils
import com.mogo.module.common.MogoApisHandler
import com.mogo.service.cloud.socket.IMogoOnMessageListener
import com.mogo.utils.logger.LogLevel
import com.mogo.utils.logger.Logger
import com.zhidao.loglib.bean.RemoteLogPushContent
import com.zhidao.loglib.call.LogInfoManagerFactory
import com.zhidao.loglib.core.ILogListener
@@ -47,7 +47,7 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
MogoApisHandler.getInstance().apis
.getSocketManagerApi(AbsMogoApplication.getApp().applicationContext)
.registerOnMessageListener(LOG_PUSH_TYPE, this)
manualContent.duration = 60
manualContent.duration = 10
manualContent.pkgName = context.packageName
}
@@ -98,19 +98,23 @@ object MogoLogCatchManager : IMogoOnMessageListener<RemoteLogPushContent>, Handl
private fun startCatchLog(content: RemoteLogPushContent) {
catchingList.add(content.pkgName)
var delay = (content.duration * 60 * 1000).toLong()
var delay = (content.duration).toLong()
handler.removeMessages(MSG_TRY_CLOSE_LOG)
if (delay <= 0) {
// 如果push 下来的delay小于等于0那就给个默认最大值一小时
delay = 60 * 60 * 1000L
delay = 10
}
handler.sendEmptyMessageDelayed(MSG_TRY_CLOSE_LOG, delay)
handler.sendEmptyMessageDelayed(MSG_TRY_CLOSE_LOG, delay * 1000L * 60)
openLoggerLevel()
logInfoManager = LogInfoManagerFactory.createPushLogInfoManager(
mContext,
MoGoAiCloudClientConfig.getInstance().sn + File.separator + TimeUtils.formatYMD(System.currentTimeMillis()),
content, this)
content, this
)
logInfoManager?.start()
logInfoManager?.registerLogOutListener { lineLog ->
CallerDevaToolsListenerManager.invokeDevaToolsLogCatchLines(lineLog)
}
}
private fun stopCatchLog(content: RemoteLogPushContent) {

View File

@@ -36,5 +36,13 @@
</intent-filter>
</receiver>
<!--转向灯和刹车-->
<receiver android:name="com.mogo.eagle.core.function.hmi.receiver.TurnLightBroadcastReceiver">
<intent-filter>
<action android:name="com.hmi.turnlight" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -5,8 +5,8 @@ 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.IMoGoWarningStatusListener
import com.mogo.utils.WindowUtils
import com.mogo.utils.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.WindowUtils
/**
* @author donghongyu
@@ -147,6 +147,14 @@ class WarningFloat {
}
}
fun setWindowWidth(width: Int) = apply {
this.config.width = width
}
fun setWindowHeight(height: Int) = apply {
this.config.height = height
}
/**
* 创建浮窗包括Activity浮窗和系统浮窗如若系统浮窗无权限先进行权限申请
*/

View File

@@ -12,12 +12,11 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import com.mogo.eagle.core.function.hmi.R
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
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.WindowUtils
/**
* @author donghongyu
@@ -66,8 +65,8 @@ internal class WarningFloatWindowHelper(
// 没有边界限制,允许窗口扩展到屏幕外
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
else WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
width = config.width
height = config.height
// 如若设置了固定坐标,直接定位
if (config.locationPair != Pair(0, 0)) {

View File

@@ -1,6 +1,7 @@
package com.mogo.eagle.core.function.hmi.notification
import android.view.View
import android.view.WindowManager
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
@@ -59,4 +60,9 @@ data class WarningNotificationConfig(
// Callbacks
var callbacks: OnFloatCallbacks? = null,
// 窗口宽度
var width: Int = WindowManager.LayoutParams.WRAP_CONTENT,
// 窗口高度
var height: Int = WindowManager.LayoutParams.WRAP_CONTENT
)

View File

@@ -7,7 +7,7 @@ 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 com.mogo.eagle.core.utilcode.util.WindowUtils
import kotlin.math.min
/**

View File

@@ -4,15 +4,11 @@ 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.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.utilcode.util.SharedPrefs
import com.mogo.utils.logger.Logger
/**
* 用于普通云公告的测试

View File

@@ -0,0 +1,50 @@
package com.mogo.eagle.core.function.hmi.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager.showBrakeLight
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager.showTurnLight
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.SharedPrefs
import com.mogo.service.IMogoServiceApis
/**
* 转向灯,刹车
*
* @author lixiaopeng
*/
class TurnLightBroadcastReceiver : BroadcastReceiver() {
companion object {
private const val TAG = "TurnLightBroadcastReceiver"
}
override fun onReceive(context: Context, intent: Intent) {
try {
val type = intent.getIntExtra("type", 0)
val lightInt = intent.getIntExtra("light", 0)
showTurnLight(type, lightInt)
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun showTurnLight( //type 1,转向灯2刹车
type: Int,
lightInt: Int
) {
if (type == 1) {
showTurnLight(lightInt) //设置转向灯
} else if (type == 2) {
showBrakeLight(lightInt) //设置刹车信息
}
}
}

View File

@@ -7,8 +7,8 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.service.IMogoServiceApis
import com.mogo.utils.logger.Logger
/**
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式 控制限速标志

View File

@@ -7,8 +7,8 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.service.IMogoServiceApis
import com.mogo.utils.logger.Logger
/**
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式 触发红绿灯场景

View File

@@ -7,9 +7,9 @@ import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.function.hmi.WaringConst
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.module.common.enums.EventTypeEnum
import com.mogo.service.IMogoServiceApis
import com.mogo.utils.logger.Logger
/**
* V2X 预警广播接收。用于跨应用,跨进程,内部也可以通过这种方式弹出预警提示框

View File

@@ -1,23 +1,30 @@
package com.mogo.eagle.core.function.hmi.ui
import android.animation.Animator
import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.WindowManager
import android.view.*
import android.view.animation.OvershootInterpolator
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.lifecycleScope
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.mvp.MvpFragment
import com.mogo.commons.voice.AIAssist
import com.mogo.eagle.core.data.autopilot.AutoPilotRecordResult
import com.mogo.eagle.core.data.camera.CameraEntity
import com.mogo.eagle.core.data.config.HmiBuildConfig
import com.mogo.eagle.core.data.constants.MoGoFragmentPaths
import com.mogo.eagle.core.data.enums.WarningDirectionEnum
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotIdentifyListener
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWaringProvider
import com.mogo.eagle.core.function.api.hmi.warning.IMoGoWarningStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotIdentifyListenerManager
import com.mogo.eagle.core.function.call.check.CallerCheckManager
import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager
import com.mogo.eagle.core.function.hmi.R
@@ -30,12 +37,21 @@ import com.mogo.eagle.core.function.hmi.ui.notice.NoticeBannerView
import com.mogo.eagle.core.function.hmi.ui.notice.NoticeNormalBannerView
import com.mogo.eagle.core.function.hmi.ui.setting.DebugSettingView
import com.mogo.eagle.core.function.hmi.ui.tools.AutoPilotAndCheckView
import com.mogo.eagle.core.function.hmi.ui.tools.AutoPilotBadCaseView
import com.mogo.eagle.core.function.hmi.ui.tools.post
import com.mogo.eagle.core.function.hmi.ui.widget.V2XNotificationView
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.module.common.enums.EventTypeEnum
import com.mogo.utils.logger.Logger
import kotlinx.android.synthetic.main.fragment_hmi.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
/**
* @author xiaoyuzhou
@@ -45,7 +61,7 @@ import kotlinx.android.synthetic.main.fragment_hmi.*
@Route(path = MoGoFragmentPaths.PATH_FRAGMENT_HMI)
class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>(),
IMoGoWaringProvider,
MoGoWarningContract.View {
MoGoWarningContract.View, IMoGoAutopilotIdentifyListener {
private val TAG = "MoGoHmiFragment"
// DebugSettingView
@@ -64,9 +80,107 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
private var toolsView: AutoPilotAndCheckView? = null
private var autoPilotToolsFloat: WarningFloat.Builder? = null
// 检测、自动驾驶速度设置
private var toolsViewFloat: WarningFloat.Builder? = null
@Volatile
private var autoPilotBadCaseEntrance: View? = null
private var autoPilotBadCaseView: AutoPilotBadCaseView? = null
companion object {
private const val MSG_WHAT_DISMISS_BAD_CASE_ENTRY = 0x1010
private val DURATION_FOR_DISMISS = TimeUnit.HOURS.toMillis(4)
}
@ExperimentalCoroutinesApi
private val channel by lazy {
Channel<AutoPilotRecordResult?>(UNLIMITED).also {
lifecycleScope.launchWhenResumed {
withContext(Dispatchers.Default) {
while (!it.isClosedForReceive) {
try {
val entrance = autoPilotBadCaseEntrance
val old = entrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
if (entrance == null || old == null || old.consumed) {
Logger.d("QQQ", "-- step -- 1 --")
var oldT = try {
old?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).parse(it)?.time ?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
var record: AutoPilotRecordResult? = null
var newT = try {
it.receive()?.also { record = it }?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).parse(it)?.time
?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
if (oldT == 0L || (newT > 0L && (newT - oldT > 0L) && (newT - oldT) < DURATION_FOR_DISMISS)) {
Logger.d("QQQ", "-- step -- 2 --")
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Logger.d("QQQ", "record: [$record] is displaying and consuming ~~~" )
showBadCaseEntrance(it)
}
continue
}
while (oldT != 0L && newT != 0L && (newT - oldT) >= DURATION_FOR_DISMISS) {
Logger.d("QQQ", "record: [$record] has been discarded, because it has been timeout." )
oldT = newT
newT = try {
it.receive()?.also {
record = it
}?.timestamp?.takeIf { it.isNotBlank() }?.let {
SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).parse(it)?.time ?: 0L
} ?: 0L
} catch (t: Throwable) {
t.printStackTrace()
0L
}
}
record?.takeIf { it.key != old?.key && it.timestamp != old?.timestamp }?.also {
Logger.d("QQQ", "record: [$record] is displaying for rest ..." )
showBadCaseEntrance(it)
}
} else {
Logger.d("QQQ", "record: [$old] hasn't been consumed~~~~" )
}
} finally {
delay(1000)
}
}
}
}
}
}
private val handler by lazy {
Handler(Handler.Callback {
if (it.what == MSG_WHAT_DISMISS_BAD_CASE_ENTRY) {
val entrance = autoPilotBadCaseEntrance
if (entrance != null && entrance.visibility == View.VISIBLE) {
Logger.d(TAG, "${DURATION_FOR_DISMISS}毫秒后BadCase入口消失")
(entrance.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult)?.let { itx ->
itx.consumed = true
}
entrance.visibility = View.GONE
}
return@Callback true
}
return@Callback false
})
}
override fun vipIdentification(visible: Boolean) {
ThreadUtils.runOnUiThread {
Logger.d(TAG, "vipIdentification")
@@ -93,50 +207,156 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
viewShowDebugView.setOnLongClickListener {
Log.d(TAG, "长按显示状态工具栏")
context?.let {
if (mDebugSettingViewFloat != null) {
WarningFloat.dismiss(mDebugSettingViewFloat!!.config.floatTag, false)
mDebugSettingViewFloat = null
mDebugSettingView = null
} else {
if (mDebugSettingView == null) {
mDebugSettingView = DebugSettingView(it)
}
mDebugSettingViewFloat = WarningFloat.with(it)
.setTag("DebugSettingView")
.setLayout(mDebugSettingView!!)
.setSidePattern(SidePattern.RIGHT)
.setGravity(Gravity.RIGHT, offsetY = 70)
.setImmersionStatusBar(true)
.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()
}
}
toggleDebugView()
true
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
CallerAutopilotIdentifyListenerManager.addListener(TAG, this)
}
@ExperimentalCoroutinesApi
override fun onAutopilotRecordResult(record: AutoPilotRecordResult?) {
record ?: return
Logger.d("QQQ", "onAutopilotRecordResult:$record")
if (record.type == 1 && record.stat == 100) {
lifecycleScope.launchWhenResumed {
channel.send(record)
}
}
}
override fun onDestroyView() {
super.onDestroyView()
CallerAutopilotIdentifyListenerManager.removeListener(TAG)
}
@VisibleForTesting
fun showBadCaseEntrance(record: AutoPilotRecordResult) {
Logger.d("QQQ", "showBadCaseEntrance:$record")
lifecycleScope.launch {
if (vs_bad_case_entrance?.parent != null) {
val inflateView = vs_bad_case_entrance.inflate()
autoPilotBadCaseEntrance = inflateView
}
val entrance = autoPilotBadCaseEntrance
if (entrance != null) {
if (entrance.visibility != View.VISIBLE) {
entrance.visibility = View.VISIBLE
}
entrance.setTag(R.id.autopilot_badcase_record, record)
entrance.onClick {
showBadCasesFloat {
it.visibility = View.GONE
}
}
dismissBadCaseEntryAfterSomeTime()
}
}
}
private fun showBadCasesFloat(dismiss: (() -> Unit)?) {
Logger.d("QQQ", "showBadCaseToolsFloat")
context?.let { it ->
if (autoPilotToolsFloat == null) {
if (autoPilotBadCaseView == null) {
autoPilotBadCaseView = AutoPilotBadCaseView(it).also { itx ->
itx.onDismiss {
dismissBadCaseFloatView()
}
itx.onSelect {
lifecycleScope.launch {
val record =
autoPilotBadCaseEntrance?.getTag(R.id.autopilot_badcase_record) as? AutoPilotRecordResult
try {
val params = mutableMapOf<String, String>()
autoPilotBadCaseEntrance?.apply {
params["carLicense"] =
MoGoAiCloudClientConfig.getInstance().sn
params["filename"] = record?.fileName ?: ""
params["filesize"] = record?.total.toString()
params["key"] = record?.key ?: ""
params["reason"] = it.reason ?: ""
params["duration"] = record?.duration?.toInt()?.toString() ?: ""
params["timestamp"] = record?.timestamp ?: ""
}
val response = post(params)
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
Logger.e("QQQ", "返回的body是空的~~~")
return@launch
}
if (body.code == 200) {
Logger.i(TAG, "ok:${body}")
dismissBadCaseFloatView()
dismiss?.invoke()
CallerAutoPilotManager.recordCause(
record?.key,
record?.fileName,
it.id, it.reason)
ToastUtils.showShort("接管反馈成功~")
record?.consumed = true
return@launch
}
Logger.e("QQQ", "fail:${body}")
}
} catch (t: Throwable) {
t.printStackTrace()
ToastUtils.showShort("网络请求失败,请尝试联网~")
Logger.e("QQQ", "exception:${t.message}")
}
}
}
}
}
autoPilotToolsFloat = WarningFloat.with(it)
.setTag("BadCaseCollectFloat")
.setLayout(autoPilotBadCaseView!!)
.setSidePattern(SidePattern.LEFT)
.setGravity(Gravity.LEFT, offsetY = 72)
.setImmersionStatusBar(true)
.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)
})
.addWarningStatusListener(object : IMoGoWarningStatusListener {
override fun onDismiss() {
autoPilotToolsFloat = null
autoPilotBadCaseView = null
}
})
.show()
} else {
autoPilotToolsFloat?.show()
}
}
}
private fun dismissBadCaseEntryAfterSomeTime() {
handler.removeMessages(MSG_WHAT_DISMISS_BAD_CASE_ENTRY)
handler.sendEmptyMessageDelayed(MSG_WHAT_DISMISS_BAD_CASE_ENTRY, DURATION_FOR_DISMISS)
}
private fun showToolsFloat() {
Logger.d(TAG, "showToolsFloat")
context?.let {
@@ -153,6 +373,10 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
override fun onClose(v: View) {
dismissToolsFloatView()
}
override fun showDebugPanelView() {
toggleDebugView()
}
})
}
toolsViewFloat = WarningFloat.with(it)
@@ -258,6 +482,52 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
ivToolsIcon?.visibility = visibility
}
/**
* 开关DebugView
*/
override fun toggleDebugView() {
Log.d(TAG, "长按显示状态工具栏")
context?.let {
if (mDebugSettingViewFloat != null) {
WarningFloat.dismiss(mDebugSettingViewFloat!!.config.floatTag, false)
mDebugSettingViewFloat = null
mDebugSettingView = null
} else {
if (mDebugSettingView == null) {
mDebugSettingView = DebugSettingView(it)
}
mDebugSettingViewFloat = WarningFloat.with(it)
.setTag("DebugSettingView")
.setLayout(mDebugSettingView!!)
.setSidePattern(SidePattern.RIGHT)
.setGravity(Gravity.RIGHT, offsetY = 70)
.setImmersionStatusBar(true)
.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()
}
}
}
/**
* 展示VR下V2X预警
*
@@ -635,9 +905,78 @@ class MoGoHmiFragment : MvpFragment<MoGoWarningContract.View?, WaringPresenter?>
}
}
/**
* 显示转向灯效果
*/
override fun showTurnLight(light: Int) {
// turnLightView.setTurnLight(light)
}
/**
* 显示刹车效果
*/
override fun showBrakeLight(light: Int) {
// brakeView.setBrakeLight(light)
}
/**
* 展示工控机下载状态信息
* @param downloadVersion 下载版本
* @param downloadStatus 下载状态(0:下载完成1:正在下载2:下载失败)
* @param downloadProgress 下载进度
*/
override fun showAdDownloadStatus(
downloadVersion: String,
downloadStatus: Int,
downloadProgress: Int
) {
// if (downloadProgress>0){
// //新版工控机包处于下载中或已下载完成状态,展示工具箱提示角标
// viewUpgradeTips.visibility = View.VISIBLE
// }
if(downloadStatus==0){
//新版本工控机包下载完成,处于可升级状态,展示工具箱提示角标
viewUpgradeTips.visibility = View.VISIBLE
}else if(downloadStatus==1){
//新版本工控机包正在下载中,展示工具箱提示角标
viewUpgradeTips.visibility = View.VISIBLE
}else if(downloadStatus==2){
//新版本工控机包下载失败,隐藏工具箱提交角标
viewUpgradeTips.visibility = View.GONE
}
}
/**
* 展示工控机升级状态信息
* @param upgradeStatus 升级状态true代表升级成功、false代表升级不成功
*/
override fun showAdUpgradeStatus(upgradeStatus: Boolean) {
if(upgradeStatus){
//工控机升级成功,隐藏工具箱提示角标
viewUpgradeTips.visibility = View.GONE
}else{
//工控机升级失败,展示工具箱提示角标
viewUpgradeTips.visibility = View.VISIBLE
}
//TODO 给工具箱空间传递状态
// toolsView?.setStatus(true)
}
private fun dismissBadCaseFloatView() {
autoPilotToolsFloat?.let {
WarningFloat.dismiss(it.config.floatTag, false)
autoPilotToolsFloat = null
autoPilotBadCaseView = null
}
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy")
handler.removeCallbacksAndMessages(null)
}

View File

@@ -23,8 +23,8 @@ import com.mogo.cloud.trafficlive.api.MoGoAiCloudTrafficLive
import com.mogo.eagle.core.data.camera.CameraEntity
import com.mogo.eagle.core.function.call.monitor.CallerMonitorManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.widget.media.video.SimpleVideoPlayer
import com.mogo.utils.logger.Logger
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.model.VideoOptionModel

View File

@@ -0,0 +1,496 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.graphics.PixelFormat
import android.util.Log
import android.view.*
import android.widget.FrameLayout
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.core.view.GravityCompat
import com.mogo.commons.context.ContextHolderUtil
import com.mogo.eagle.core.utilcode.util.ScreenUtils
import com.mogo.eagle.core.utilcode.util.Utils
abstract class AbsLogView : ILogView, TouchProxy.OnTouchEventListener {
class ViewArgs {
var edgePinned = false
}
private val mInnerReceiver = InnerReceiver()
@JvmField
protected var mWindowManager =
ContextHolderUtil.getContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager
/**
* 创建FrameLayout#LayoutParams 系统悬浮窗调用
*/
protected var systemLayoutParams: WindowManager.LayoutParams? = null
/**
* 整个悬浮窗的View
*/
private var mRootView: FrameLayout? = null
/**
* rootView的直接子View 一般是用户的xml布局 被添加到mRootView中
*/
private var mChildView: View? = null
val logView: View?
get() = mRootView
/**
* 手势代理
*/
@JvmField
var mTouchProxy = TouchProxy(this)
private val viewProps = ViewArgs()
/**
* 控件在布局边界发生大小变化被裁剪的原因
*/
val parentView: LogFrameLayout?
get() = if (mRootView != null) {
mRootView!!.parent as LogFrameLayout
} else null
fun show(context: Context) {
if (isShow) {
Log.d("EmArrow", "isShow : $isShow")
return
}
performCreate(context)
createView()
onResume()
}
fun dismiss() {
removeView()
performDestroy()
}
/**
* 执行floatPage create
*
* @param context 上下文环境
*/
@SuppressLint("ClickableViewAccessibility")
private fun performCreate(context: Context) {
try {
//调用onCreate方法
onCreate(context)
//系统悬浮窗的返回按键监听
mRootView = object : LogFrameLayout(context, LogFrameLayoutFlag_CHILD) {
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_UP && shouldDealBackKey()) {
//监听返回键
if (event.keyCode == KeyEvent.KEYCODE_BACK || event.keyCode == KeyEvent.KEYCODE_HOME) {
return onBackPressed()
}
}
return super.dispatchKeyEvent(event)
}
}
//添加根布局的layout回调
addViewTreeObserverListener()
//调用onCreateView抽象方法
mChildView = onCreateView(context, mRootView)
//将子View添加到rootView中
mRootView?.addView(mChildView)
//设置根布局的手势拦截
mRootView?.setOnTouchListener { v, event -> mTouchProxy.onTouchEvent(v, event) }
//调用onViewCreated回调
onViewCreated(mRootView)
mLogViewLayoutParams = LogViewLayoutParams()
//分别创建对应的LayoutParams
systemLayoutParams = WindowManager.LayoutParams()
//shouldDealBackKey : false 不自己收返回事件处理
if (shouldDealBackKey()) {
//自己处理返回按键
systemLayoutParams?.flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
mLogViewLayoutParams.flags =
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or LogViewLayoutParams.FLAG_LAYOUT_NO_LIMITS
} else {
//设置WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE会导致RootView监听不到返回按键的监听失效 系统处理返回按键
systemLayoutParams?.flags =
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
mLogViewLayoutParams.flags =
LogViewLayoutParams.FLAG_NOT_FOCUSABLE or LogViewLayoutParams.FLAG_LAYOUT_NO_LIMITS
}
systemLayoutParams?.apply {
format = PixelFormat.TRANSPARENT
gravity = GravityCompat.START or Gravity.TOP
}
mLogViewLayoutParams.gravity = GravityCompat.START or Gravity.TOP
//动态注册关闭系统弹窗的广播
val intentFilter = IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
context.registerReceiver(mInnerReceiver, intentFilter)
initViewLayoutParams(mLogViewLayoutParams)
onSystemLayoutParamsCreated()
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun createView() {
mWindowManager.addView(logView, systemLayoutParams)
}
override fun onResume() {
mRootView?.requestLayout()
}
private fun removeView() {
mWindowManager.removeView(logView)
}
private fun performDestroy() {
context?.unregisterReceiver(mInnerReceiver)
//移除布局监听
removeViewTreeObserverListener()
mRootView = null
onDestroy()
}
/**
* 用来保存rootView的LayoutParams
*/
private lateinit var mLogViewLayoutParams: LogViewLayoutParams
/**
* 上一次LogView的位置信息
*/
private val mLastLogViewPosInfo: LastLogViewPosInfo = LastLogViewPosInfo()
/**
* 根布局的实际宽
*/
private var mLogViewWidth = 0
/**
* 根布局的实际高
*/
private var mLogViewHeight = 0
private var mViewTreeObserver: ViewTreeObserver? = null
private val mOnGlobalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener =
ViewTreeObserver.OnGlobalLayoutListener {
//每次布局发生变动的时候重新赋值
mRootView?.let {
mLogViewWidth = it.measuredWidth
mLogViewHeight = it.measuredHeight
mLastLogViewPosInfo.logViewWidth = mLogViewWidth
mLastLogViewPosInfo.logViewHeight = mLogViewHeight
}
}
private fun addViewTreeObserverListener() {
if (mViewTreeObserver == null && mRootView != null) {
mViewTreeObserver = mRootView!!.viewTreeObserver
mViewTreeObserver?.addOnGlobalLayoutListener(mOnGlobalLayoutListener)
}
}
private fun removeViewTreeObserverListener() {
mViewTreeObserver?.let {
if (it.isAlive) {
it.removeOnGlobalLayoutListener(mOnGlobalLayoutListener)
}
}
}
override fun onDestroy() {
}
/**
* 确定系统浮标的初始位置
* LayoutParams创建完以后调用
* 调用时建议放在实现下方
*/
private fun onSystemLayoutParamsCreated() {
//如果有上一个页面的位置记录 这更新位置
systemLayoutParams?.flags = mLogViewLayoutParams.flags
systemLayoutParams?.gravity = mLogViewLayoutParams.gravity
systemLayoutParams?.width = mLogViewLayoutParams.width
systemLayoutParams?.height = mLogViewLayoutParams.height
systemLayoutParams?.x = mLogViewLayoutParams.x
systemLayoutParams?.y = mLogViewLayoutParams.y
}
/**
* 默认实现为true
*
* @return
*/
override fun canDrag(): Boolean {
return true
}
/**
* 搭配shouldDealBackKey使用 自定义处理完以后需要返回true
* 默认模式的onBackPressed 拦截在NormViewManager#getRootContentView中被处理
* 系统模式下的onBackPressed 在当前类的performCreate 初始化View时被处理
* 返回false 表示交由系统处理
* 返回 true 表示当前的返回事件已由自己处理 并拦截了改返回事件
*/
override fun onBackPressed(): Boolean {
return false
}
/**
* 默认不自己处理返回按键
*
* @return
*/
override fun shouldDealBackKey(): Boolean {
return false
}
override fun onEnterBackground() {
mRootView?.let {
it.visibility = View.GONE
}
}
override fun onEnterForeground() {
mRootView?.let {
it.visibility = View.VISIBLE
}
}
override fun onMove(x: Int, y: Int, dx: Int, dy: Int) {
if (!canDrag()) {
return
}
systemLayoutParams?.apply {
this.x += dx
this.y += dy
}
//限制布局边界
resetBorderline(systemLayoutParams)
mWindowManager.updateViewLayout(mRootView, systemLayoutParams)
}
/**
* 限制边界 调用的时候必须保证是在控件能获取到宽高德前提下
*/
private fun resetBorderline(
windowLayoutParams: WindowManager.LayoutParams?
) {
//如果是系统模式或者手动关闭动态限制边界
if (!restrictBorderline()) {
return
}
if (windowLayoutParams != null) {
// 均是横向计算
if (windowLayoutParams.y >= screenShortSideLength - mLogViewHeight) {
windowLayoutParams.y = screenShortSideLength - mLogViewHeight
}
// 均是横向计算
if (windowLayoutParams.x >= screenLongSideLength - mLogViewWidth) {
windowLayoutParams.x = screenLongSideLength - mLogViewWidth
}
//系统模式
if (windowLayoutParams.y <= 0) {
windowLayoutParams.y = 0
}
if (windowLayoutParams.x <= 0) {
windowLayoutParams.x = 0
}
}
}
/**
* 手指弹起时保存当前浮标位置
*
* @param x
* @param y
*/
override fun onUp(x: Int, y: Int) {
if (!canDrag()) {
return
}
if (!viewProps.edgePinned) {
endMoveAndRecord()
return
}
animatedMoveToEdge()
}
private fun endMoveAndRecord() {
systemLayoutParams?.let {
mLastLogViewPosInfo.logViewWidth = it.x
mLastLogViewPosInfo.logViewHeight = it.y
}
}
private fun animatedMoveToEdge() {
val viewSize = mRootView?.width ?: return
systemLayoutParams?.also { layoutAttrs ->
makeAnimator(layoutAttrs.x, viewSize, ViewGroup.LayoutParams.MATCH_PARENT) {
addUpdateListener { v ->
layoutAttrs.x = v.animatedValue as Int
mWindowManager.updateViewLayout(mRootView, layoutAttrs)
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
endMoveAndRecord()
}
})
}
}
}
private inline fun makeAnimator(
from: Int,
size: Int,
containerSize: Int,
setup: ValueAnimator.() -> Unit
) {
if (size <= 0 || containerSize <= 0) return
ValueAnimator.ofInt(
from,
if (from <= (containerSize - size) / 2) 0 else (containerSize - size)
)
.apply {
duration = 150L
setup()
}
.start()
}
/**
* 手指按下时的操作
*
* @param x
* @param y
*/
override fun onDown(x: Int, y: Int) {
if (!canDrag()) {
return
}
}
/**
* home键被点击 只有系统悬浮窗控件才会被调用
*/
open fun onHomeKeyPress() {}
/**
* 菜单键被点击 只有系统悬浮窗控件才会被调用
*/
open fun onRecentAppKeyPress() {}
override fun onPause() {}
/**
* 系统悬浮窗需要调用
*
* @return
*/
val context: Context?
get() = if (mRootView != null) {
mRootView!!.context
} else {
null
}
val resources: Resources?
get() = if (context == null) {
null
} else context!!.resources
fun getString(@StringRes resId: Int): String? {
return if (context == null) {
null
} else context!!.getString(resId)
}
private val isShow: Boolean
get() = mRootView?.isShown ?: false
protected fun <T : View> findViewById(@IdRes id: Int): T? {
if (mRootView == null) {
return null
}
return mRootView?.findViewById(id)
}
/**
* 是否限制布局边界
*
* @return
*/
open fun restrictBorderline(): Boolean {
return true
}
/**
* 获取屏幕短边的长度(横屏) 不包含statusBar
*
* @return
*/
private val screenShortSideLength: Int
get() = ScreenUtils.getAppScreenHeight()
/**
* 获取屏幕长边的长度(横屏) 不包含statusBar
*
* @return
*/
private val screenLongSideLength: Int
get() = ScreenUtils.getAppScreenWidth()
/**
* 强制刷新当前view
*/
open fun immInvalidate() {
mRootView?.requestLayout()
}
/**
* 广播接收器 系统悬浮窗需要调用
*/
private inner class InnerReceiver : BroadcastReceiver() {
private val SYSTEM_DIALOG_REASON_KEY = "reason"
private val SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"
private val SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS == action) {
val reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY)
if (reason != null) {
if (reason == SYSTEM_DIALOG_REASON_HOME_KEY) {
//点击home键
onHomeKeyPress()
} else if (reason == SYSTEM_DIALOG_REASON_RECENT_APPS) {
//点击menu按钮
onRecentAppKeyPress()
}
}
}
}
}
}

View File

@@ -0,0 +1,148 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 内置一个List的通用、简化的适用于RecyclerView的Adapter。
*/
public abstract class AbsRecyclerAdapter<T extends AbsViewBinder, V> extends RecyclerView.Adapter<T> {
protected List<V> mList;
private LayoutInflater mInflater;
protected Context mContext;
public AbsRecyclerAdapter(Context context) {
if (context == null) {
return;
}
mContext = context;
mList = new ArrayList<>();
mInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public final T onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = createView(mInflater, parent, viewType);
return createViewHolder(view, viewType);
}
protected abstract T createViewHolder(View view, int viewType);
/**
* 如果是通过LayoutInflater创建的View,不要绑定到父View,RecyclerView会负责添加。
*/
protected abstract View createView(LayoutInflater inflater, ViewGroup parent, int viewType);
@Override
public final void onBindViewHolder(T holder, int position) {
V data = mList.get(position);
holder.setData(data);
holder.bind(data, position);
}
@Override
public int getItemCount() {
return mList.size();
}
/**
* 列表末尾追加一个元素
*/
@SuppressLint("NotifyDataSetChanged")
public void append(V item) {
if (item == null) {
return;
}
mList.add(item);
notifyDataSetChanged();
}
/**
* 在特定位置增加一个元素
*/
public void append(V item, int position) {
if (item == null) {
return;
}
if (position < 0) {
position = 0;
} else if (position > mList.size()) {
position = mList.size();
}
mList.add(position, item);
notifyItemChanged(position, item);
}
/**
* 追加一个集合
*/
@SuppressLint("NotifyDataSetChanged")
public final void append(Collection<V> items) {
if (items == null || items.size() == 0) {
return;
}
mList.addAll(items);
notifyDataSetChanged();
}
/**
* 清空集合
*/
@SuppressLint("NotifyDataSetChanged")
public final void clear() {
if (mList.isEmpty()) {
return;
}
mList.clear();
notifyDataSetChanged();
}
/**
* 删除一个元素
*/
@SuppressLint("NotifyDataSetChanged")
public final void remove(V item) {
if (item == null) {
return;
}
if (mList.contains(item)) {
mList.remove(item);
notifyDataSetChanged();
}
}
/**
* 删除一个元素
*/
public final void remove(int index) {
if (index < mList.size()) {
mList.remove(index);
notifyItemRemoved(index);
}
}
/**
* 删除一个集合
*/
@SuppressLint("NotifyDataSetChanged")
public final void remove(Collection<V> items) {
if (items == null || items.size() == 0) {
return;
}
if (mList.removeAll(items)) {
notifyDataSetChanged();
}
}
}

View File

@@ -0,0 +1,55 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.content.Context;
import android.view.View;
import androidx.annotation.IdRes;
import androidx.recyclerview.widget.RecyclerView;
/**
* 简单封装的适用于RecyclerView的ViewHolder
*/
public abstract class AbsViewBinder<T> extends RecyclerView.ViewHolder {
private T data;
private View mView;
public AbsViewBinder(final View view) {
super(view);
mView = view;
getViews();
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onViewClick(view, data);
}
});
}
protected final View getView() {
return mView;
}
protected abstract void getViews();
public final <V extends View> V getView(@IdRes int id) {
return (V) mView.findViewById(id);
}
public abstract void bind(T t);
public void bind(T t, int position) {
bind(t);
}
protected void onViewClick(View view, T data) {
}
protected final void setData(T data) {
this.data = data;
}
protected final Context getContext() {
return mView.getContext();
}
}

View File

@@ -0,0 +1,82 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import android.content.Context
import android.view.View
import android.widget.FrameLayout
interface ILogView {
/**
* view 创建时调用 做一些变量的初始化 当还不能进行View的操作
*
* @param context
*/
fun onCreate(context: Context?)
/**
* 传入rootView 用于创建控件
*
* @param context
* @param rootView
* @return 返回创建的childView
*/
fun onCreateView(context: Context?, rootView: FrameLayout?): View?
/**
* 将xml中的控件添加到rootView以后调用在当前方法中可以进行view的一些操作
*
* @param rootView
*/
fun onViewCreated(rootView: FrameLayout?)
/**
* 当前的View添加到根布局里时调用
*/
fun onResume()
/**
* 当前activity onPause时调用
*/
fun onPause()
/**
* 确定系统悬浮窗浮标的初始位置
* LayoutParams创建完以后调用
*
* @param params
*/
fun initViewLayoutParams(params: LogViewLayoutParams?)
/**
* app进入后台时调用 内置View 不需要实现
*/
fun onEnterBackground()
/**
* app回到前台时调用 内置view 不需要实现
*/
fun onEnterForeground()
/**
* 浮标控件是否可以拖动
*
* @return
*/
fun canDrag(): Boolean
/**
* 是否需要自己处理返回键
*
* @return
*/
fun shouldDealBackKey(): Boolean
/**
* shouldDealBackKey == true 时调用
*/
fun onBackPressed(): Boolean
/**
* 悬浮窗主动销毁时调用 不能在当前生命周期回调函数中调用 detach自己 否则会出现死循环
*/
fun onDestroy()
}

View File

@@ -0,0 +1,6 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
interface ILogViewListener {
fun onAttach()
fun onDetach()
}

View File

@@ -0,0 +1,30 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import com.mogo.eagle.core.utilcode.util.ScreenUtils
/**
* 保存上一次LogView的位置信息
*/
class LastLogViewPosInfo {
var logViewWidth = 0
var logViewHeight = 0
var leftMarginPercent = 0f
private set
var topMarginPercent = 0f
private set
fun setLeftMargin(leftMargin: Int) {
leftMarginPercent = leftMargin.toFloat() / ScreenUtils.getAppScreenWidth().toFloat()
}
fun setTopMargin(topMargin: Int) {
topMarginPercent = topMargin.toFloat() / ScreenUtils.getAppScreenHeight().toFloat()
}
override fun toString(): String {
return "LastLogViewPosInfo{" +
", leftMarginPercent=" + leftMarginPercent +
", topMarginPercent=" + topMarginPercent +
'}'
}
}

View File

@@ -0,0 +1,26 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
open class LogFrameLayout : FrameLayout {
private var mFlag = LogFrameLayoutFlag_ROOT
constructor(context: Context, flag: Int) : super(context) {
mFlag = flag
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
}
companion object {
const val LogFrameLayoutFlag_ROOT = 100
const val LogFrameLayoutFlag_CHILD = 200
}
}

View File

@@ -0,0 +1,287 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch
import android.annotation.SuppressLint
import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mogo.eagle.core.function.hmi.R
import java.util.*
class LogInfoView : AbsLogView() {
companion object {
const val MAX_LOG_LINE_NUM = 10000
const val UPDATE_CHECK_INTERVAL = 200
}
private var logLines = LinkedList<LogLine>()
private var counter = 0
private var mAutoScrollToBottom = true
private var mIsLoaded = false
private var mLogRv: RecyclerView? = null
private var mLogItemAdapter: LogItemAdapter? = null
private var mLogFilter: EditText? = null
private var mRadioGroup: RadioGroup? = null
private var mLinearLayout: LinearLayout? = null
/**
* 单行的log
*/
private var mLogHint: TextView? = null
private var mLogRvWrap: RelativeLayout? = null
private var logViewListener: ILogViewListener? = null
override fun onCreate(context: Context?) {}
override fun onCreateView(context: Context?, rootView: FrameLayout?): View? {
return LayoutInflater.from(context).inflate(R.layout.log_view, rootView, false)
}
override fun onViewCreated(rootView: FrameLayout?) {
initView()
}
private fun initView() {
mLogHint = findViewById(R.id.log_hint)
mLogRvWrap = findViewById(R.id.log_page)
mLogRv = findViewById(R.id.log_list)
mLogRv!!.layoutManager = LinearLayoutManager(context)
mLogItemAdapter = LogItemAdapter(context!!)
mLogRv!!.adapter = mLogItemAdapter
mLogFilter = findViewById(R.id.log_filter)
mLogFilter!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable) {
mLogItemAdapter!!.filter.filter(s)
}
})
val mTitleBar = findViewById<LogTitleBar>(R.id.title_bar)
mTitleBar!!.setListener(object : LogTitleBar.OnTitleBarClickListener {
override fun onRightClick() {
if (logViewListener != null) {
logViewListener!!.onDetach()
}
}
override fun onLeftClick() {
minimize()
}
})
mLogHint!!.setOnClickListener { v: View? -> maximize() }
mRadioGroup = findViewById(R.id.radio_group)
mRadioGroup!!.setOnCheckedChangeListener { group: RadioGroup?, checkedId: Int ->
when (checkedId) {
R.id.verbose -> {
mLogItemAdapter!!.logLevelLimit = Log.VERBOSE
}
R.id.debug -> {
mLogItemAdapter!!.logLevelLimit = Log.DEBUG
}
R.id.info -> {
mLogItemAdapter!!.logLevelLimit = Log.INFO
}
R.id.warn -> {
mLogItemAdapter!!.logLevelLimit = Log.WARN
}
R.id.error -> {
mLogItemAdapter!!.logLevelLimit = Log.ERROR
}
}
mLogItemAdapter!!.filter.filter(mLogFilter!!.text)
}
mLogRv!!.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager?
// if the bottom of the list isn't visible anymore, then stop autoscrolling
mAutoScrollToBottom =
layoutManager!!.findLastCompletelyVisibleItemPosition() == recyclerView.adapter!!
.itemCount - 1
}
})
mRadioGroup!!.check(R.id.verbose)
val mBtnTop = findViewById<Button>(R.id.btn_top)
val mBtnBottom = findViewById<Button>(R.id.btn_bottom)
val mBtnClean = findViewById<Button>(R.id.btn_clean)
val mBtnExport = findViewById<Button>(R.id.btn_export)
mBtnTop!!.setOnClickListener {
if (mLogItemAdapter == null || mLogItemAdapter!!.itemCount == 0) {
return@setOnClickListener
}
mLogRv!!.scrollToPosition(0)
}
mBtnBottom!!.setOnClickListener {
if (mLogItemAdapter == null || mLogItemAdapter!!.itemCount == 0) {
return@setOnClickListener
}
mLogRv!!.scrollToPosition(mLogItemAdapter!!.itemCount - 1)
}
mBtnExport!!.setOnClickListener { }
mBtnClean!!.setOnClickListener {
if (mLogItemAdapter == null || mLogItemAdapter!!.itemCount == 0) {
return@setOnClickListener
}
counter = 0
mLogItemAdapter!!.clearLog()
}
}
override fun onResume() {
super.onResume()
if (logViewListener != null) {
logViewListener!!.onAttach()
}
}
override fun onEnterForeground() {
super.onEnterForeground()
parentView?.visibility = View.VISIBLE
}
override fun onEnterBackground() {
super.onEnterBackground()
parentView?.visibility = View.GONE
}
fun registerLogViewListener(listener: ILogViewListener?) {
logViewListener = listener
}
fun unRegisterLogViewListener() {
logViewListener = null
}
override fun initViewLayoutParams(params: LogViewLayoutParams?) {
params!!.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
params.width = LogViewLayoutParams.MATCH_PARENT
params.height = LogViewLayoutParams.MATCH_PARENT
}
@SuppressLint("NotifyDataSetChanged")
fun onLogCatch(lineLog: String) {
if (mLogRv == null || mLogItemAdapter == null) {
return
}
if (!mIsLoaded) {
mIsLoaded = true
mLinearLayout = findViewById(R.id.ll_loading)
mLinearLayout!!.visibility = View.GONE
mLogRv!!.visibility = View.VISIBLE
}
val logLine = LogLine.newLogLine(lineLog, false)
if (logLines.size > MAX_LOG_LINE_NUM) {
logLines.removeFirst()
}
logLines.add(logLine)
if (logLines.size == 1) {
mLogItemAdapter!!.addWithFilter(logLines[0], mLogFilter?.text, true)
} else {
mLogItemAdapter!!.addWithFilter(logLine, mLogFilter?.text, false)
mLogItemAdapter!!.notifyDataSetChanged()
}
if (logLines.size > 0) {
val line = logLines[logLines.size - 1]
"${line.tag} : ${line.logOutput}".also { mLogHint?.text = it }
}
if (++counter % UPDATE_CHECK_INTERVAL == 0
&& mLogItemAdapter!!.trueValues.size > MAX_LOG_LINE_NUM
) {
val numItemsToRemove = mLogItemAdapter!!.trueValues.size - MAX_LOG_LINE_NUM
mLogItemAdapter!!.removeFirst(numItemsToRemove)
}
if (mAutoScrollToBottom) {
scrollToBottom()
}
}
private fun scrollToBottom() {
mLogRv!!.scrollToPosition(mLogItemAdapter!!.itemCount - 1)
}
private val selectLogLevel: Int
get() {
return when (mRadioGroup!!.checkedRadioButtonId) {
R.id.verbose -> {
Log.VERBOSE
}
R.id.debug -> {
Log.DEBUG
}
R.id.info -> {
Log.INFO
}
R.id.warn -> {
Log.WARN
}
R.id.error -> {
Log.ERROR
}
else -> {
Log.VERBOSE
}
}
}
/**
* 最小化
*/
fun minimize() {
isMaximize = false
mLogHint!!.visibility = View.VISIBLE
mLogRvWrap!!.visibility = View.GONE
val layoutParams = systemLayoutParams ?: return
Log.d("EmArrow", "minimize , layoutParams is not null")
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
layoutParams.gravity = Gravity.START or Gravity.TOP
mWindowManager.updateViewLayout(logView, layoutParams)
}
/**
* 是否最大化
*/
private var isMaximize = true
private fun maximize() {
isMaximize = true
mLogHint!!.visibility = View.GONE
mLogRvWrap!!.visibility = View.VISIBLE
val layoutParams = systemLayoutParams ?: return
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT
layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT
layoutParams.gravity = Gravity.START or Gravity.TOP
mWindowManager.updateViewLayout(logView, layoutParams)
}
override fun onBackPressed(): Boolean {
return if (isMaximize) {
minimize()
true
} else {
false
}
}
override fun shouldDealBackKey(): Boolean {
return true
}
override fun canDrag(): Boolean {
return false
}
}

View File

@@ -0,0 +1,260 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static android.content.Context.CLIPBOARD_SERVICE;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.function.hmi.ui.utils.SearchCriteria;
import com.mogo.eagle.core.function.hmi.ui.utils.TagColorUtil;
public class LogItemAdapter extends AbsRecyclerAdapter<AbsViewBinder<LogLine>, LogLine> implements Filterable {
public LogItemAdapter(Context context) {
super(context);
mClipboard = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
}
private ArrayList<LogLine> mOriginalValues = new ArrayList<>();
private final ArrayFilter mFilter = new ArrayFilter();
private int logLevelLimit = Log.VERBOSE;
private final ClipboardManager mClipboard;
/**
* 清空log
*/
@SuppressLint("NotifyDataSetChanged")
public void clearLog() {
if (mOriginalValues != null && mOriginalValues.size() > 0) {
mOriginalValues.clear();
}
clear();
notifyDataSetChanged();
}
@Override
protected AbsViewBinder<LogLine> createViewHolder(View view, int viewType) {
return new LogInfoViewHolder(view);
}
@Override
protected View createView(LayoutInflater inflater, ViewGroup parent, int viewType) {
return inflater.inflate(R.layout.item_log, parent, false);
}
@Override
public Filter getFilter() {
return mFilter;
}
public int getLogLevelLimit() {
return logLevelLimit;
}
public void setLogLevelLimit(int logLevelLimit) {
this.logLevelLimit = logLevelLimit;
}
public List<LogLine> getTrueValues() {
return mOriginalValues != null ? mOriginalValues : mList;
}
@SuppressLint("NotifyDataSetChanged")
public void removeFirst(int n) {
if (mOriginalValues != null) {
List<LogLine> subList = mOriginalValues.subList(n, mOriginalValues.size());
for (int i = 0; i < n; i++) {
// value to delete - delete it from the mObjects as well
mList.remove(mOriginalValues.get(i));
}
mOriginalValues = new ArrayList<>(subList);
}
notifyDataSetChanged();
}
public class LogInfoViewHolder extends AbsViewBinder<LogLine> {
private TextView mLogText;
private TextView mPid;
private TextView mTime;
private TextView mTag;
private TextView mLevel;
public LogInfoViewHolder(View view) {
super(view);
}
@Override
protected void getViews() {
mLogText = getView(R.id.log_output_text);
mLevel = getView(R.id.log_level_text);
mPid = getView(R.id.pid_text);
mTime = getView(R.id.timestamp_text);
mTag = getView(R.id.tag_text);
}
@Override
protected void onViewClick(View view, final LogLine data) {
super.onViewClick(view, data);
data.setExpanded(!data.isExpanded());
if (data.isExpanded() && data.getProcessId() != -1) {
mLogText.setSingleLine(false);
mTime.setVisibility(View.VISIBLE);
mPid.setVisibility(View.VISIBLE);
view.setBackgroundColor(Color.BLACK);
mLogText.setTextColor(TagColorUtil.getTextColor(getContext(), data.getLogLevel(), true));
mTag.setTextColor(TagColorUtil.getTextColor(getContext(), data.getLogLevel(), true));
} else {
mLogText.setSingleLine(true);
mTime.setVisibility(View.GONE);
mPid.setVisibility(View.GONE);
view.setBackgroundColor(Color.WHITE);
mLogText.setTextColor(TagColorUtil.getTextColor(getContext(), data.getLogLevel(), false));
mTag.setTextColor(TagColorUtil.getTextColor(getContext(), data.getLogLevel(), false));
}
itemView.setOnLongClickListener(v -> {
ClipData clipData = ClipData.newPlainText("Label", data.getOriginalLine());
mClipboard.setPrimaryClip(clipData);
return true;
});
}
@Override
public void bind(LogLine item) {
mLevel.setText(item.getLogLevelText());
mLevel.setTextColor(TagColorUtil.getLevelColor(getContext(), item.getLogLevel()));
mLevel.setBackgroundColor(TagColorUtil.getLevelBgColor(getContext(), item.getLogLevel()));
mPid.setText(String.valueOf(item.getProcessId()));
mTime.setText(item.getTimestamp());
mLogText.setText(item.getLogOutput());
mTag.setText(item.getTag());
if (item.isExpanded() && item.getProcessId() != -1) {
mLogText.setSingleLine(false);
mTime.setVisibility(View.VISIBLE);
mPid.setVisibility(View.VISIBLE);
mLogText.setTextColor(TagColorUtil.getTextColor(getContext(), item.getLogLevel(), true));
mTag.setTextColor(TagColorUtil.getTextColor(getContext(), item.getLogLevel(), true));
itemView.setBackgroundColor(Color.BLACK);
} else {
mLogText.setSingleLine(true);
mTime.setVisibility(View.GONE);
mPid.setVisibility(View.GONE);
itemView.setBackgroundColor(Color.WHITE);
mLogText.setTextColor(TagColorUtil.getTextColor(getContext(), item.getLogLevel(), false));
mTag.setTextColor(TagColorUtil.getTextColor(getContext(), item.getLogLevel(), false));
}
}
}
/**
* 添加日志到adapter中
*/
public void addWithFilter(LogLine logObj, CharSequence text, boolean notify) {
if (mOriginalValues != null) {
List<LogLine> inputList = Collections.singletonList(logObj);
List<LogLine> filteredObjects = mFilter.performFilteringOnList(inputList, text);
mOriginalValues.add(logObj);
mList.addAll(filteredObjects);
if (notify) {
notifyItemRangeInserted(mList.size() - filteredObjects.size(), filteredObjects.size());
}
} else {
mList.add(logObj);
if (notify) {
notifyItemInserted(mList.size());
}
}
}
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
ArrayList<LogLine> allValues = performFilteringOnList(mOriginalValues, prefix);
results.values = allValues;
results.count = allValues.size();
return results;
}
public ArrayList<LogLine> performFilteringOnList(List<LogLine> inputList, CharSequence query) {
SearchCriteria searchCriteria = new SearchCriteria(query);
// search by log level
ArrayList<LogLine> allValues = new ArrayList<>();
ArrayList<LogLine> logLines = new ArrayList<>(inputList);
for (LogLine logLine : logLines) {
if (logLine != null && logLine.getLogLevel() >= logLevelLimit) {
allValues.add(logLine);
}
}
ArrayList<LogLine> finalValues = allValues;
// search by criteria
if (!searchCriteria.isEmpty()) {
final int count = allValues.size();
final ArrayList<LogLine> newValues = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
final LogLine value = allValues.get(i);
// search the logLine based on the criteria
if (searchCriteria.matches(value)) {
newValues.add(value);
}
}
finalValues = newValues;
}
return finalValues;
}
@SuppressLint("NotifyDataSetChanged")
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mList = (List<LogLine>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetChanged();
}
}
}
}

View File

@@ -0,0 +1,202 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.text.TextUtils;
import android.util.Log;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LogLine {
private static final int TIMESTAMP_LENGTH = 19;
private static final Pattern logPattern = Pattern.compile(
// log level
"(\\w)" +
"/" +
// tag
"([^(]+)" +
"\\(\\s*" +
// pid
"(\\d+)" +
// optional weird number that only occurs on ZTE blade
"(?:\\*\\s*\\d+)?" +
"\\): ");
private static final String filterPattern = "ResourceType|memtrack|android.os.Debug|BufferItemConsumer|DPM.*|MDM.*|ChimeraUtils|BatteryExternalStats.*|chatty.*|DisplayPowerController|WidgetHelper|WearableService|DigitalWidget.*|^ANDR-PERF-.*";
private int logLevel;
private String tag;
private String logOutput;
private int processId = -1;
private String timestamp;
private boolean expanded = false;
private boolean highlighted = false;
public static LogLine newLogLine(String originalLine, boolean expanded) {
LogLine logLine = new LogLine();
logLine.setExpanded(expanded);
int startIdx = 0;
// if the first char is a digit, then this starts out with a timestamp
// otherwise, it's a legacy log or the beginning of the log output or something
if (!TextUtils.isEmpty(originalLine)
&& Character.isDigit(originalLine.charAt(0))
&& originalLine.length() >= TIMESTAMP_LENGTH) {
String timestamp = originalLine.substring(0, TIMESTAMP_LENGTH - 1);
logLine.setTimestamp(timestamp);
// cut off timestamp
startIdx = TIMESTAMP_LENGTH;
}
Matcher matcher = logPattern.matcher(originalLine);
if (matcher.find(startIdx)) {
char logLevelChar = matcher.group(1).charAt(0);
String logText = originalLine.substring(matcher.end());
if (logText.matches("^maxLineHeight.*|Failed to read.*")) {
logLine.setLogLevel(convertCharToLogLevel('V'));
} else {
logLine.setLogLevel(convertCharToLogLevel(logLevelChar));
}
String tagText = matcher.group(2);
if (tagText.matches(filterPattern)) {
logLine.setLogLevel(convertCharToLogLevel('V'));
}
logLine.setTag(tagText);
logLine.setProcessId(Integer.parseInt(matcher.group(3)));
logLine.setLogOutput(logText);
} else {
logLine.setLogOutput(originalLine);
logLine.setLogLevel(-1);
}
return logLine;
}
private static int convertCharToLogLevel(char logLevelChar) {
switch (logLevelChar) {
case 'D':
return Log.DEBUG;
case 'E':
return Log.ERROR;
case 'I':
return Log.INFO;
case 'V':
case 'F':
return Log.VERBOSE;
case 'W':
return Log.WARN;
}
return -1;
}
private static char convertLogLevelToChar(int logLevel) {
switch (logLevel) {
case Log.DEBUG:
return 'D';
case Log.ERROR:
return 'E';
case Log.INFO:
return 'I';
case Log.VERBOSE:
return 'V';
case Log.WARN:
return 'W';
}
return ' ';
}
public String getOriginalLine() {
if (logLevel == -1) { // starter line like "begin of log etc. etc."
return logOutput;
}
StringBuilder stringBuilder = new StringBuilder();
if (timestamp != null) {
stringBuilder.append(timestamp).append(' ');
}
stringBuilder.append(convertLogLevelToChar(logLevel))
.append('/')
.append(tag)
.append('(')
.append(processId)
.append("): ")
.append(logOutput);
return stringBuilder.toString();
}
public String getLogLevelText() {
return Character.toString(convertLogLevelToChar(logLevel));
}
public int getLogLevel() {
return logLevel;
}
public void setLogLevel(int logLevel) {
this.logLevel = logLevel;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getLogOutput() {
return logOutput;
}
public void setLogOutput(String logOutput) {
this.logOutput = logOutput;
}
public int getProcessId() {
return processId;
}
public void setProcessId(int processId) {
this.processId = processId;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
public boolean isHighlighted() {
return highlighted;
}
public void setHighlighted(boolean highlighted) {
this.highlighted = highlighted;
}
}

View File

@@ -0,0 +1,114 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import com.mogo.eagle.core.function.hmi.R;
/**
* 日志专属TitleBar
*/
public class LogTitleBar extends FrameLayout {
private OnTitleBarClickListener mListener;
private TextView mBack;
private TextView mTitle;
private ImageView mIcon;
public LogTitleBar(@NonNull Context context) {
this(context, null);
}
public LogTitleBar(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public LogTitleBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.log_title_bar, this, true);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.LogTitleBar);
int icon = a.getResourceId(R.styleable.LogTitleBar_Icon, 0);
String title = a.getString(R.styleable.LogTitleBar_Title);
String back = a.getString(R.styleable.LogTitleBar_Back);
a.recycle();
mBack = findViewById(R.id.back);
mIcon = findViewById(R.id.icon);
mTitle = findViewById(R.id.title);
mIcon.setOnClickListener(v -> {
if (mListener != null) {
mListener.onRightClick();
}
});
mBack.setOnClickListener(v -> {
if (mListener != null) {
mListener.onLeftClick();
}
});
setBack(back);
setTitle(title);
setIcon(icon);
}
/**
* TitleBar 点击事件回调
*/
public interface OnTitleBarClickListener {
void onRightClick();
void onLeftClick();
}
public void setTitle(@StringRes int title) {
setTitle(getResources().getString(title));
}
public void setTitle(String title) {
if (TextUtils.isEmpty(title)) {
mTitle.setText("");
} else {
mTitle.setText(title);
mTitle.setAlpha(0);
mTitle.setVisibility(View.VISIBLE);
mTitle.animate().alpha(1).start();
}
}
public void setBack(String back) {
if (TextUtils.isEmpty(back)) {
mBack.setText("");
} else {
mBack.setText(back);
}
}
public void setIcon(@DrawableRes int id) {
if (id == 0) {
return;
}
mIcon.setImageResource(id);
mIcon.setVisibility(View.VISIBLE);
}
public void setListener(OnTitleBarClickListener listener) {
mListener = listener;
}
}

View File

@@ -0,0 +1,49 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.view.ViewGroup;
import android.view.WindowManager;
public class LogViewLayoutParams {
/**
* 悬浮窗不能获取焦点
*/
public static int FLAG_NOT_FOCUSABLE = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
public static int FLAG_NOT_TOUCHABLE = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
/**
* 是否允许超出屏幕
*/
public static int FLAG_LAYOUT_NO_LIMITS = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
/**
* 悬浮窗不能获取焦点并且不相应触摸
*/
public static int FLAG_NOT_FOCUSABLE_AND_NOT_TOUCHABLE = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
public static int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
public static int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
/**
* 只针对系统悬浮窗起作用 值基本上为以上2个
*/
public int flags;
/**
* 只针对系统悬浮窗起作用 值基本上为Gravity
*/
public int gravity;
public int x;
public int y;
public int width;
public int height;
@Override
public String toString() {
return "LogViewLayoutParams{" +
"flags=" + flags +
", gravity=" + gravity +
", x=" + x +
", y=" + y +
", width=" + width +
", height=" + height +
'}';
}
}

View File

@@ -0,0 +1,32 @@
package com.mogo.eagle.core.function.hmi.ui.logcatch;
import android.view.MotionEvent;
import android.view.View;
/**
* touch 事件代理 解决点击和触摸事件的冲突
*/
public class TouchProxy {
public TouchProxy(OnTouchEventListener eventListener) {
}
public void setEventListener(OnTouchEventListener eventListener) {
}
public boolean onTouchEvent(View v, MotionEvent event) {
return true;
}
public interface OnTouchEventListener {
void onMove(int x, int y, int dx, int dy);
void onUp(int x, int y);
void onDown(int x, int y);
}
}

View File

@@ -17,10 +17,10 @@ import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.function.hmi.WaringConst;
import com.mogo.eagle.core.function.hmi.notification.WarningFloat;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform;
import com.mogo.eagle.core.utilcode.util.BitmapHelper;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
import com.mogo.utils.BitmapHelper;
import com.mogo.utils.glide.GlideApp;
import com.mogo.utils.glide.GlideRoundedCornersTransform;
/**
* @author liujing

View File

@@ -10,15 +10,15 @@ import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.data.notice.NoticeNormalData
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform
import com.mogo.eagle.core.utilcode.util.BitmapHelper
import com.mogo.eagle.core.widget.media.video.NoticeSimpleVideoPlayer
import com.mogo.module.common.MogoApisHandler
import com.mogo.module.common.dialog.BaseFloatDialog
import com.mogo.service.IMogoServiceApis
import com.mogo.service.statusmanager.IMogoStatusChangedListener
import com.mogo.service.statusmanager.StatusDescriptor
import com.mogo.utils.BitmapHelper
import com.mogo.utils.glide.GlideApp
import com.mogo.utils.glide.GlideRoundedCornersTransform
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack
@@ -122,8 +122,10 @@ class NoticeCheckDialog(context: Context) : BaseFloatDialog(context), LifecycleO
pushVideo?.visibility = View.GONE
pushImageView?.visibility = View.VISIBLE
pushImageView?.let {
GlideApp.with(context).load(noticeNormal.imageUrl).optionalTransform(GlideRoundedCornersTransform(
20f, GlideRoundedCornersTransform.CornerType.ALL)).into(it)
GlideApp.with(context).load(noticeNormal.imageUrl).optionalTransform(
GlideRoundedCornersTransform(
20f, GlideRoundedCornersTransform.CornerType.ALL)
).into(it)
}
}

View File

@@ -12,14 +12,12 @@ import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.mogo.eagle.core.data.notice.NoticeNormalData;
import com.mogo.eagle.core.data.notice.NoticeTrafficStyleInfo;
import com.mogo.eagle.core.data.notice.NoticeTrafficStylePushData;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.function.hmi.WaringConst;
import com.mogo.eagle.core.function.hmi.notification.WarningFloat;
import com.mogo.eagle.core.utilcode.util.SharedPrefs;
import com.mogo.utils.glide.GlideApp;
import com.mogo.utils.glide.GlideRoundedCornersTransform;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform;
/**
* @author lixiaopeng

View File

@@ -22,6 +22,10 @@ import com.mogo.eagle.core.function.api.notice.NoticeNetCallBack;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
import com.mogo.eagle.core.function.call.notice.CallerNoticeManager;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideApp;
import com.mogo.eagle.core.utilcode.mogo.glide.GlideRoundedCornersTransform;
import com.mogo.eagle.core.utilcode.util.BitmapHelper;
import com.mogo.eagle.core.utilcode.util.DateTimeUtils;
import com.mogo.eagle.core.widget.media.video.NoticeSimpleSmallVideoPlayer;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.module.common.dialog.BaseFloatDialog;
@@ -29,11 +33,7 @@ import com.mogo.service.IMogoServiceApis;
import com.mogo.service.imageloader.MogoImageView;
import com.mogo.service.statusmanager.IMogoStatusChangedListener;
import com.mogo.service.statusmanager.StatusDescriptor;
import com.mogo.utils.BitmapHelper;
import com.mogo.utils.glide.GlideApp;
import com.mogo.utils.glide.GlideRoundedCornersTransform;
import com.shuyu.gsyvideoplayer.GSYVideoManager;
import com.mogo.utils.DateTimeUtils;
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder;
import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack;
import com.shuyu.gsyvideoplayer.utils.NetworkUtils;

View File

@@ -34,17 +34,13 @@ import com.mogo.eagle.core.function.call.map.CallerSmpManager
import com.mogo.eagle.core.function.call.obu.CallerOBUManager
import com.mogo.eagle.core.function.call.obu.CallerObuListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.logcatch.ILogViewListener
import com.mogo.eagle.core.function.hmi.ui.logcatch.LogInfoView
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.AppUtils
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.eagle.core.utilcode.util.LogUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
import com.mogo.eagle.core.utilcode.util.*
import com.mogo.map.MogoMap
import com.mogo.utils.DeviceIdUtils
import com.mogo.utils.NetworkUtils
import com.mogo.utils.UiThreadHandler
import com.mogo.utils.storage.SharedPrefsMgr
import kotlinx.android.synthetic.main.view_debug_setting.view.*
/**
@@ -62,6 +58,9 @@ class DebugSettingView @JvmOverloads constructor(
private val TAG = "DebugSettingView"
private var logInfoView: LogInfoView? = null
private var logViewAttach = false
init {
LayoutInflater.from(context).inflate(R.layout.view_debug_setting, this, true)
initView()
@@ -73,6 +72,9 @@ class DebugSettingView @JvmOverloads constructor(
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
CallerAutopilotCarStatusListenerManager.addListener(TAG, this)
CallerMapLocationListenerManager.addListener(TAG, this)
if (logInfoView != null) {
logInfoView!!.onEnterForeground()
}
}
override fun onDetachedFromWindow() {
@@ -81,6 +83,9 @@ class DebugSettingView @JvmOverloads constructor(
CallerAutoPilotStatusListenerManager.removeListener(TAG)
CallerAutopilotCarStatusListenerManager.removeListener(TAG)
CallerMapLocationListenerManager.removeListener(TAG)
if (logInfoView != null) {
logInfoView!!.onEnterBackground()
}
}
private fun initView() {
@@ -102,7 +107,6 @@ class DebugSettingView @JvmOverloads constructor(
}
}
tbSpeedView.setOnCheckedChangeListener { buttonView, isChecked ->
if (!isChecked) {
CallerHmiManager.setSpeedChartViewVisibility(View.VISIBLE)
@@ -135,6 +139,11 @@ class DebugSettingView @JvmOverloads constructor(
}
}
changesight_top_btn.setOnClickListener {
CallerHDMapManager.setMapDAngle(0);
}
tvObuInfo.text = CallerObuListenerManager.getObuStatusInfoJsonString()
tvAutopilotInfo.text =
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfoJsonString()
@@ -160,7 +169,7 @@ class DebugSettingView @JvmOverloads constructor(
// 初始化工控机 IP信息
val autoPilotIpAddress =
SharedPrefsMgr.getInstance(context).getString(MoGoConfig.AUTOPILOT_IP, "192.168.1.102")
SharedPrefsMgr.getInstance(context).getString(MoGoConfig.AUTOPILOT_IP, "192.168.1.102")
etAutopilotIP.setText(autoPilotIpAddress)
etAutopilotIP.text?.let { etAutopilotIP.setSelection(it.length) }
@@ -269,13 +278,11 @@ class DebugSettingView @JvmOverloads constructor(
if (isChecked) {
LogUtils.getConfig().isLogSwitch = false
Logger.init(LogLevel.OFF)
com.mogo.utils.logger.Logger.init(com.mogo.utils.logger.LogLevel.OFF)
com.elegant.log.simplelog.Logger.init(com.elegant.log.simplelog.LogLevel.OFF)
com.zhidao.account.sdk.utils.Logger.init(false)
} else {
LogUtils.getConfig().isLogSwitch = true
Logger.init(LogLevel.DEBUG)
com.mogo.utils.logger.Logger.init(com.mogo.utils.logger.LogLevel.DEBUG)
com.elegant.log.simplelog.Logger.init(com.elegant.log.simplelog.LogLevel.DEBUG)
com.zhidao.account.sdk.utils.Logger.init(true)
}
@@ -298,7 +305,7 @@ class DebugSettingView @JvmOverloads constructor(
}
tbLogCatch.isChecked =
SharedPrefsMgr.getInstance(context).getBoolean(MoGoConfig.CATCH_LOG,false)
SharedPrefsMgr.getInstance(context).getBoolean(MoGoConfig.CATCH_LOG, false)
tbLogCatch.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
CallerDevaToolsManager.startCatchLog()
@@ -314,7 +321,43 @@ class DebugSettingView @JvmOverloads constructor(
super.onLogCatchClose()
tbLogCatch.isChecked = false
}
override fun onLogCatch(lineLog: String) {
logInfoView?.let {
if (logViewAttach) {
it.onLogCatch(lineLog)
}
}
}
})
tbLogDebugView.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
logInfoView = LogInfoView()
logInfoView!!.registerLogViewListener(object : ILogViewListener {
override fun onAttach() {
logViewAttach = true
}
override fun onDetach() {
logViewDestroy()
tbLogDebugView.isChecked = false
}
})
logInfoView!!.show(context)
} else {
logViewDestroy()
}
}
}
private fun logViewDestroy() {
logInfoView?.let {
it.dismiss()
it.unRegisterLogViewListener()
logInfoView = null
logViewAttach = false
}
}
/**

View File

@@ -0,0 +1,46 @@
package com.mogo.eagle.core.function.hmi.ui.tools
import android.content.Context
import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.module.common.dialog.BaseFloatDialog
/**
* @author XuXinChao
* @description 工控机确认升级对话框
* @since: 2022/1/13
*/
class AdUpgradeDialog(context: Context) : BaseFloatDialog(context), LifecycleObserver {
companion object {
const val TAG = "AdUpgradeDialog"
}
private var upgradeConfirm : TextView? = null
private var upgradeCancel : TextView? = null
init {
setContentView(R.layout.dialog_ad_upgrade)
setCanceledOnTouchOutside(true)
upgradeConfirm=findViewById(R.id.tv_upgrade_confirm)
upgradeCancel=findViewById(R.id.tv_upgrade_cancel)
upgradeConfirm?.setOnClickListener{
Logger.i(TAG,"upgradeConfirm click")
}
upgradeCancel?.setOnClickListener {
Logger.i(TAG,"upgradeCancel click")
}
}
fun showUpgradeDialog(){
if(isShowing){
return
}
show()
}
}

View File

@@ -7,22 +7,29 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.check.CallerCheckManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.utils.KeyBoardUtil
import com.mogo.eagle.core.utilcode.util.ToastUtils
import kotlinx.android.synthetic.main.view_auto_pilot_check.view.*
import kotlinx.android.synthetic.main.view_check_system.view.*
/**
* @author ChenFufeng
* 设置自动驾驶速度和检测页入口
*/
class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
class AutoPilotAndCheckView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(
context,
attrs,
defStyleAttr
), IMoGoAutopilotStatusListener {
private val TAG = "AutoPilotAndCheckView"
@@ -30,13 +37,6 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
private var keyBoardUtil: KeyBoardUtil? = null
private var connectStatus = false
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(
context,
attrs,
defStyleAttr
)
init {
LayoutInflater.from(context).inflate(R.layout.view_auto_pilot_check, this, true)
initView()
@@ -61,7 +61,7 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
else -> {
keyBoardUtil?.hideKeyboard()
// 设置自动驾驶速度
var isSuccess = CallerAutoPilotManager.setAutoPilotSpeed(speed)
val isSuccess = CallerAutoPilotManager.setAutoPilotSpeed(speed)
when {
isSuccess -> {
ToastUtils.showShort("车速设置成功,立即生效")
@@ -81,17 +81,26 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
viewCheckStatus.setOnClickListener {
clickListener?.go2CheckPage()
}
ivDebugPanel.setOnClickListener {
clickListener?.showDebugPanelView()
}
etInputSpeed.setOnTouchListener { v, _ ->
if (etInputSpeed.hasFocusable()) {
if (keyBoardUtil == null) {
keyBoardUtil = KeyBoardUtil(sKeyBoardView, etInputSpeed)
if (!connectStatus) {
ToastUtils.showShort("设置车速失败,请启动域控制器")
keyBoardUtil?.hideKeyboard()
return@setOnTouchListener true
} else {
if (etInputSpeed.hasFocusable()) {
if (keyBoardUtil == null) {
keyBoardUtil = KeyBoardUtil(sKeyBoardView, etInputSpeed)
}
keyBoardUtil?.showKeyboard()
}
keyBoardUtil?.showKeyboard()
if (!etInputSpeed.hasFocus()) {
etInputSpeed.requestFocus()
}
return@setOnTouchListener false
}
if (!etInputSpeed.hasFocus()) {
etInputSpeed.requestFocus()
}
return@setOnTouchListener false
}
// // 比如需要设置默认速度
// val speed = "30"
@@ -117,10 +126,6 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
connectStatus = autoPilotStatusInfo.connectStatus
}
override fun onAutopilotArriveAtStation(autopilotWayArrive: AutopilotStationInfo?) {
}
override fun onAutopilotGuardian(guardianInfo: AutopilotGuardianStatusInfo?) {
}
@@ -128,5 +133,6 @@ class AutoPilotAndCheckView : FrameLayout, IMoGoAutopilotStatusListener {
interface ClickListener {
fun go2CheckPage()
fun onClose(v: View)
fun showDebugPanelView()
}
}

View File

@@ -0,0 +1,338 @@
package com.mogo.eagle.core.function.hmi.ui.tools
import android.annotation.SuppressLint
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.StateListDrawable
import android.os.Handler
import android.util.AttributeSet
import android.util.StateSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.Keep
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.annotations.Expose
import com.mogo.commons.debug.DebugConfig
import com.mogo.commons.debug.DebugConfig.getNetMode
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.tools.BadCaseEntity.Reason
import com.mogo.eagle.core.network.RetrofitFactory
import com.mogo.eagle.core.network.utils.GsonUtil
import com.mogo.eagle.core.utilcode.kotlin.*
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.Utils
import kotlinx.android.synthetic.main.layout_badcase_collect.view.*
import kotlinx.coroutines.*
import kotlinx.coroutines.android.asCoroutineDispatcher
import retrofit2.Response
import retrofit2.http.FieldMap
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.POST
import kotlin.Result.Companion.failure
import kotlin.Result.Companion.success
private typealias OnDismissCallback = () -> Unit
private typealias OnSelectCallback = (Reason) -> Unit
interface BadCaseApi {
@FormUrlEncoded
@POST("/yycp-vehicle-management-service/tool/badcase/add")
suspend fun post(@FieldMap map: Map<String, String>): Response<PostResult>
@GET("/yycp-vehicle-management-service/tool/badcase/reasons")
suspend fun get(): Response<BadCaseEntity>
}
@Keep
class BadCaseEntity {
var code: Int = -1
var data: List<Reason>? = null
var msg: String? = null
var success: Boolean = false
var total: Int = -1
@Expose(serialize = false, deserialize = false)
var isBuildIn: Boolean = false
@Keep
class Reason {
var id: String? = null
var reason: String? = null
/**
* 业务字段,不参与序列化和反序列化
*/
@Expose(deserialize = false, serialize = false)
var isChecked: Boolean = false
}
}
@Keep
class PostResult {
var code: Int = -1
var msg: String? = null
var data: Array<String>? = null
var success: Boolean = false
override fun toString(): String {
return "Result(code=$code, msg=$msg, data=${data?.contentToString()}, success=$success)"
}
}
private fun getHost(): String = if (getNetMode() == DebugConfig.NET_MODE_RELEASE) "http://dzt.zhidaozhixing.com" else "http://front.zdjs-private-test.myghost.zhidaoauto.com"
internal suspend fun post(map: Map<String, String>): Response<PostResult> {
return RetrofitFactory.getInstance(getHost()).create(BadCaseApi::class.java).post(map)
}
private suspend fun get(): Response<BadCaseEntity>? {
return try { RetrofitFactory.getInstance(getHost()).create(BadCaseApi::class.java).get() } catch (t: Throwable) { t.printStackTrace(); null}
}
private suspend fun updateCache(entity: BadCaseEntity) = suspendCancellableCoroutine<Unit> {
try {
val future = ThreadUtils.getIoPool().submit {
try {
val gson = GsonUtil.jsonFromObject(entity)
Sp.saveBody(gson)
it.resumeWith(success(Unit))
} catch (t: Throwable) {
it.resumeWith(failure(t))
}
}
it.invokeOnCancellation {
future.cancel(true)
}
} catch (e: Throwable) {
it.resumeWith(failure(e))
}
}
private suspend fun getCache(): BadCaseEntity? = suspendCancellableCoroutine {
try {
val body = Sp.getBody()
if (body != null && body.isNotEmpty()) {
val future = ThreadUtils.getIoPool().submit {
try {
val result = GsonUtil.objectFromJson(body, BadCaseEntity::class.java)
it.resumeWith(success(result))
} catch (t: Throwable) {
it.resumeWith(success(null))
}
}
it.invokeOnCancellation {
future.cancel(true)
}
}
} catch (t: Throwable) {
it.resumeWith(success(null))
}
}
private fun getBuildIn(): BadCaseEntity = BadCaseEntity().also { itx ->
val data = mutableListOf<Reason>()
data += Reason().also {
it.reason = "变道有干扰"
}
data += Reason().also {
it.reason = "遇红绿灯未停车"
}
data += Reason().also {
it.reason = "遇障碍物未停车"
}
itx.data = data
itx.isBuildIn = true
}
internal object Sp {
private val sp by lazy {
Utils.getApp().getSharedPreferences("bad_case_prefs", MODE_PRIVATE)
}
@SuppressLint("ApplySharedPref")
fun saveBody(body: String) {
sp.edit().putString("prefs", body).commit()
}
fun getBody(): String? {
return sp.getString("prefs", null)
}
}
class AutoPilotBadCaseView: ConstraintLayout {
private var dismiss: OnDismissCallback? = null
private var select: OnSelectCallback? = null
private var cases: List<Reason>? = null
private var selectCase: Reason? = null
private val scope = CoroutineScope(Handler().asCoroutineDispatcher() + SupervisorJob())
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
LayoutInflater.from(context).inflate(R.layout.layout_badcase_collect, this, true)
background = ColorDrawable(Color.parseColor("#F0151D41"))
isClickable = true
layoutParams = ViewGroup.LayoutParams(960.toPixels().toInt(), 1528.toPixels().toInt())
close?.onClick {
dismiss?.invoke()
}
cancel?.also {
it.background = shape(solid = Color.parseColor("#3B4577"), radius = 16)
it.onClick {
dismiss?.invoke()
}
}
ok?.also {
val enabled = gradient(radius = 16.toPixels().toInt(), orientation = GradientDrawable.Orientation.LEFT_RIGHT, centerX = 0.06f, startColor = Color.rgb(35, 146, 252), endColor = Color.rgb(28, 75, 252))
val disabled = gradient(radius = 16.toPixels().toInt(), orientation = GradientDrawable.Orientation.LEFT_RIGHT, centerX = 0.06f, startColor = Color.rgb(24, 71, 129), endColor = Color.rgb(21, 46, 129))
it.background = object : StateListDrawable() {}.also { itx ->
itx.addState(intArrayOf(android.R.attr.state_enabled), enabled)
itx.addState(StateSet.WILD_CARD, disabled)
}
it.onClick {
val case = selectCase
if (case != null) {
select?.invoke(case)
}
}
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
val adapter = rv_take_over?.adapter
if (adapter != null && adapter.itemCount > 0) {
return
}
scope.launch {
showLoading()
try {
get()?.takeIf { it.isSuccessful && it.body() != null && it.body()?.code == 200 }?.let {
val entity = it.body()!!
try {
updateCache(entity)
} catch (t: Throwable) {
t.printStackTrace()
} finally {
updateBadCaseList(entity)
}
}
?:
getCache()?.also {
updateBadCaseList(it)
}
?:
updateBadCaseList(getBuildIn())
} finally {
hideLoading()
}
}
}
private fun updateBadCaseList(body: BadCaseEntity) {
cases = body.data
rv_take_over?.let {
it.layoutManager = LinearLayoutManager(it.context, LinearLayoutManager.VERTICAL, false)
it.adapter = object : RecyclerView.Adapter<BadCaseViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BadCaseViewHolder = BadCaseViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_badcase_item, parent, false))
override fun onBindViewHolder(holder: BadCaseViewHolder, position: Int) {
val cases = cases
if (cases == null || cases.isEmpty()) {
return
}
if (position >= cases.size) {
return
}
val case = cases[position]
holder.bindData(case)
}
override fun getItemCount(): Int = cases?.size ?: 0
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
scope.cancel("Cancel all for AutoPilotBadCaseView#onDetachedFromWindow")
}
private fun showLoading() {
pb?.let {
it.visibility = View.VISIBLE
}
}
private fun hideLoading() {
pb?.let {
it.visibility = View.INVISIBLE
}
}
private inner class BadCaseViewHolder(item: View) : RecyclerView.ViewHolder(item) {
private val check: ImageView = item.findViewById(R.id.check)
private val reason: TextView = item.findViewById(R.id.reason)
init {
check.background = StateListDrawable().also {
it.addState(intArrayOf(android.R.attr.state_selected), ContextCompat.getDrawable(itemView.context, R.drawable.icon_ap_badcase_check))
it.addState(StateSet.WILD_CARD, ContextCompat.getDrawable(itemView.context, R.drawable.icon_ap_badcase_default))
}
}
@SuppressLint("NotifyDataSetChanged")
fun bindData(case: Reason) {
check.isSelected = case.isChecked
reason.text = case.reason ?: ""
if (case.isChecked) {
ok?.isSelected = true
}
itemView.onClick {
case.isChecked = !case.isChecked
selectCase = case
cancelOtherChecked(case)
ok?.isEnabled = hasCheckedItem()
rv_take_over?.adapter?.notifyDataSetChanged()
}
}
private fun hasCheckedItem(): Boolean = cases?.find { it.isChecked } != null
private fun cancelOtherChecked(case: Reason) {
val cases = cases
if (cases == null || cases.isEmpty()) {
return
}
cases.filterNot { it == case }.forEach {
it.isChecked = false
}
}
}
fun onDismiss(dismiss:() -> Unit) {
this.dismiss = dismiss
}
fun onSelect(cb:(Reason) -> Unit) {
this.select = cb
}
}

View File

@@ -0,0 +1,109 @@
package com.mogo.eagle.core.function.hmi.ui.turnlight
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.os.Looper
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.DecelerateInterpolator
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import kotlinx.android.synthetic.main.view_brake_light_status.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
/**
* @description
*
* @author lixiaopeng
* @since 2022/1/10
*/
class BrakeViewStatus @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
init {
LayoutInflater.from(context).inflate(R.layout.view_brake_light_status, this, true)
}
private var isBrake :Boolean = false
/**
* 刹车动画
*/
fun setBrakeLight(brakeLight: Int) {
if (brakeLight == 1) { //TODO 暂时还不知道数据,如果一直猜会怎样?
if (!isBrake) {
var appearAnimation = AlphaAnimation(0f, 1f)
appearAnimation.duration = 300
layout_brake.startAnimation(appearAnimation)
image_brake.startAnimation(appearAnimation)
tv_brake.startAnimation(appearAnimation)
layout_brake.visibility = View.VISIBLE
image_brake.visibility = View.VISIBLE
tv_brake.visibility = View.VISIBLE
isBrake = true
}
} else { //不踩刹车,就消失
if (isBrake) {
isBrake = false
GlobalScope.launch(Dispatchers.Main) {
scaleImageAndTv()
}
var disappearAnimation = AlphaAnimation(1f, 0f)
disappearAnimation.duration = 1200
layout_brake.startAnimation(disappearAnimation)
image_brake.startAnimation(disappearAnimation)
tv_brake.startAnimation(disappearAnimation)
disappearAnimation.setAnimationListener(object: Animation.AnimationListener{
override fun onAnimationRepeat(p0: Animation?) {
}
override fun onAnimationStart(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
layout_brake.visibility = View.GONE
image_brake.visibility = View.GONE
tv_brake.visibility = View.GONE
stopAnimate()
}
})
}
}
}
private fun scaleImageAndTv() {
val animatorSuofang = AnimatorSet() //组合动画
val scaleX = ObjectAnimator.ofFloat(image_brake, "scaleX", 1f, 1.3f)
val scaleY = ObjectAnimator.ofFloat(image_brake, "scaleY", 1f, 1.3f)
animatorSuofang.duration = 200
animatorSuofang.interpolator = DecelerateInterpolator()
animatorSuofang.play(scaleX).with(scaleY)
animatorSuofang.start()
val scaleXTv = ObjectAnimator.ofFloat(tv_brake, "scaleX", 1f, 1.3f)
val scaleYTv = ObjectAnimator.ofFloat(tv_brake, "scaleY", 1f, 1.3f)
animatorSuofang.duration = 200
animatorSuofang.interpolator = DecelerateInterpolator()
animatorSuofang.play(scaleXTv).with(scaleYTv)
animatorSuofang.start()
}
private fun stopAnimate() {
tv_brake.clearAnimation()
image_brake.clearAnimation()
}
}

View File

@@ -0,0 +1,155 @@
package com.mogo.eagle.core.function.hmi.ui.turnlight
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import kotlinx.android.synthetic.main.view_brake_light_status.view.*
import kotlinx.android.synthetic.main.view_turn_light_status.view.*
/**
* @description
*
* @author lixiaopeng
* @since 2022/1/10
*/
class TurnLightViewStatus @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
init {
LayoutInflater.from(context).inflate(R.layout.view_turn_light_status, this, true)
}
private var isShowNormalBg :Boolean = false
private var isLeftLight :Boolean = false
private var isRightLight :Boolean = false
private var isDisappare :Boolean = false
/**
* 转向灯动画
*/
fun setTurnLight(directionLight: Int) {
if (!isShowNormalBg && (directionLight == 1 || directionLight == 2)) {
showNormalAnimation()
isShowNormalBg = true
}
//根据左右进行显示和隐藏,实际要判断每个来的时间和频度
if (directionLight == 1) { //左转向
if (!isLeftLight) {
left_select_image.visibility = View.VISIBLE
right_select_image.visibility = View.GONE
right_select_image.clearAnimation()
setAnimation(left_select_image)
isLeftLight = true
isRightLight = false
isDisappare = false
}
} else if (directionLight == 2) { //右转向
if (!isRightLight) {
left_select_image.visibility = View.GONE
right_select_image.visibility = View.VISIBLE
left_select_image.clearAnimation()
setAnimation(right_select_image)
isRightLight = true
isLeftLight = false
isDisappare = false
}
} else { //消失
if (!isDisappare) {
animationDisappear()
isDisappare = true
isShowNormalBg = false
isLeftLight = false
isDisappare = false
}
}
}
//显示背景
private fun showNormalAnimation() {
val appearAnimation = AlphaAnimation(0f, 1.0f)
appearAnimation.duration = 600
val appearAnimationImage = AlphaAnimation(0f, 1.0f)
appearAnimation.duration = 1000
turn_light_layout.startAnimation(appearAnimation)
left_nor_image.startAnimation(appearAnimationImage)
right_nor_image.startAnimation(appearAnimationImage)
turn_light_layout.visibility = View.VISIBLE
left_nor_image.visibility = View.VISIBLE
right_nor_image.visibility = View.VISIBLE
}
//消失动画,当转向等数据为空时候
private fun animationDisappear() {
left_select_image.visibility = View.GONE
right_select_image.visibility = View.GONE
left_select_image.clearAnimation()
right_select_image.clearAnimation()
val disappearAnimationLeft = AlphaAnimation(1.0f, 0f)
disappearAnimationLeft.duration = 300
val disappearAnimationBg = AlphaAnimation(1.0f, 0f)
disappearAnimationBg.duration = 1200
left_nor_image.startAnimation(disappearAnimationLeft)
right_nor_image.startAnimation(disappearAnimationLeft)
turn_light_layout.startAnimation(disappearAnimationBg)
disappearAnimationLeft.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(p0: Animation?) {
}
override fun onAnimationStart(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
left_nor_image.visibility = View.GONE
right_nor_image.visibility = View.GONE
}
})
disappearAnimationBg.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(p0: Animation?) {
}
override fun onAnimationStart(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
turn_light_layout.visibility = View.GONE
stopAnimate()
}
})
}
//实现图片闪烁效果
private fun setAnimation(imageView: ImageView) {
val animation = AlphaAnimation(1.0f, 0f)
animation.duration = 600
animation.interpolator = LinearInterpolator()
animation.repeatCount = Animation.INFINITE
animation.repeatMode = Animation.REVERSE
imageView.startAnimation(animation)
}
private fun stopAnimate() {
turn_light_layout.clearAnimation()
left_nor_image.clearAnimation()
right_nor_image.clearAnimation()
}
}

View File

@@ -0,0 +1,120 @@
package com.mogo.eagle.core.function.hmi.ui.utils
import android.text.TextUtils
import com.mogo.eagle.core.function.hmi.ui.logcatch.LogLine
import java.util.regex.Pattern
class SearchCriteria(inputQuery: CharSequence?) {
private var pid = -1
private var tag: String? = null
private val searchText: String
private var searchTextAsInt = -1
val isEmpty: Boolean
get() = pid == -1 && TextUtils.isEmpty(tag) && TextUtils.isEmpty(searchText)
fun matches(logLine: LogLine): Boolean {
// consider the criteria to be ANDed
if (!checkFoundPid(logLine)) {
return false
}
return if (!checkFoundTag(logLine)) {
false
} else checkFoundText(logLine)
}
private fun checkFoundText(logLine: LogLine): Boolean {
return (TextUtils.isEmpty(searchText)
|| searchTextAsInt != -1 && searchTextAsInt == logLine.processId
|| logLine.tag != null && containsIgnoreCase(logLine.tag, searchText)
|| logLine.logOutput != null && containsIgnoreCase(
logLine.logOutput,
searchText
))
}
private fun checkFoundTag(logLine: LogLine): Boolean {
return (TextUtils.isEmpty(tag)
|| logLine.tag != null && containsIgnoreCase(logLine.tag, tag))
}
private fun checkFoundPid(logLine: LogLine): Boolean {
return pid == -1 || logLine.processId == pid
}
fun nullToEmpty(str: CharSequence?): String {
return str?.toString() ?: ""
}
/**
* same as String.contains, but ignores case.
*
* @param str
* @param query
* @return
*/
fun containsIgnoreCase(str: String?, query: String?): Boolean {
if (str != null && query != null) {
val limit = str.length - query.length + 1
for (i in 0 until limit) {
if (matchesIgnoreCase(str, query, i)) {
return true
}
}
}
return false
}
private fun matchesIgnoreCase(str: String, query: String, startingAt: Int): Boolean {
val len = query.length
for (i in 0 until len) {
if (Character.toUpperCase(query[i]) != Character.toUpperCase(str[startingAt + i])) {
return false
}
}
return true
}
companion object {
private const val PID_KEYWORD = "pid:"
private const val TAG_KEYWORD = "tag:"
private val PID_PATTERN = Pattern.compile("$PID_KEYWORD:(\\d+)", Pattern.CASE_INSENSITIVE)
private val TAG_PATTERN = Pattern.compile("$TAG_KEYWORD:(\"[^\"]+\"|\\S+)", Pattern.CASE_INSENSITIVE)
}
init {
// check for the "pid" keyword
val query = StringBuilder(nullToEmpty(inputQuery))
val pidMatcher = PID_PATTERN.matcher(query)
if (pidMatcher.find()) {
try {
pid = pidMatcher.group(1).toInt()
query.replace(pidMatcher.start(), pidMatcher.end(), "") // detach
// from
// search
// string
} catch (ignore: NumberFormatException) {
}
}
// check for the "tag" keyword
val tagMatcher = TAG_PATTERN.matcher(query)
if (tagMatcher.find()) {
tag = tagMatcher.group(1)
tag?.let {
if (it.startsWith("\"") && it.endsWith("\"")) {
tag = it.substring(1, it.length - 1) // detach quotes
}
}
query.replace(tagMatcher.start(), tagMatcher.end(), "") // detach
}
// everything else becomes a search term
searchText = query.toString().trim { it <= ' ' }
try {
searchTextAsInt = searchText.toInt()
} catch (ignore: NumberFormatException) {
}
}
}

View File

@@ -0,0 +1,68 @@
package com.mogo.eagle.core.function.hmi.ui.utils
import android.content.Context
import android.util.Log
import android.util.SparseIntArray
import androidx.core.content.ContextCompat
import com.mogo.eagle.core.function.hmi.R
object TagColorUtil {
private val TEXT_COLOR = SparseIntArray(6)
private val TEXT_COLOR_EXPAND = SparseIntArray(6)
private val LEVEL_COLOR = SparseIntArray(6)
private val LEVEL_BG_COLOR = SparseIntArray(6)
@JvmStatic
fun getTextColor(context: Context?, level: Int, expand: Boolean): Int {
val map = if (expand) TEXT_COLOR_EXPAND else TEXT_COLOR
var result = map[level]
if (result == null) {
result = map[Log.VERBOSE]
}
return ContextCompat.getColor(context!!, result)
}
@JvmStatic
fun getLevelBgColor(context: Context?, level: Int): Int {
var result = LEVEL_BG_COLOR[level]
if (result == null) {
result = LEVEL_BG_COLOR[Log.VERBOSE]
}
return ContextCompat.getColor(context!!, result)
}
@JvmStatic
fun getLevelColor(context: Context?, level: Int): Int {
var result = LEVEL_COLOR[level]
if (result == null) {
result = LEVEL_COLOR[Log.VERBOSE]
}
return ContextCompat.getColor(context!!, result)
}
init {
TEXT_COLOR.put(Log.DEBUG, R.color.color_000000)
TEXT_COLOR.put(Log.INFO, R.color.color_000000)
TEXT_COLOR.put(Log.VERBOSE, R.color.color_000000)
TEXT_COLOR.put(Log.ASSERT, R.color.color_8F0005)
TEXT_COLOR.put(Log.ERROR, R.color.color_FF0006)
TEXT_COLOR.put(Log.WARN, R.color.color_0099dd)
TEXT_COLOR_EXPAND.put(Log.DEBUG, R.color.color_FFFFFF)
TEXT_COLOR_EXPAND.put(Log.INFO, R.color.color_FFFFFF)
TEXT_COLOR_EXPAND.put(Log.VERBOSE, R.color.color_FFFFFF)
TEXT_COLOR_EXPAND.put(Log.ASSERT, R.color.color_8F0005)
TEXT_COLOR_EXPAND.put(Log.ERROR, R.color.color_FF0006)
TEXT_COLOR_EXPAND.put(Log.WARN, R.color.color_0099dd)
LEVEL_BG_COLOR.put(Log.DEBUG, R.color.background_debug)
LEVEL_BG_COLOR.put(Log.ERROR, R.color.background_error)
LEVEL_BG_COLOR.put(Log.INFO, R.color.background_info)
LEVEL_BG_COLOR.put(Log.VERBOSE, R.color.background_verbose)
LEVEL_BG_COLOR.put(Log.WARN, R.color.background_warn)
LEVEL_BG_COLOR.put(Log.ASSERT, R.color.background_wtf)
LEVEL_COLOR.put(Log.DEBUG, R.color.foreground_debug)
LEVEL_COLOR.put(Log.ERROR, R.color.foreground_error)
LEVEL_COLOR.put(Log.INFO, R.color.foreground_info)
LEVEL_COLOR.put(Log.VERBOSE, R.color.foreground_verbose)
LEVEL_COLOR.put(Log.WARN, R.color.foreground_warn)
LEVEL_COLOR.put(Log.ASSERT, R.color.foreground_wtf)
}
}

View File

@@ -7,7 +7,6 @@ import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
import com.mogo.eagle.core.data.autopilot.AutopilotGuardianStatusInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStationInfo
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager
@@ -16,8 +15,8 @@ import com.mogo.eagle.core.function.call.hmi.CallerHmiListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.utilcode.util.LogUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.module.common.MogoApisHandler
import com.mogo.utils.UiThreadHandler
import kotlinx.android.synthetic.main.view_autopilot_status.view.*
/**
@@ -26,11 +25,11 @@ import kotlinx.android.synthetic.main.view_autopilot_status.view.*
* 自动驾驶状态按钮
*/
class AutoPilotStatusView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet
context: Context,
attrs: AttributeSet
) : ConstraintLayout(context, attrs),
View.OnClickListener,
IMoGoAutopilotStatusListener {
View.OnClickListener,
IMoGoAutopilotStatusListener {
private val TAG = "AutopilotStatusView"
@@ -80,14 +79,17 @@ class AutoPilotStatusView @JvmOverloads constructor(
private fun startAutoPilot() {
// TODO 测试数据,真实情况需要业务自己传入控制数据
val currentAutopilot =
AutopilotControlParameters()
AutopilotControlParameters()
currentAutopilot.startName = "HYKXC"
currentAutopilot.endName = "HYJC"
currentAutopilot.isSpeakVoice = false
currentAutopilot.startLatLon =
AutopilotControlParameters.AutoPilotLonLat(MogoApisHandler.getInstance().apis.adasControllerApi.lastLat, MogoApisHandler.getInstance().apis.adasControllerApi.lastLon)
AutopilotControlParameters.AutoPilotLonLat(
MogoApisHandler.getInstance().apis.adasControllerApi.lastLat,
MogoApisHandler.getInstance().apis.adasControllerApi.lastLon
)
currentAutopilot.endLatLon =
AutopilotControlParameters.AutoPilotLonLat(26.819716071924688, 112.57715442110867)
AutopilotControlParameters.AutoPilotLonLat(26.819716071924688, 112.57715442110867)
currentAutopilot.vehicleType = 10
CallerAutoPilotManager.startAutoPilot(currentAutopilot)
@@ -126,11 +128,6 @@ class AutoPilotStatusView @JvmOverloads constructor(
setAutoPilotStatus(autoPilotStatusInfo.state)
}
override fun onAutopilotArriveAtStation(autopilotWayArrive: AutopilotStationInfo?) {
}
override fun onAutopilotGuardian(guardianInfo: AutopilotGuardianStatusInfo?) {
}

View File

@@ -0,0 +1,77 @@
package com.mogo.eagle.core.function.hmi.ui.widget
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.notification.WarningFloat
import kotlinx.android.synthetic.main.view_check_system.view.*
class CheckSystemView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoAutopilotStatusListener {
companion object {
const val TAG = "CheckSystemView"
}
private var connectStatus = false
init {
LayoutInflater.from(context).inflate(R.layout.view_check_system, this, true)
initView()
}
private fun initView() {
//todo view状态注意消息回执
viewCheckShutDown.setOnClickListener {
//dialog
// showSystemOperationWindow()
}
viewCheckReboot.setOnClickListener {
//dialog
// showSystemOperationWindow()
}
}
private fun showSystemOperationWindow(view: View) {
WarningFloat.with(context).setGravity(Gravity.CENTER).setLayout(view)
.setImmersionStatusBar(true).show()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerAutoPilotStatusListenerManager.removeListener(TAG)
}
override fun onAutopilotStatusResponse(autoPilotStatusInfo: AutopilotStatusInfo) {
connectStatus = autoPilotStatusInfo.connectStatus
setViewStatus()
}
private fun setViewStatus() {
if (connectStatus) {
viewCheckShutDown.requestFocus()
viewCheckShutDown.isClickable = true
viewCheckReboot.requestFocus()
viewCheckReboot.isClickable = true
} else {
viewCheckShutDown.isClickable = false
viewCheckReboot.isClickable = false
}
}
}

View File

@@ -0,0 +1,191 @@
package com.mogo.eagle.core.function.hmi.ui.widget
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.TypedArray
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.OvershootInterpolator
import androidx.core.content.ContextCompat
import com.mogo.eagle.core.function.hmi.R
/**
* @author XuXinChao
* @description 自定义圆形进度条
* @since: 2022/1/14
*/
class CircularProgressView @JvmOverloads constructor(
context: Context, attrs: AttributeSet?, defStyleAttr : Int)
: View(context, attrs, defStyleAttr){
val typedArray : TypedArray = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressView)
// 绘制画笔
private val mBackPaint : Paint = Paint()
private val mProgPaint : Paint = Paint()
// 绘制区域
private var mRectF : RectF? = null
// 圆环渐变色
private var mColorArray : IntArray?=null
// 圆环进度(0-100) 初始化进度
private var mProgress : Int = typedArray.getInteger(R.styleable.CircularProgressView_progress, 0)
constructor(context : Context) : this(context,null)
constructor(context : Context,attrs : AttributeSet?) :this(context, attrs, 0)
init {
// 初始化背景圆环画笔
mBackPaint.style = Paint.Style.STROKE // 只描边,不填充
mBackPaint.strokeCap = Paint.Cap.ROUND // 设置圆角
mBackPaint.isAntiAlias = true // 设置抗锯齿
mBackPaint.isDither = true // 设置抖动
mBackPaint.strokeWidth = typedArray.getDimension(R.styleable.CircularProgressView_backWidth, 5.0f)
mBackPaint.color = typedArray.getColor(R.styleable.CircularProgressView_backColor, Color.LTGRAY)
// 初始化进度圆环画笔
mProgPaint.style = Paint.Style.STROKE // 只描边,不填充
mProgPaint.strokeCap = Paint.Cap.ROUND // 设置圆角
mProgPaint.isAntiAlias = true // 设置抗锯齿
mProgPaint.isDither = true // 设置抖动
mProgPaint.strokeWidth = typedArray.getDimension(R.styleable.CircularProgressView_progWidth, 10.0f)
mProgPaint.color = typedArray.getColor(R.styleable.CircularProgressView_progColor, Color.BLUE)
// 初始化进度圆环渐变色
val startColor = typedArray.getColor(R.styleable.CircularProgressView_progStartColor, -1)
val firstColor = typedArray.getColor(R.styleable.CircularProgressView_progFirstColor, -1)
if(startColor != -1 && firstColor != -1){
mColorArray = intArrayOf(startColor,firstColor)
}else{
mColorArray = null
}
typedArray.recycle();
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val viewWide = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
val viewHigh = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
val mRectLength =
((if (viewWide > viewHigh) viewHigh else viewWide) - if (mBackPaint.strokeWidth > mProgPaint.strokeWidth) mBackPaint.strokeWidth else mProgPaint.strokeWidth).toInt()
val mRectL = getPaddingLeft() + (viewWide - mRectLength) / 2
val mRectT = getPaddingTop() + (viewHigh - mRectLength) / 2
mRectF = RectF(mRectL.toFloat(), mRectT.toFloat(), (mRectL + mRectLength).toFloat(),
(mRectT + mRectLength).toFloat())
// 设置进度圆环渐变色
mColorArray?.let {
mProgPaint.shader = LinearGradient(
0.0f, 0.0f, 0.0f,
measuredWidth.toFloat(), it, null, Shader.TileMode.MIRROR)
}
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.let {
mRectF?.let { it1 -> it.drawArc(it1, 0.0f, 360.0f, false, mBackPaint) }
mRectF?.let { it1 -> it.drawArc(it1, 275.0f,
(360 * mProgress / 100).toFloat(), false, mProgPaint) }
}
}
/**
* 获取当前进度
* @return 当前进度0-100
*/
fun getProgress() : Int{
return mProgress
}
/**
* 设置当前进度
* @param progress 当前进度0-100
*/
fun setProgress(progress : Int){
mProgress = progress
invalidate()
}
/**
* 设置当前进度并展示进度动画。如果动画时间小于等于0则不展示动画
* @param progress 当前进度0-100
* @param animTime 动画时间(毫秒)
*/
fun setProgress(progress : Int, animTime : Long){
if (animTime<=0){
setProgress(progress)
} else{
val animator = ValueAnimator.ofInt(mProgress, progress)
animator.addUpdateListener{
mProgress = it.animatedValue as Int
invalidate()
}
animator.interpolator = OvershootInterpolator()
animator.duration = animTime
animator.start()
}
}
/**
* 设置背景圆环宽度
* @param width 背景圆环宽度
*/
fun setBackWidth(width : Int){
mBackPaint.strokeWidth = width.toFloat()
invalidate()
}
/**
* 设置背景圆环颜色
* @param color 背景圆环颜色
*/
fun setBackColor(color : Int){
mBackPaint.color = ContextCompat.getColor(context,color)
invalidate()
}
/**
* 设置进度圆环宽度
* @param width 进度圆环宽度
*/
fun setProgWidth(width : Int){
mProgPaint.strokeWidth = width.toFloat()
invalidate()
}
/**
* 设置进度圆环颜色
* @param color 景圆环颜色
*/
fun setProgColor(color : Int){
mProgPaint.color = ContextCompat.getColor(context,color)
mProgPaint.shader = null
invalidate()
}
fun setProgColor(startColor : Int,endColor: Int){
mColorArray = intArrayOf(ContextCompat.getColor(context,startColor),ContextCompat.getColor(context,endColor))
mColorArray?.let {
mProgPaint.shader = LinearGradient(0f, 0f, 0f,
getMeasuredWidth().toFloat(), it, null, Shader.TileMode.MIRROR)
}
}
fun setProgColor(colorArray : IntArray){
colorArray.let {
if(it.size<2){
return
}
mColorArray = it.copyOf()
mColorArray?.let{
mProgPaint.shader = LinearGradient(0f, 0f, 0f,
getMeasuredWidth().toFloat(), it, null, Shader.TileMode.MIRROR)
}
}
}
}

View File

@@ -1,28 +1,18 @@
package com.mogo.eagle.core.function.hmi.ui.widget
import android.animation.Animator
import android.content.Context
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.view.WindowManager
import android.view.animation.OvershootInterpolator
import android.widget.FrameLayout
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.eagle.core.data.constants.MogoServicePaths
import com.mogo.eagle.core.data.map.MogoLatLng
import com.mogo.eagle.core.function.hmi.R
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.setting.DebugSettingView
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.map.navi.IMogoCarLocationChangedListener2
import com.mogo.service.IMogoServiceApis
import com.mogo.service.statusmanager.StatusDescriptor
import com.mogo.utils.UiThreadHandler
import java.util.*
/**

View File

@@ -0,0 +1,181 @@
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.data.autopilot.AutopilotStatusInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.hmi.R
import com.mogo.eagle.core.function.hmi.ui.tools.AdUpgradeDialog
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.AppUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import kotlinx.android.synthetic.main.view_system_version.view.*
/**
* @author XuXinChao
* @description 工具箱-系统版本(鹰眼版本、工控机版本)视图
* @since: 2022/1/13
*/
class SystemVersionView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), IMoGoAutopilotStatusListener {
companion object {
const val TAG = "SystemVersionView"
const val AD_LATEST_VERSION = 1 //AD最新版
const val AD_DOWNING = 2 //AD下载中
const val AD_DOWNLOAD_FAIL = 3 //AD下载失败
const val AD_UPGRADEABLE = 4 //AD下载成功可升级状态
const val AD_UPGRADING = 5 //AD升级中
const val AD_UPGRADE_FAIL = 6 //AD升级失败
}
private var connectStatus = false
private var adUpgradeDialog : AdUpgradeDialog? = null
private var adStatus = AD_LATEST_VERSION //工控机默认为最新版
init {
LayoutInflater.from(context).inflate(R.layout.view_system_version, this, true)
initView()
}
private fun initView(){
showCurrentPadVersion()
showCurrentAdVersion()
//鹰眼版本视图点击事件
ivPadVersion.setOnClickListener {
Logger.i(TAG,"pad version view clicked")
}
//工控机版本视图点击事件
ivAdVersion.setOnClickListener {
Logger.i(TAG,"ad version view clicked")
when(adStatus){
AD_LATEST_VERSION -> {
//最新版
}
AD_DOWNING -> {
//下载中
//TODO 需要向工控机要下载时间
ToastUtils.showShort("新版本下载中预计XX分钟下载完成")
}
AD_DOWNLOAD_FAIL -> {
//下载失败
}
AD_UPGRADEABLE -> {
//下载成功,可升级,点击弹起升级确认弹窗
if(adUpgradeDialog == null){
adUpgradeDialog = AdUpgradeDialog(context)
}
adUpgradeDialog?.showUpgradeDialog()
}
AD_UPGRADING -> {
//升级中
ToastUtils.showShort("新版本升级中预计5分钟升级完成")
}
AD_UPGRADE_FAIL -> {
//升级失败
ToastUtils.showShort("升级失败,请联系运维人员")
}
}
}
}
/**
* 展示当前鹰眼版本
*/
private fun showCurrentPadVersion(){
tvPadVersionContent?.let {
it.text = AppUtils.getAppVersionName()
}
}
/**
* 展示当前工控机版本
*/
private fun showCurrentAdVersion(){
tvAdVersionContent?.let {
// it.text = AdasManager.getInstance().getAdasConfig().getDockVersion())
}
}
/**
* 展示工控机下载状态信息
* @param downloadVersion 下载版本
* @param downloadStatus 下载状态(0:下载完成1:正在下载2:下载失败)
* @param downloadProgress 下载进度
*/
fun showAdDownloadStatus(downloadVersion : String,downloadStatus : Int,downloadProgress : Int){
if(downloadStatus==0){
//下载完成处于可升级状态展示“可升级”角标将AD背景变为蓝色并隐藏下载进度条
adStatus = AD_UPGRADEABLE
ivAdStatus?.setImageResource(R.drawable.icon_upgradeable)
ivAdVersion?.setBackgroundResource(R.drawable.version_upgradeable_background)
adCircularProgressView?.visibility = View.GONE
}else if(downloadStatus==1){
//正在下载,展示“下载中”角标,展示进度条,并设置当前下载进度
adStatus = AD_DOWNING
ivAdStatus?.setImageResource(R.drawable.icon_downloading)
adCircularProgressView?.let {
it.visibility = View.VISIBLE
it.setProgress(downloadProgress)
}
}else if(downloadStatus==2){
//下载失败,目前暂时将状态设为“最新版”角标,并隐藏进度条
adStatus = AD_DOWNLOAD_FAIL
ivAdStatus?.setImageResource(R.drawable.icon_latest_version)
adCircularProgressView?.visibility = View.GONE
}
}
/**
* 展示工控机升级状态信息
* @param upgradeStatus 升级状态true代表升级成功、false代表升级不成功
*/
fun showAdUpgradeStatus(upgradeStatus : Boolean){
if(upgradeStatus){
//AD升级成功工控机图标展示最新版样式
adStatus = AD_LATEST_VERSION
ivAdStatus?.setImageResource(R.drawable.icon_latest_version)
}else{
//AD升级失败工控机图标展示升级失败样式
adStatus = AD_UPGRADE_FAIL
ivAdStatus?.setImageResource(R.drawable.icon_upgrade_failed)
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerAutoPilotStatusListenerManager.removeListener(TAG)
}
override fun onAutopilotStatusResponse(autoPilotStatusInfo: AutopilotStatusInfo) {
connectStatus = autoPilotStatusInfo.connectStatus
setViewStatus()
}
private fun setViewStatus(){
}
}

View File

@@ -6,7 +6,7 @@ import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import com.mogo.utils.UiThreadHandler
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.view_traffic_light_vr.view.*
/**

View File

@@ -9,7 +9,7 @@ import androidx.annotation.Nullable
import androidx.annotation.StringRes
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import com.mogo.utils.UiThreadHandler
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.notification_v2x_msg_vr.view.*
/**

View File

@@ -7,8 +7,8 @@ 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.UiThreadHandler
import com.mogo.utils.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.module_hmi_warning_v2x.view.*
/**

View File

@@ -5,7 +5,7 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.hmi.R
import com.mogo.utils.UiThreadHandler
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import kotlinx.android.synthetic.main.view_vip_identification.view.*
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="90px" />
<solid android:color="#4C6873A5" />
</shape>
<size android:width="2dp"/>
<solid android:color="#CCCCCC"/>
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="#FFFFFF" />
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_checked_background" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_normal_background" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_checked_background_left" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_normal_background_left" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_checked_background_middle" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_normal_background_middle" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_checked_background_right" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_normal_background_right" android:state_checked="false" />
</selector>

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