Merge branch 'dev_robotaxi-d_231205_6.2.6' into dev_robotaxi-d_231218_6.2.6

# Conflicts:
#	OCH/bus/passenger/src/jinlvvan/java/com/mogo/och/bus/passenger/MogoOCHBusPassenger.java
This commit is contained in:
yangyakun
2023-12-18 11:28:25 +08:00
1189 changed files with 7194 additions and 1825 deletions

View File

@@ -0,0 +1,24 @@
package com.mogo.och.common.module
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.mogo.och.common.module.test", appContext.packageName)
}
}

View File

@@ -0,0 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.och.common.module">
<application>
<receiver android:name="com.mogo.och.common.module.debug.BizBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.mogo.launcher.debug" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -0,0 +1,38 @@
package com.mogo.och.common.module.debug
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
class BizBroadcastReceiver: BroadcastReceiver() {
private var mContext: Context? = null
companion object {
private const val TAG = "BizBroadcastReceiver"
// 类型
private const val type = "type"
// 数据源
private const val sourceFile = "path"
// 数据频率 默认1s一次 -1 发送一次
private const val frequencyKey = "fre"
}
override fun onReceive(context: Context, intent: Intent) {
try {
mContext = context
val type = intent.getStringExtra(type)
val frequency = intent.getIntExtra(frequencyKey,1)
val sourceFilePath = intent.getStringExtra(sourceFile)
CallerLogger.d("${SceneConstant.M_OCHCOMMON}${TAG}",
"类型:${type} 频率:${frequency} 命令所在文件:${sourceFilePath}"
)
DebugDataDispatch.disPathc(type,frequency,sourceFilePath,intent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View File

@@ -0,0 +1,259 @@
package com.mogo.och.common.module.debug
import android.content.Intent
import android.os.Environment
import android.os.SystemClock
import chassis.Chassis
import chassis.Chassis.DoorNumber
import chassis.VehicleStateOuterClass
import com.amap.api.maps.model.LatLng
import com.google.gson.reflect.TypeToken
import com.mogo.eagle.core.data.enums.DataSourceType
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotStatisticsListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisDoorStateListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisGnssListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLamplightListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningActionsListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningRottingListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.ActivityUtils
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.och.common.module.debug.location.MogoLocationExit
import com.mogo.och.common.module.manager.distancemamager.TrajectoryAndDistanceManager
import com.mogo.och.common.module.utils.CoordinateCalculateRouteUtil
import com.mogo.och.common.module.view.DebugFloatWindow
import com.zhjt.mogo.adas.data.bean.AutopilotStatistics
import mogo.telematics.pad.MessagePad
import mogo_msg.MogoReportMsg
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStreamReader
object DebugDataDispatch {
const val TAG = "DebugDataDispatch"
const val globalPathMock = "globalPath"
const val locationMock = "location"
const val carDoorMock = "carDoor"
const val carNeedTurnAround = "trunAroud"
const val carLightSwitch = "lightSwitch"
const val trajectoryStation = "trajectoryStation"
const val showDebugView = "showDebugView"
const val stateAutopilot = "stateAutopilot"
const val stateAutopilotFail = "stateAutopilotFail"
const val stopSite = "stopSite"
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "location" --es path "1111/11111"
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "globalPath" --es path "sy73.json"
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "carDoor" --ei doorPostion 1 --ei doorStatus 1
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "trunAroud" --es code "IMAP_TRA_LOADED"
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "lightSwitch" --ei lightPostion 0
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "stateAutopilot" --ei autopilotMode 0 --ei autopilotState 0
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "stateAutopilotFail"
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "trajectoryStation" --ef startLon 116.74053643938474 --ef startLat 40.200487993233246 --ef endLon 116.73876977409685 --ef endLat 40.20179054129441 --el lineID 8
// adb shell am broadcast -a com.mogo.launcher.debug -f 0x011000000 --es type "stopSite" --ei state 6 --ei action 1
val ROOT_PATH =
Environment.getExternalStorageDirectory().absolutePath + File.separator + "MLog" + File.separator + "APP_mock" + File.separator //程序外部存储跟目录
fun disPathc(type: String?, frequency: Int, sourceFilePath: String?, intent: Intent) {
val file = File(ROOT_PATH);
if(file.exists()){
}
when (type) {
globalPathMock -> {
sourceFilePath?.let {
loadRawPoints(ROOT_PATH+it)
}
}
locationMock -> {
sourceFilePath?.let {
getLocaitonByLog(ROOT_PATH+it)
}
}
carDoorMock -> {
// 1--5
val intArrayExtra = intent.getIntExtra("doorPostion",0)
// 0 -1
val booleanArrayExtra = intent.getIntExtra("doorStatus",0)
val newBuilder = VehicleStateOuterClass.DoorStateV2.newBuilder()
newBuilder.number = DoorNumber.forNumber(intArrayExtra)
newBuilder.status = booleanArrayExtra
val multiList = mutableListOf<VehicleStateOuterClass.DoorStateV2>()
multiList.add(newBuilder.build())
CallerChassisDoorStateListenerManager.invokeAutopilotDoorState(multiList)
}
carNeedTurnAround -> {
// 1--5
val intArrayExtra = intent.getStringExtra("code")
val newBuilder = MogoReportMsg.MogoReportMessage.newBuilder()
newBuilder.code = intArrayExtra
newBuilder.timestampBuilder.sec = 0
newBuilder.timestampBuilder.nsec = 0
newBuilder.src = "2"
newBuilder.level = ""
ThreadUtils.getSinglePool().execute {
CallerAutoPilotStatusListenerManager.invokeAutopilotGuardian(newBuilder.build())
}
}
carLightSwitch -> {
//0-5
// LIGHT_NONE = 0;
// LIGHT_LEFT = 1;
// LIGHT_RIGHT = 2;
// LIGHT_FLASH = 3;
// LIGHT_MANUAL = 4;
// LIGHT_AUTO = 5;
val lightPostion = intent.getIntExtra("lightPostion",0)
CallerChassisLamplightListenerManager.invokeAutopilotLightSwitchData(Chassis.LightSwitch.forNumber(lightPostion))
}
trajectoryStation -> {
val startLon = intent.getFloatExtra("startLon", -1.0f)
val startLat = intent.getFloatExtra("startLat", -1.0f)
val endLon = intent.getFloatExtra("endLon", -1.0f)
val endLat = intent.getFloatExtra("endLat", -1.0f)
val lineID = intent.getLongExtra("lineID", -1)
setStation(startLon.toDouble(),startLat.toDouble(),endLon.toDouble(),endLat.toDouble(),lineID)
}
showDebugView -> {
val debugFloatWindow = DebugFloatWindow(ActivityUtils.getTopActivity())
debugFloatWindow.showFloatWindow()
}
stateAutopilot -> {
val autopilotState = intent.getIntExtra("autopilotState", 0)
val autopilotMode = intent.getIntExtra("autopilotMode", 0)
CallerAutoPilotStatusListenerManager.updateAutoPilotStatus(autopilotState,autopilotMode)
}
stateAutopilotFail -> {
val newBuilder = MogoReportMsg.MogoReportMessage.newBuilder()
newBuilder.code = "100"
newBuilder.msg = "adb 模拟指令"
newBuilder.timestampBuilder.sec = 0
newBuilder.timestampBuilder.nsec = 0
newBuilder.src = "2"
newBuilder.level = ""
var autopilotStatistics =
AutopilotStatistics(1, SystemClock.elapsedRealtime(), null, newBuilder.build())
CallerAutopilotStatisticsListenerManager.invokeAutopilotStatistics(autopilotStatistics)
}
stopSite -> {
val state = intent.getIntExtra("state", 0)
val action = intent.getIntExtra("action", 0)
val pncAction = MessagePad.PlanningActionMsg.newBuilder()
val planningaction = MessagePad.ParkScenarioPlanningAction.newBuilder()
val drivingAction = MessagePad.ParkScenarioDrivingAction.newBuilder()
drivingAction.drivingState = MessagePad.ParkScenarioDrivingState.forNumber(state)
drivingAction.drivingAction = MessagePad.DrivingAction.forNumber(action)
planningaction.actionMsg = drivingAction.build()
pncAction.parkScenarioAction = planningaction.build()
CallerPlanningActionsListenerManager.invokePNCActions(pncAction.build())
}
else -> {}
}
}
fun setStation(startLon:Double,startLat:Double,endLon:Double,endLat:Double,lineID:Long){
if(startLon<0||startLat<0||endLon<0||endLat<0||lineID<0){
TrajectoryAndDistanceManager.setStationPoint(null,null,-1)
}
val startLocation = MogoLocation()
startLocation.longitude = startLon
startLocation.latitude = startLat
val endLocation = MogoLocation()
endLocation.longitude = endLon
endLocation.latitude = endLat
TrajectoryAndDistanceManager.setStationPoint(startLocation,endLocation,lineID)
}
fun getLocaitonByLog(path:String) {
ThreadUtils.getIoPool().execute {
try {
val inputStream = FileInputStream(path)
val reader = BufferedReader(InputStreamReader(inputStream))
var line: String? = ""
while (reader.readLine().also { line = it } != null) {
val list = GsonUtils.fromJson<MogoLocationExit>(
line.toString(),
object : TypeToken<MogoLocationExit>() {}.type
)
val newBuilder = MessagePad.GnssInfo.newBuilder()
newBuilder.latitude = list.msg.GnssInfo.latitude
newBuilder.longitude = list.msg.GnssInfo.longitude
newBuilder.heading = list.msg.GnssInfo.heading
newBuilder.gnssSpeed = list.msg.GnssInfo.gnssSpeed
CallerChassisGnssListenerManager.invokeChassisGnssListener(newBuilder.build())
Thread.sleep(100)
}
} catch (e: IOException) {
e.printStackTrace()
}
}
}
fun loadRawPoints(path:String) {
val inputStream = FileInputStream(path)
val reader = BufferedReader(InputStreamReader(inputStream))
val jsonStr = StringBuilder()
var line: String? = ""
try {
while (reader.readLine().also { line = it } != null) {
jsonStr.append(line)
}
} catch (e: IOException) {
e.printStackTrace()
}
val list = GsonUtils.fromJson<List<LatLng>>(
jsonStr.toString(),
object : TypeToken<List<LatLng?>?>() {}.type
)
val newBuilder = MessagePad.GlobalPathResp.newBuilder()
for (latLng in list) {
val locationBuilder = MessagePad.Location.newBuilder()
locationBuilder.latitude = latLng.latitude
locationBuilder.longitude = latLng.longitude
newBuilder.addWayPoints(locationBuilder)
}
try {
val file = File(path)
newBuilder.lineId = file.name.toLong()
} catch (e: Exception) {
e.printStackTrace()
}
val mogoLocation = MogoLocation()
mogoLocation.latitude = list[0].latitude
mogoLocation.longitude = list[0].longitude
val mogoSecondLocation = MogoLocation()
mogoSecondLocation.latitude = list[1].latitude
mogoSecondLocation.longitude = list[1].longitude
val angle = CoordinateCalculateRouteUtil.getHeadingAngle(
mogoLocation,
mogoSecondLocation
)
mogoLocation.heading = angle
CallerChassisLocationGCJ02ListenerManager.invokeChassisLocationGCJ02(
mogoLocation,
DataSourceType.OBU
)
CallerLogger.d(SceneConstant.M_OCHCOMMON + TAG,"轨迹点个数:${list.size}")
// 发送轨迹
CallerPlanningRottingListenerManager.invokeAutopilotRotting(newBuilder.build())
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.och.common.module.debug.location
data class MogoLocationExit(
val localTime: Long,
val msg: Msg
)
data class Msg(
val GnssInfo: GnssInfo,
val Header: Header,
val stringDate0: String
)
data class Header(
val msgID: Int,
val msgType: String,
val sourceTimestamp: Double,
val timestamp: Double
)
data class GnssInfo(
val acceleration: Double,
val altitude: Double,
val gnssSpeed: Double,
val heading: Double,
val latitude: Double,
val longitude: Double,
val satelliteTime: Double,
val systemTime: Double,
val yawRate: Double
)

View File

@@ -0,0 +1,55 @@
package com.mogo.och.common.module.utils;
import com.mogo.cloud.network.OkHttpFactory;
import java.lang.reflect.Field;
import java.util.List;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
public class CollectionUtils {
public static void setInterceptor(){
OkHttpClient okHttpClient = OkHttpFactory.Companion.getOkHttpClient();
List<Interceptor> interceptors = okHttpClient.interceptors();
Field pro = getDeclaredField(interceptors, "list");
if(pro != null){
pro.setAccessible(true);
List<Interceptor> modifierList;
try {
modifierList = (List<Interceptor>) pro.get(interceptors);
if(modifierList != null){
modifierList.add(new SimpleInterceptor());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
/**
* 获得名为field的字段
* @Description:如果本身找不到去父类寻找
* @param object
* @param fieldName
* @return Field
* @author luhao
* @since2019年2月26日 下午4:06:16
*/
public static Field getDeclaredField(Object object, String fieldName){
if(fieldName == null){
return null;
}
Field field;
Class<?> clazz = object.getClass() ;
for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {
try {
field = clazz.getDeclaredField(fieldName) ;
return field ;
} catch (Exception e) {
//这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。
//如果这里的异常打印或者往外抛则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
}
}
return null;
}
}

View File

@@ -0,0 +1,26 @@
package com.mogo.och.common.module.utils
import android.util.Log
import okhttp3.*
class SimpleInterceptor: Interceptor {
var first = true;
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
val encodedPath = original.url().encodedPath()
Log.e("SimpleInterceptor",original.method()+ encodedPath+original.url().encodedQuery())
when (encodedPath) {
"/autopilot-car-hailing/cab/flow/v1/bus/driver/bus/startOperation" -> {
val builder = Response.Builder()
val create = ResponseBody.create(MediaType.parse("application/json"), "{\"code\":100006,\"msg\":\"\",\"data\":null}")
builder.code(200)
builder.request(original)
builder.protocol(Protocol.HTTP_1_1)
builder.message("")
return builder.body(create).build()
}
else -> {}
}
return chain.proceed(original)
}
}

View File

@@ -0,0 +1,107 @@
package com.mogo.och.common.module.view
import android.app.Activity
import android.graphics.PixelFormat
import android.util.DisplayMetrics
import android.view.*
import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.AppCompatImageView
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.util.BarUtils
import com.mogo.och.common.module.R
import mogo_msg.MogoReportMsg
/**
* @author XuXinChao
* @description 工控机上报列表面板
* @since: 2022/4/13
*/
class DebugFloatWindow constructor(activity: Activity) : View.OnTouchListener{
private var mActivity: Activity = activity
private var mWindowParams: WindowManager.LayoutParams? = null
private var mWindowManager: WindowManager? = null
private lateinit var mFloatLayout: View
private var mInViewX = 0f
private var mInViewY = 0f
private var mDownInScreenX = 0f
private var mDownInScreenY = 0f
private var mInScreenX = 0f
private var mInScreenY = 0f
init {
initFloatWindow();
}
private fun initFloatWindow() {
mFloatLayout = LayoutInflater.from(mActivity).inflate(R.layout.debug_view, null) as View
mFloatLayout.setOnTouchListener(this)
mWindowParams = WindowManager.LayoutParams()
mWindowManager = mActivity.windowManager
mWindowParams?.let {
it.format = PixelFormat.RGBA_8888
it.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
it.gravity = Gravity.START or Gravity.TOP
it.width = 800
it.height = 1000
it.alpha = 0.9f
}
mFloatLayout.findViewById<AppCompatImageView>(R.id.close_window).onClick {
hideFloatWindow()
}
mFloatLayout.findViewById<AppCompatButton>(R.id.acbtn_send_15_dir).onClick {
val newBuilder = MogoReportMsg.MogoReportMessage.newBuilder()
newBuilder.code = "EMAP_ATTITUDE_INIT_FAILED"
newBuilder.timestampBuilder.sec = 0
newBuilder.timestampBuilder.nsec = 0
newBuilder.src = "2"
newBuilder.level = ""
CallerAutoPilotStatusListenerManager.invokeAutopilotGuardian(newBuilder.build())
}
}
override fun onTouch(v: View?, motionEvent: MotionEvent?): Boolean {
when (motionEvent?.action) {
MotionEvent.ACTION_DOWN -> {
// 获取相对View的坐标即以此View左上角为原点
mInViewX = motionEvent.x
mInViewY = motionEvent.y
// 获取相对屏幕的坐标,即以屏幕左上角为原点
mDownInScreenX = motionEvent.rawX
mDownInScreenY = motionEvent.rawY
mInScreenX = motionEvent.rawX
mInScreenY = motionEvent.rawY
}
MotionEvent.ACTION_MOVE -> {
// 更新浮动窗口位置参数
mInScreenX = motionEvent.rawX
mInScreenY = motionEvent.rawY
mWindowParams!!.x = (mInScreenX - mInViewX).toInt()
mWindowParams!!.y = (mInScreenY - mInViewY).toInt()
// 手指移动的时候更新小悬浮窗的位置
mWindowManager!!.updateViewLayout(mFloatLayout, mWindowParams)
}
}
return true
}
fun showFloatWindow() {
if (mFloatLayout.parent == null) {
val metrics = DisplayMetrics()
// 默认固定位置,靠屏幕右边缘的中间
mWindowManager!!.defaultDisplay.getMetrics(metrics)
mWindowParams!!.x = metrics.widthPixels
mWindowParams!!.y = metrics.heightPixels / 2 - BarUtils.getStatusBarHeight()
mWindowManager!!.addView(mFloatLayout, mWindowParams)
}
}
fun hideFloatWindow() {
if (mFloatLayout.parent != null) mWindowManager!!.removeView(mFloatLayout)
}
}

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#717B98"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/blue"/>
</shape>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/green"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/close_window"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/baseline_close_24"
android:background="@drawable/common_close"
android:layout_marginTop="@dimen/dp_40"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="@dimen/dp_40"
android:layout_width="@dimen/dp_50"
android:layout_height="@dimen/dp_50"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/acbtn_send_15_dir"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="@dimen/dp_40"
android:layout_marginStart="@dimen/dp_40"
android:text="15m/方向相反"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,18 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.och.common.module">
<application>
<activity
android:name=".wigets.video.VideoPlayerActivity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize"
android:enabled="true"
android:exported="true"
android:process=":video_ad"
android:resizeableActivity="false"
android:resumeWhilePausing="true"
android:screenOrientation="landscape"
android:stateNotNeeded="true"
android:theme="@style/Main"
android:windowSoftInputMode="adjustPan|stateHidden" />
</application>
</manifest>

View File

@@ -0,0 +1,24 @@
package com.mogo.och.common.module
/**
* @author: wangmingjun
* @date: 2022/4/26
*/
class OchCommonApi private constructor(){
companion object{
private var instance: OchCommonApi? = null
get() {
if (field == null){
field = OchCommonApi();
}
return field
}
@Synchronized
fun get():OchCommonApi{
return instance!!
}
}
}

View File

@@ -0,0 +1,16 @@
package com.mogo.och.common.module.bean.dpmsg
/**
* @author: wangmingjun
* @date: 2023/2/24
*/
enum class DPMsgType(val type: Int) {
TYPE_COMMON(0), //常规
TYPE_CHANGE_DEST(1),// 变更目的地确认/到站
TYPE_OPEN_CLOSE_DOOR(2),//开关门
TYPE_ORDER_CLOSED(3), // 订单结束
TYPE_TASK_DETAILS(4), //路线任务详情
TYPE_LOGIN_STATUS(5), //login status
TYPE_ARRIVEDEST_STATUS(6), //到站通知 status
TYPE_ORDER_CLOSED_BY_M1_STATUS(7) //到站通知 status
}

View File

@@ -0,0 +1,85 @@
package com.mogo.och.common.module.bean.dpmsg
/**
* @author: wangmingjun
* @date: 2023/3/27
*/
object BusCacheKey{
const val BUS_LINE_CACHE = "bus_line_cache"
const val BUS_LOGIN_STATUS_CACHE = "bus_login_status_cache"
}
open class BaseDPMsg(open var type: Int){// 0: 常规 1确认路线/站点 2开/关车门 3结束订单
companion object{
const val TAG = "BaseDPMsg"
}
}
data class LoginCacheStatus(
var loginStatus: Int,
var updateTime: Long
): BaseDPMsg(DPMsgType.TYPE_LOGIN_STATUS.type)
data class ChangeDestMsg(
var orderNo: String,// 订单号
var lineId: Int, //线路id
var lineName: String = "", //线路名称
var startSiteId: Int= 0, //当前站点
var startSiteName: String = "",
var destSiteId: Int= 0, //目的地
var destSiteName: String = "",
var isConfirmed: Boolean = false, //司机端是否同意
var arriveStatus:Int?, //1:未到达 2:到达
var writeVersion:Long?, //版本标记
var lineSiteList: MutableList<LineSite>? = null
): BaseDPMsg(DPMsgType.TYPE_CHANGE_DEST.type)
data class LineSite(
val lineId: Long?,//线路Id
val lineName: String?,//线路名称
val siteId: Long?,//线路Id
val siteName: String?,//站点名称
val Wgs84Lon: Double?,//高精坐标
val Wgs84Lat: Double?,//高精坐标
val GcjLon: Double?,//高德坐标
val GcjLat: Double?,//高德坐标
val seq: Double?,
val type: Int?,
)
data class ArriveDestMsg(
var orderNo: String, //订单id
var lineId: Int, //线路id
var lineName: String = "", //线路名称
var startSiteId: Int= 0, //当前站点
var startSiteName: String = "",
var destSiteId: Int= 0, //目的地
var destSiteName: String = "",
var arriveStatus:Int?, //1:未到达 2:到达
var writtenVersion:Long?, //版本标记
): BaseDPMsg(DPMsgType.TYPE_ARRIVEDEST_STATUS.type)
data class EndOrderMsg(
var orderNo: String, //订单id
): BaseDPMsg(DPMsgType.TYPE_ORDER_CLOSED_BY_M1_STATUS.type)
data class DPCommonOperationMsg(
var msg: String
): BaseDPMsg(DPMsgType.TYPE_COMMON.type)
data class DPOperateDoorMsg(
var open: Boolean = false // true: 开门, false: 关门
): BaseDPMsg(DPMsgType.TYPE_OPEN_CLOSE_DOOR.type)
data class DPOrderClosedMsg(
var closed: Boolean = true // true: 结束
): BaseDPMsg(DPMsgType.TYPE_ORDER_CLOSED.type)
data class AppConnectMsg(
var isViewShow: Boolean = true, var isPlay: Boolean = false, var msg: String,var boxType:Int=-1
) : BaseDPMsg(DPMsgType.TYPE_COMMON.type)
data class TaskDetailsMsg(
var msg: String?,
): BaseDPMsg(DPMsgType.TYPE_TASK_DETAILS.type)

View File

@@ -0,0 +1,25 @@
package com.mogo.och.common.module.biz.bean;
import com.mogo.eagle.core.data.BaseData;
/**
* Created by pangfan on 2021/8/19
*
* 状态查询返回数据结构
*/
public class DriverStatusQueryRespBean extends BaseData {
public Result data;
public static class Result {
public int servingStatus; //1接单1暂停接单
public int driverStatus; //1登录0登出
public String orderNo;
public int purpose; // 1 运营, 2 测试, 3演示
public String sn;
public String plateNumber;//车牌号
public String phone;//手机号
public Integer lineId;//线路id
public Integer taskId;//任务id
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.och.common.module.biz.bean;
/**
* Created by yangyakun on 2021/8/19
* 通过手机号验证码登录
*/
public class TaxiLoginReqBean {
public String phone;
public String captcha;
public String sn;
public Location4Login loc;
public TaxiLoginReqBean(String phone, String code, String sn,Location4Login location4Login) {
this.phone = phone;
this.captcha = code;
this.sn = sn;
this.loc = location4Login;
}
public static class Location4Login{
public double lat;
public double lon;
public Location4Login(double lat, double lon) {
this.lat = lat;
this.lon = lon;
}
}
}

View File

@@ -0,0 +1,18 @@
package com.mogo.och.common.module.biz.bean;
import com.mogo.eagle.core.data.BaseData;
/**
* Created by yangyakun on 2021/8/19
* 通过验证码登录
*/
public class TaxiLoginRespBean extends BaseData {
public Result data;
public static class Result {
public Double lat;
public Double lon;
}
}

View File

@@ -0,0 +1,14 @@
package com.mogo.och.common.module.biz.bean;
/**
* Created by yyk on 2021/8/19
* 获取验证码
*/
public class TaxiLoginSmsReqBean {
public String phone;
public TaxiLoginSmsReqBean(String phone) {
this.phone = phone;
}
}

View File

@@ -0,0 +1,25 @@
package com.mogo.och.common.module.biz.bean;
/**
* Created by yyk on 2021/8/19
* 登出请求参数
*/
public class TaxiLogoutReqBean {
public String sn;
public Location4Login loc;
public TaxiLogoutReqBean(String sn, Location4Login location4Login) {
this.sn = sn;
this.loc = location4Login;
}
public static class Location4Login{
public double lat;
public double lon;
public Location4Login(double lat, double lon) {
this.lat = lat;
this.lon = lon;
}
}
}

View File

@@ -0,0 +1,9 @@
package com.mogo.och.common.module.biz.callback;
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean;
public interface ILoginCallback {
void loginSuccess(DriverStatusQueryRespBean data);
void loginFail(boolean isLogin);
}

View File

@@ -0,0 +1,13 @@
package com.mogo.och.common.module.biz.callback
interface ILoginViewCallback {
/**
* 展示登录页面
*/
fun showLoginDialogFragment()
/**
* 隐藏登录页面
*/
fun hideLoginDialogFragment()
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.common.module.biz.callback;
/**
* Created on 2021/9/8
*
* Model->Presenter回调订单相关进行中/待服务单变更,当前进行单状态变更,新到预约单,抢单,抢单结果状态等等)
*/
public interface ITaxiLoginCallback {
void getPhoneCodeSuccess();
void loginSuccess();
}

View File

@@ -0,0 +1,40 @@
package com.mogo.och.common.module.biz.common.socketmessage
import com.mogo.aicloud.services.socket.IMogoOnMessageListener
import com.mogo.aicloud.services.socket.MogoAiCloudSocketManager
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.msgbox.MsgBoxBean
import com.mogo.eagle.core.data.msgbox.MsgBoxType
import com.mogo.eagle.core.data.msgbox.OperationMsg
import com.mogo.eagle.core.function.call.msgbox.CallerMsgBoxManager
/**
* 统一管理业务长链消息推送
*/
object OCHSocketMessageManager {
const val msgMonitorType:Int = 6295553 //后台运营消息
const val msgWriteOffPassengerType:Int = 6295554 //核销消息
const val msgOperateDoorType = 6295556 // 开/关门消息
const val msgOrderClosedType = 6295555 //订单结束消息
const val OPERATION_SYSTEM: Int = -1 // 运营消息 默认是次消息类型
const val OPERATION_ORDER_TYPE: Int = 0 // 还车通知
const val OPERATION_ROAD_SIDE_TYPE: Int = 1 //靠边停车通知
fun <T> registerSocketMessageListener(msgType:Int,
mogoOnMessageListener :IMogoOnMessageListener<T>){
MogoAiCloudSocketManager.getInstance(AbsMogoApplication.getApp().applicationContext)
.registerOnMessageListener(msgType,mogoOnMessageListener)
}
fun releaseSocketMessageListener(msgType:Int){
MogoAiCloudSocketManager.getInstance(AbsMogoApplication.getApp().applicationContext)
.unregisterLifecycleListener(msgType)
}
fun pushAppOperationalMsgBox(time : Long,content : String,type: Int = -1){
CallerMsgBoxManager.saveMsgBox(MsgBoxBean(MsgBoxType.OPERATION,
OperationMsg(time,content,type)))
}
}

View File

@@ -0,0 +1,7 @@
package com.mogo.och.common.module.biz.common.socketmessage.data
/**
* @author: wangmingjun
* @date: 2023/2/25
*/
open class DataBaseMsg (var msgType: Int,var pushTimeStamp: Long = System.currentTimeMillis())

View File

@@ -0,0 +1,10 @@
package com.mogo.och.common.module.biz.common.socketmessage.data
import java.io.Serializable
data class OCHOperationalMessage(
var message: String = "",//运营消息
var messageType: Int = 0, // 运营消息类型,目前没有用处,后台也没返回
// 运营消息类型,目前没有用处,后台也没返回
var pushTimeStamp: Long = System.currentTimeMillis() // 消息下发时间戳
): Serializable

View File

@@ -0,0 +1,10 @@
package com.mogo.och.common.module.biz.common.socketmessage.data
/**
* @author: wangmingjun
* @date: 2023/2/25
*/
data class OperateDoorMsg(
var orderNo: String,
var message: String,
var pushTimeStamp: Long = System.currentTimeMillis() // 消息下发时间戳
) //{"orderNo":"","message":""}

View File

@@ -0,0 +1,10 @@
package com.mogo.och.common.module.biz.common.socketmessage.data
/**
* @author: wangmingjun
* @date: 2023/2/25
*/
data class OrderCloseMsg(
var orderNo: String,
var message: String,
var pushTimeStamp: Long = System.currentTimeMillis() // 消息下发时间戳
)

View File

@@ -0,0 +1,11 @@
package com.mogo.och.common.module.biz.common.socketmessage.data
/**
* @author: wangmingjun
* @date: 2023/2/25
*/
data class SystemMsg(
var context: String,
var screenList: MutableList<Int>,//1:司机屏 2:乘客屏
var pushTimeStamp: Long = System.currentTimeMillis() // 消息下发时间戳
)

View File

@@ -0,0 +1,59 @@
package com.mogo.och.common.module.biz.constant
import java.util.concurrent.ConcurrentHashMap
object LoginStatusManager {
/**
* 登录状态
*/
private var loginStatus: TaxiLoginStatusEnum = TaxiLoginStatusEnum.None
private val mStatusChangeListener = ConcurrentHashMap<String, ILoginStatusChangeListener>()
@JvmStatic
fun setLoginStatus(status: Int) {
when (status) {
0 -> {
setLoginStatus(TaxiLoginStatusEnum.Logout)
}
1 -> {
setLoginStatus(TaxiLoginStatusEnum.Login)
}
else -> {
setLoginStatus(TaxiLoginStatusEnum.None)
}
}
}
@JvmStatic
fun setLoginStatus(loginStatus: TaxiLoginStatusEnum) {
if(loginStatus!=this.loginStatus){
this.loginStatus = loginStatus
for (callback in mStatusChangeListener.values) {
callback.onStatusChange(loginStatus)
}
}
}
@JvmStatic
fun getLoginStatus(): TaxiLoginStatusEnum {
return loginStatus
}
@JvmStatic
fun isLogin():Boolean {
if(loginStatus== TaxiLoginStatusEnum.Login){
return true
}
return false
}
fun setControllerStatusCallback(tag: String, callback: ILoginStatusChangeListener?) {
if (tag.isBlank()) return
if (callback == null) {
mStatusChangeListener.remove(tag)
return
}
mStatusChangeListener[tag] = callback
}
interface ILoginStatusChangeListener{
fun onStatusChange(currentStatus:TaxiLoginStatusEnum)
}
}

View File

@@ -0,0 +1,32 @@
package com.mogo.och.common.module.biz.constant
import com.mogo.eagle.core.data.config.FunctionBuildConfig
/**
* Created on 2021/12/6
*/
class OchCommonConst {
companion object {
@JvmStatic
fun getBaseUrl(): String {
return FunctionBuildConfig.urlJson.ochUrl
}
@JvmStatic
fun getShuttleUrl(): String {
return FunctionBuildConfig.urlJson.shuttleUrl
}
@JvmStatic
fun getSweeperUrl(): String {
return FunctionBuildConfig.urlJson.sweeperUrl
}
// token 失效 重新获取token
const val WAIT_TAKEN = 100046
const val LOGINSERVICE = "/ochbiz/common/login"
const val BUSINESS_STRING = 100
// 自动驾驶自动规划的最大距离
const val AUTOMATIC_PLANNING_MAX_DISTANCE = 15
}
}

View File

@@ -0,0 +1,27 @@
package com.mogo.och.common.module.biz.constant
/**
* Created on 2022/08/19
*
*
* 0 初始状态,
* 10 已登录,
* 20 已登出,
*/
enum class TaxiLoginStatusEnum(val code: Int) {
None( 0 ),
Login( 10),
Logout( 20),;
companion object {
@JvmStatic
fun valueOf(code: Int): TaxiLoginStatusEnum? {
for (value in values()) {
if (value.code == code) {
return value
}
}
return None
}
}
}

View File

@@ -0,0 +1,128 @@
package com.mogo.och.common.module.biz.model
import android.annotation.SuppressLint
import android.content.Context
import com.mogo.eagle.core.data.BaseData
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
import com.mogo.eagle.core.utilcode.util.NetworkUtils
import com.mogo.och.common.module.R
import com.mogo.och.common.module.biz.bean.TaxiLoginReqBean
import com.mogo.och.common.module.biz.bean.TaxiLoginRespBean
import com.mogo.och.common.module.biz.callback.ITaxiLoginCallback
import com.mogo.och.common.module.biz.constant.LoginStatusManager
import com.mogo.och.common.module.biz.constant.TaxiLoginStatusEnum
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
import com.mogo.och.common.module.biz.network.OchCommonServiceManager
import com.mogo.och.common.module.utils.ToastUtilsOch
import com.mogo.och.common.module.wigets.toast.ToastCharterUtils
/**
* Created by pangfan on 2021/8/19
*
*
* 网约车 - 出租车业务逻辑处理
*/
@SuppressLint("StaticFieldLeak")
object OchCommonLoginModel {
private val TAG = "TaxiLoginModel"
private var mContext: Context? = null
var iTaxiLoginCallback: ITaxiLoginCallback? = null
fun init(context: Context) {
mContext = context.applicationContext
}
fun hasInit(): Boolean {
if (mContext == null && iTaxiLoginCallback == null) {
return false
}
return true
}
fun getiTaxiLoginCallback(): ITaxiLoginCallback? {
return iTaxiLoginCallback
}
fun setiTaxiLoginCallback(iTaxiLoginCallback: ITaxiLoginCallback?) {
this.iTaxiLoginCallback = iTaxiLoginCallback
}
fun release() {
mContext = null
iTaxiLoginCallback = null
}
/**
* 获取手机验证码
*/
fun getPhoneCode(phone: String?) {
mContext?.let {
OchCommonServiceManager.getPhoneCode(it, phone,
object : OchCommonServiceCallback<BaseData> {
override fun onSuccess(data: BaseData?) {
if (null != data && 0 == data.code) {
// 获取验证码成功
ToastCharterUtils.showToastShort(mContext?.getString(R.string.module_och_taxi_login_get_code_success))
iTaxiLoginCallback?.getPhoneCodeSuccess()
} else {
if (data != null) {
ToastCharterUtils.showToastShort(data.code.toString())
}
}
}
override fun onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastCharterUtils.showToastShort(mContext?.getString(R.string.network_error_tip))
} else {
ToastCharterUtils.showToastShort(mContext?.getString(R.string.request_error_tip))
}
}
override fun onFail(code: Int, msg: String) {
ToastUtilsOch.showWithCodeMessage(code, msg)
}
})
}
}
fun gotoLogin(phone: String, code: String) {
mContext?.let {
val location = CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()
val location4Login = TaxiLoginReqBean.Location4Login(location.latitude, location.longitude)
OchCommonServiceManager.gotoLoginBycode(it, phone, code, location4Login,
object : OchCommonServiceCallback<TaxiLoginRespBean> {
override fun onSuccess(data: TaxiLoginRespBean?) {
if (null != data && 0 == data.code) {
// 获取验证码成功
ToastCharterUtils.showToastShort(mContext?.getString(R.string.module_och_taxi_login_login_success))
LoginStatusManager.setLoginStatus(TaxiLoginStatusEnum.Login)
mContext?.let { c ->
SharedPrefsMgr.getInstance(c).putString("och_account", phone)
}
iTaxiLoginCallback?.loginSuccess()
} else {
if (data != null) {
ToastCharterUtils.showToastShort(data.code.toString())
}
}
}
override fun onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastCharterUtils.showToastShort(mContext?.getString(R.string.network_error_tip))
} else {
ToastCharterUtils.showToastShort(mContext?.getString(R.string.request_error_tip))
}
}
override fun onFail(code: Int, msg: String) {
ToastUtilsOch.showWithCodeMessage(code, msg)
}
})
}
}
}

View File

@@ -0,0 +1,60 @@
package com.mogo.och.common.module.biz.model
import android.annotation.SuppressLint
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.och.common.module.bean.dpmsg.BusCacheKey
import com.mogo.och.common.module.bean.dpmsg.LoginCacheStatus
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean
import com.mogo.och.common.module.biz.callback.ILoginCallback
import com.mogo.och.common.module.biz.callback.ILoginViewCallback
import com.mogo.och.common.module.biz.constant.LoginStatusManager
import com.mogo.och.common.module.utils.DateTimeUtil
import com.mogo.och.data.manager.cache.CacheDataManager
@SuppressLint("StaticFieldLeak")
object OchCommonLoginStatusDefaultModel : OchCommonLoginStatusModel() {
const val TAG = "OchCommonLoginStatusDefaultModel"
var loginCallback: ILoginCallback? = null
var loginViewCallback: ILoginViewCallback? = null
override fun loginSuccess(data: DriverStatusQueryRespBean?) {
CallerLogger.d(SceneConstant.M_TAXI + TAG, "loginSuccess:${LoginStatusManager.isLogin()}")
if (LoginStatusManager.isLogin()) {
SharedPrefsMgr.getInstance(mContext).putString("och_account", data?.data?.phone)
loginViewCallback?.hideLoginDialogFragment()
} else {
SharedPrefsMgr.getInstance(mContext).putString("och_account", "")
loginViewCallback?.showLoginDialogFragment()
}
loginCallback?.loginSuccess(data)
data?.data?.driverStatus?.let { updateLoginLocalStatus(it) }
}
private fun updateLoginLocalStatus(loginStatus: Int = 0) {
val loginCacheStatus = LoginCacheStatus(loginStatus,DateTimeUtil.getCurrentTimeStamp())
CacheDataManager.instance.putCacheData(mContext, BusCacheKey.BUS_LOGIN_STATUS_CACHE,
GsonUtils.toJson(loginCacheStatus))
}
override fun loginFail(isLogin: Boolean) {
CallerLogger.d(SceneConstant.M_TAXI + TAG, "loginFail:$isLogin")
if (isLogin) {
loginViewCallback?.hideLoginDialogFragment()
} else {
loginViewCallback?.showLoginDialogFragment()
}
loginCallback?.loginFail(isLogin)
updateLoginLocalStatus(0)
}
}

View File

@@ -0,0 +1,129 @@
package com.mogo.och.common.module.biz.model;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI;
import android.content.Context;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.eagle.core.data.BaseData;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.NetworkUtils;
import com.mogo.eagle.core.utilcode.util.ToastUtils;
import com.mogo.och.common.module.R;
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean;
import com.mogo.och.common.module.biz.bean.TaxiLogoutReqBean;
import com.mogo.och.common.module.biz.constant.LoginStatusManager;
import com.mogo.och.common.module.biz.constant.OchCommonConst;
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback;
import com.mogo.och.common.module.biz.network.OchCommonServiceManager;
import com.mogo.och.common.module.utils.ToastUtilsOch;
import com.mogo.och.common.module.wigets.toast.ToastCharterUtils;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
public abstract class OchCommonLoginStatusModel {
private static final String TAG = "OchCommonLoginStatusModel";
protected Context mContext;
private Disposable subscribe;
public void init() {
mContext = AbsMogoApplication.getApp();
queryCarStatus();
}
protected abstract void loginSuccess(DriverStatusQueryRespBean data);
protected abstract void loginFail(boolean isLogin);
/**
* 接单状态和登录状态查询
* 1、初始化查询
* 2、错误重试
* 3、错误重试
* 4、登出后重试
* 5、变更出车状态查下
* 6、变更出车状态查下
* 7、网络状态变更后查询
* 8、登录页面关闭后查下状态
*/
public void queryCarStatus() {
OchCommonServiceManager.queryDriverServiceStatus(mContext,
new OchCommonServiceCallback<DriverStatusQueryRespBean>() {
@Override
public void onSuccess(DriverStatusQueryRespBean data) {
if (null != data && 0 == data.code) {
LoginStatusManager.setLoginStatus(data.data.driverStatus);
CallerLogger.d(M_TAXI + TAG, "changeCarStatus:" + LoginStatusManager.getLoginStatus());
loginSuccess(data);
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastCharterUtils.showToastShort(mContext.getString(R.string.network_error_tip));
} else {
ToastCharterUtils.showToastShort(mContext.getString(R.string.request_error_tip));
}
subscribe = Observable.timer(5, TimeUnit.SECONDS).subscribe(aLong -> {
queryCarStatus();
});
}
@Override
public void onFail(int code, String msg) {
ToastUtilsOch.showWithCodeMessage(code,msg);
if(code== OchCommonConst.WAIT_TAKEN){
subscribe = Observable.timer(3, TimeUnit.SECONDS).subscribe(aLong -> {
queryCarStatus();
});
}else {
loginFail(LoginStatusManager.isLogin());
}
}
});
}
// 登出
public void logout(double mLatitude,double mLongitude) {
TaxiLogoutReqBean.Location4Login location4Login = new TaxiLogoutReqBean.Location4Login(mLatitude, mLongitude);
OchCommonServiceManager.logout(mContext,location4Login,
new OchCommonServiceCallback<BaseData>() {
@Override
public void onSuccess(BaseData data) {
if (null != data && 0 == data.code) {
loginFail(false);
queryCarStatus();
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastCharterUtils.showToastShort(mContext.getString(R.string.network_error_tip));
} else {
ToastCharterUtils.showToastShort(mContext.getString(R.string.request_error_tip));
}
}
@Override
public void onFail(int code, String msg) {
ToastUtilsOch.showWithCodeMessage(code,msg);
}
});
}
public void release() {
if(subscribe!=null&&!subscribe.isDisposed()){
subscribe.dispose();
}
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.och.common.module.biz.network
import android.content.Context
import com.mogo.eagle.core.data.BaseData
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean
import com.mogo.och.common.module.biz.bean.TaxiLoginReqBean
import com.mogo.och.common.module.biz.bean.TaxiLoginRespBean
import com.mogo.och.common.module.biz.bean.TaxiLogoutReqBean
interface LoginDefaultManage {
fun getPhoneCode(
context: Context,
phone: String?,
callback: OchCommonServiceCallback<BaseData>?
)
fun gotoLoginBycode(
context: Context, phone: String?, code: String?,
location4Login: TaxiLoginReqBean.Location4Login?,
callback: OchCommonServiceCallback<TaxiLoginRespBean>?
)
fun logout(
context: Context,
location4Login: TaxiLogoutReqBean.Location4Login?,
callback: OchCommonServiceCallback<BaseData>?
)
fun queryDriverServiceStatus(
context: Context,
callback: OchCommonServiceCallback<DriverStatusQueryRespBean>?
)
}

View File

@@ -0,0 +1,18 @@
package com.mogo.och.common.module.biz.network;
/**
* @author congtaowang
* @since 2021/1/15
*
* 修改订单状态回调接口
*/
public interface OchCommonServiceCallback< T > {
void onSuccess(T data);
void onFail(int code, String msg);
default void onError() {
}
}

View File

@@ -0,0 +1,85 @@
package com.mogo.och.common.module.biz.network
import android.content.Context
import com.mogo.och.common.module.biz.constant.OchCommonConst.Companion.getBaseUrl
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.och.common.module.biz.bean.TaxiLoginSmsReqBean
import com.mogo.och.common.module.biz.bean.TaxiLoginReqBean
import com.mogo.och.common.module.biz.bean.TaxiLoginRespBean
import com.mogo.och.common.module.biz.bean.TaxiLogoutReqBean
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean
import com.mogo.och.common.module.biz.constant.OchCommonConst
import com.mogo.cloud.passport.MoGoAiCloudClient
import com.mogo.commons.debug.DebugConfig
import com.mogo.eagle.core.data.BaseData
import com.mogo.eagle.core.network.MoGoRetrofitFactory
import com.mogo.och.common.module.biz.network.interceptor.transformTry
import io.reactivex.Observable
/**
* Created by pangfan on 2021/8/19
*/
object OchCommonServiceManager {
private const val TAG = "OchCommonServiceManager"
private var loginDefaultManage: LoginDefaultManage?=null
fun setLoginDefaultManage(loginDefaultManage: LoginDefaultManage?){
this.loginDefaultManage = loginDefaultManage
}
/**
* 获取手机验证码
* @param context
* @param callback
*/
@JvmStatic
fun getPhoneCode(
context: Context, phone: String?,
callback: OchCommonServiceCallback<BaseData>?
) {
loginDefaultManage?.getPhoneCode(context,phone,callback)
}
/**
* 通过验证码登录
* @param context
* @param callback
*/
@JvmStatic
fun gotoLoginBycode(
context: Context, phone: String?, code: String?,
location4Login: TaxiLoginReqBean.Location4Login?,
callback: OchCommonServiceCallback<TaxiLoginRespBean>?
) {
loginDefaultManage?.gotoLoginBycode(context,phone,code,location4Login,callback)
}
/**
* 登出
*/
@JvmStatic
fun logout(
context: Context,
location4Login: TaxiLogoutReqBean.Location4Login?,
callback: OchCommonServiceCallback<BaseData>?
) {
loginDefaultManage?.logout(context,location4Login,callback)
}
/**
* 接单状态和登录状态查询
*
* @param context
* @param callback
*/
@JvmStatic
fun queryDriverServiceStatus(
context: Context,
callback: OchCommonServiceCallback<DriverStatusQueryRespBean>?
) {
loginDefaultManage?.queryDriverServiceStatus(context,callback)
}
}

View File

@@ -0,0 +1,36 @@
package com.mogo.och.common.module.biz.network
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.CallerLogger
import com.mogo.och.common.module.utils.CallerLoggerUtils.flavorTag
class OchCommonSubscribeImpl<T : BaseData>(
val context: Any,
val callback: OchCommonServiceCallback<T>?,
val apiName: String
) : SubscribeImpl<T>(RequestOptions.create(context)) {
companion object {
const val TAG = "OchCommonSubscribeImpl"
}
override fun onSuccess(o: T) {
super.onSuccess(o)
CallerLogger.d("$flavorTag$TAG", "$apiName: onSuccess() ${o.msg}")
callback?.onSuccess(o)
}
override fun onError(e: Throwable) {
super.onError(e)
CallerLogger.e("$flavorTag$TAG", "$apiName: onError() ${e.message}")
callback?.onError()
}
override fun onError(message: String, code: Int) {
super.onError(message, code)
CallerLogger.e("$flavorTag$TAG", "$apiName: onError() code = $code; message = $message")
callback?.onFail(code, message)
}
}

View File

@@ -0,0 +1,24 @@
package com.mogo.och.common.module.biz.network.interceptor
import com.mogo.cloud.passport.MoGoAiCloudClient
import com.mogo.eagle.core.data.BaseData
import com.mogo.och.common.module.biz.constant.LoginStatusManager
import com.mogo.och.common.module.biz.constant.TaxiLoginStatusEnum
import io.reactivex.Observable
import io.reactivex.ObservableSource
import io.reactivex.functions.Function
class FRetryWithTime<T : BaseData> : Function<T, ObservableSource<T>> {
override fun apply(baseData: T): ObservableSource<T> {
baseData.let {
if (it.code == 100046 || it.code == 100045 || it.code == 100005 || it.code == 100006 || it.code == 520003) {
MoGoAiCloudClient.getInstance().refreshToken()
return Observable.error(OchCommonRetryException())
}else if(it.code == 1003){
LoginStatusManager.setLoginStatus(TaxiLoginStatusEnum.Logout)
}
}
return Observable.just(baseData)
}
}

View File

@@ -0,0 +1,19 @@
package com.mogo.och.common.module.biz.network.interceptor
import io.reactivex.Observable
import com.mogo.eagle.core.data.BaseData
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
fun <T : BaseData> Observable<T>.transformTry():Observable<T> {
return flatMap(FRetryWithTime<T>())
.retryWhen(RetryWithTime())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
fun <T : BaseData> Observable<T>.transformIoTry():Observable<T> {
return flatMap(FRetryWithTime<T>())
.retryWhen(RetryWithTime())
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
}

View File

@@ -0,0 +1,5 @@
package com.mogo.och.common.module.biz.network.interceptor;
public class OchCommonRetryException extends RuntimeException{
}

View File

@@ -0,0 +1,30 @@
package com.mogo.och.common.module.biz.network.interceptor
import com.mogo.eagle.core.utilcode.constant.TimeConstants
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.TimeUtils
import com.mogo.och.common.module.utils.CallerLoggerUtils.flavorTag
import io.reactivex.Observable
import io.reactivex.ObservableSource
import io.reactivex.functions.Function
import java.util.concurrent.TimeUnit
class RetryWithTime : Function<Observable<Throwable?>, ObservableSource<Any?>> {
companion object {
private const val TAG = "RetryWithTime"
}
var current = -1
private var timeDelys = intArrayOf(3, 1, 2)
override fun apply(throwableObservable: Observable<Throwable?>): ObservableSource<Any?>? {
return throwableObservable.flatMap {
++current
CallerLogger.e("${flavorTag}${TAG}", " 时间:${TimeUtils.getStringByNow(0, TimeConstants.SEC)}")
if (it is OchCommonRetryException && current < timeDelys.size) {
Observable.timer(timeDelys[current].toLong(), TimeUnit.SECONDS)
} else {
Observable.error(it)
}
}
}
}

View File

@@ -0,0 +1,112 @@
package com.mogo.och.common.module.biz.presenter
import androidx.lifecycle.LifecycleOwner
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.mvp.Presenter
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.RegexUtils
import com.mogo.och.common.module.R
import com.mogo.och.common.module.biz.callback.ITaxiLoginCallback
import com.mogo.och.common.module.biz.model.OchCommonLoginModel
import com.mogo.och.common.module.biz.ui.TaxiLoginDialogFragment
import com.mogo.och.common.module.wigets.toast.ToastCharterUtils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit
/**
* @author congtaowang
* @since 2021/1/18
*
* 描述
*/
class OchCommonLoginPresenter(view: TaxiLoginDialogFragment?) :
Presenter<TaxiLoginDialogFragment?>(view), ITaxiLoginCallback {
private var countDownDisposable: Disposable? = null
init {
initListeners()
}
private fun initListeners() {
OchCommonLoginModel.init(AbsMogoApplication.getApp())
OchCommonLoginModel.setiTaxiLoginCallback(this)
}
fun getPhoneCode(phone:String){
if(!OchCommonLoginModel.hasInit()){
initListeners()
}
if (!RegexUtils.isMobileExact(phone)) {
ToastCharterUtils.showToastShort(R.string.module_och_taxi_login_phone_error)
mView?.inputPhoneError()
return
}
OchCommonLoginModel.getPhoneCode(phone)
}
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
CallerLogger.d(SceneConstant.M_TAXI + TAG, "网约车-出租车登陆")
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
OchCommonLoginModel.release()
countDownDisposable?.let {
if (!it.isDisposed) {
it.dispose()
}
}
}
companion object {
private val TAG = OchCommonLoginPresenter::class.java.simpleName
}
override fun getPhoneCodeSuccess() {
val countDownSeconds = 60L;
countDownDisposable = Observable.intervalRange(0, countDownSeconds, 0, 1, TimeUnit.SECONDS)
.map { aLong -> countDownSeconds - aLong }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
mView?.setCountDownText("${it}s",false)
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "倒计时:$it")
}, {
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "倒计时onError:${it}")
it.printStackTrace()
mView?.setCountDownText(AbsMogoApplication.getApp().getString(R.string.module_och_taxi_login_get_code),true)
}, {
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "倒计时onComplete")
mView?.setCountDownText(AbsMogoApplication.getApp().getString(R.string.module_och_taxi_login_get_code),true)
})
}
override fun loginSuccess() {
mView?.loginSuccess()
}
fun gotoLogin(phone: String, code: String) {
if(!OchCommonLoginModel.hasInit()){
initListeners()
}
if (!RegexUtils.isMobileExact(phone)) {
ToastCharterUtils.showToastShort(R.string.module_och_taxi_login_phone_error)
mView?.inputPhoneError()
return
}
if(code.isBlank()||code.length<4){
ToastCharterUtils.showToastShort(R.string.module_och_taxi_login_code_error)
return
}
mView?.closeSoftInput()
OchCommonLoginModel.gotoLogin(phone,code)
}
}

View File

@@ -0,0 +1,48 @@
package com.mogo.och.common.module.biz.presenter
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.mogo.och.common.module.biz.callback.ILoginCallback
import com.mogo.och.common.module.biz.callback.ILoginViewCallback
import com.mogo.och.common.module.biz.model.OchCommonLoginStatusDefaultModel
/**
* @author yangyakun
* @since 2020-09-19
*/
class OchCommonLoginStatusDefaultPresenter : LifecycleObserver {
private var isFirstShow = true
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate(owner: LifecycleOwner) {
OchCommonLoginStatusDefaultModel.init()
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume(owner: LifecycleOwner) {
if (isFirstShow) {
isFirstShow = false
} else {
queryLoginStatus()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestory(owner: LifecycleOwner) {
OchCommonLoginStatusDefaultModel.release()
}
fun queryLoginStatus() {
OchCommonLoginStatusDefaultModel.queryCarStatus()
}
fun logOut(mLatitude:Double,mLongitude:Double){
OchCommonLoginStatusDefaultModel.logout(mLatitude, mLongitude)
}
fun setLoginCallback(loginCallback: ILoginCallback?,loginViewCallback: ILoginViewCallback?){
OchCommonLoginStatusDefaultModel.loginCallback = loginCallback
OchCommonLoginStatusDefaultModel.loginViewCallback = loginViewCallback
}
}

View File

@@ -0,0 +1,32 @@
package com.mogo.och.common.module.biz.provider
import androidx.fragment.app.Fragment
import com.alibaba.android.arouter.facade.template.IProvider
import com.mogo.och.common.module.biz.callback.ILoginCallback
import com.mogo.och.common.module.biz.network.LoginDefaultManage
interface LoginService : IProvider {
/**
* 注册页面
* @param fragment 主页面
* @param callback 回调
*/
fun registerFragment(fragment: Fragment?,loginCallback: ILoginCallback?,logindefaultmanage: LoginDefaultManage?)
fun unRegisterFragment()
/**
* 查询登录状态
*/
fun queryLoginStatus():Boolean
fun queryLoginStatusByNet()
fun showUiModel(show:Boolean)
/**
* 登出
*/
fun loginOut(mLatitude:Double,mLongitude:Double)
}

View File

@@ -0,0 +1,125 @@
package com.mogo.och.common.module.biz.provider
import android.content.Context
import android.content.DialogInterface
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import com.alibaba.android.arouter.facade.annotation.Route
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.AppStateManager.currentActivity
import com.mogo.eagle.core.utilcode.util.ClickUtils
import com.mogo.och.common.module.biz.callback.ILoginCallback
import com.mogo.och.common.module.biz.callback.ILoginViewCallback
import com.mogo.och.common.module.biz.constant.LoginStatusManager
import com.mogo.och.common.module.biz.constant.OchCommonConst
import com.mogo.och.common.module.biz.network.LoginDefaultManage
import com.mogo.och.common.module.biz.network.OchCommonServiceManager
import com.mogo.och.common.module.biz.presenter.OchCommonLoginStatusDefaultPresenter
import com.mogo.och.common.module.biz.ui.TaxiLoginDialogFragment
import com.mogo.och.common.module.biz.ui.TaxiLoginDialogFragment.Companion.newInstance
import java.lang.ref.WeakReference
@Route(path = OchCommonConst.LOGINSERVICE)
class LoginServiceImpl : LoginService,ILoginViewCallback {
companion object{
const val TAG = "LoginServiceImpl"
}
private var fragment: Fragment?=null
private var taxiLoginDialogFragment: WeakReference<TaxiLoginDialogFragment>? = null
private var presenter: OchCommonLoginStatusDefaultPresenter?=null
private var uiModel = true
override fun init(context: Context) {
}
override fun registerFragment(fragment: Fragment?,loginCallback: ILoginCallback?,logindefaultmanage: LoginDefaultManage?) {
OchCommonServiceManager.setLoginDefaultManage(logindefaultmanage)
presenter = OchCommonLoginStatusDefaultPresenter()
this.fragment = fragment
presenter?.let {
it.setLoginCallback(loginCallback, this)
this.fragment?.lifecycle?.addObserver(it)
}
}
override fun unRegisterFragment() {
this.presenter?.let {
it.setLoginCallback(null,null)
this.fragment?.lifecycle?.removeObserver(it)
}
this.fragment = null
}
override fun showLoginDialogFragment() {
if(uiModel) {
fragment?.let {
CallerHmiManager.hideToolsView()
CallerHmiManager.hideSOPSettingView()
val parentFragmentManager = it.childFragmentManager
val fragmentByTag: Fragment? = parentFragmentManager.findFragmentByTag(TAG)
if (fragmentByTag is DialogFragment) {
if (fragmentByTag.dialog != null && fragmentByTag.dialog!!.isShowing) {
return
}
if (fragmentByTag.dialog != null && fragmentByTag.isAdded) {
if (currentActivity() == null) { // 没有在当前应用内 在启动页面关闭应用
CallerLogger.d(SceneConstant.M_TAXI + TAG, "showLoginDialogFragment 权限验证")
return
}
}
}
if (taxiLoginDialogFragment?.get() == null) {
taxiLoginDialogFragment = WeakReference(newInstance())
}
val taxiLoginDialog = taxiLoginDialogFragment?.get()
if (taxiLoginDialog != null) {
if (taxiLoginDialog.dialog != null && taxiLoginDialog.dialog!!.isShowing) {
return
}
if (taxiLoginDialog.isAdded) { //解决方法就是添加这行代码如果已经添加了就移除掉然后再show就不会出现Fragment already added的错误了。
parentFragmentManager.beginTransaction().remove(taxiLoginDialog)
.commitAllowingStateLoss()
}
if (ClickUtils.isFastClick()) {
taxiLoginDialog.show(parentFragmentManager, TAG)
taxiLoginDialog.setOnDismissListener(DialogInterface.OnDismissListener { dialog: DialogInterface? ->
taxiLoginDialogFragment?.clear()
presenter?.queryLoginStatus()
})
CallerLogger.d(SceneConstant.M_TAXI + TAG, "showLoginDialogFragment 展示登录界面")
}else{
CallerLogger.d(SceneConstant.M_TAXI + TAG, "showLoginDialogFragment 展示登录界面 1s内执行一次")
}
}
}
}
}
override fun hideLoginDialogFragment() {
CallerLogger.d(SceneConstant.M_TAXI + TAG, "hideLoginDialogFragment 隐藏登录界面")
if (taxiLoginDialogFragment?.get() != null) {
taxiLoginDialogFragment?.get()?.dismissAllowingStateLoss()
}
}
override fun queryLoginStatus():Boolean {
return LoginStatusManager.isLogin()
}
override fun queryLoginStatusByNet() {
presenter?.queryLoginStatus()
}
override fun showUiModel(show:Boolean) {
uiModel = show;
}
override fun loginOut(mLatitude:Double,mLongitude:Double) {
presenter?.logOut(mLatitude,mLongitude)
}
}

View File

@@ -0,0 +1,288 @@
package com.mogo.och.common.module.biz.ui
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Rect
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import android.view.*
import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.AppCompatEditText
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import com.alibaba.android.arouter.launcher.ARouter
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.debug.DebugConfig
import com.mogo.commons.mvp.MvpDialogFragment
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.KeyboardUtils
import com.mogo.och.common.module.R
import com.mogo.och.common.module.biz.bean.TaxiLoginReqBean
import com.mogo.och.common.module.biz.constant.OchCommonConst
import com.mogo.och.common.module.biz.network.OchCommonServiceManager
import com.mogo.och.common.module.biz.network.interceptor.transformTry
import com.mogo.och.common.module.biz.presenter.OchCommonLoginPresenter
import com.mogo.och.common.module.biz.provider.LoginService
/**
* @author: yangyakun
* @date: 2022/8/15
*/
class TaxiLoginDialogFragment :
MvpDialogFragment<TaxiLoginDialogFragment?, OchCommonLoginPresenter?>(),
DialogInterface.OnKeyListener {
lateinit var clMain: ConstraintLayout
lateinit var acbtnLogin: AppCompatButton
lateinit var actvLoginGetCode: AppCompatTextView
lateinit var actvLoginShowSn: AppCompatTextView
lateinit var aceLoginPhoneValue: AppCompatEditText
lateinit var acetPhoneCodeValue: AppCompatEditText
lateinit var actvWelcomeLoginTitle: AppCompatTextView
lateinit var acivLoginBg: AppCompatImageView
private var mOnClickListener: DialogInterface.OnDismissListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.DialogFullScreen) //dialog全屏
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
dialog?.setOnKeyListener(this)
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun getLayoutId(): Int {
return R.layout.taxi_login_view
}
override fun initViews() {
clMain = mRootView.findViewById(R.id.cl_main)
acbtnLogin = mRootView.findViewById(R.id.acbtn_login)
actvLoginGetCode = mRootView.findViewById(R.id.actv_login_get_code)
aceLoginPhoneValue = mRootView.findViewById(R.id.ace_login_phone_value)
acetPhoneCodeValue = mRootView.findViewById(R.id.acet_phone_code_value)
actvWelcomeLoginTitle = mRootView.findViewById(R.id.actv_welcome_login_title)
actvLoginShowSn = mRootView.findViewById(R.id.actv_login_show_sn)
acivLoginBg = mRootView.findViewById(R.id.aciv_login_bg)
inputPhoneNormal()
initBg()
initListener()
dialog?.window?.let {
context?.let { _ ->
CallerHmiManager.setStatusBarDarkOrLight(false)
}
}
}
private fun initBg() {
if (AppIdentityModeUtils.isTaxi(FunctionBuildConfig.appIdentityMode) &&
AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
//出租车司机
acivLoginBg.setImageResource(R.drawable.taxi_ic_login_bg)
} else if ((AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode)
|| AppIdentityModeUtils.isShuttle(FunctionBuildConfig.appIdentityMode)
|| AppIdentityModeUtils.isCharter(FunctionBuildConfig.appIdentityMode)) &&
AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
//小巴车司机
acivLoginBg.setImageResource(R.drawable.bus_ic_login_bg)
}
}
@SuppressLint("ClickableViewAccessibility")
private fun initListener() {
mRootView.isFocusable = true
mRootView.isFocusableInTouchMode = true
mRootView.setOnTouchListener { _, event ->
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
closeSoftInput()
}
}
false
}
acbtnLogin.onClick {
val phone = aceLoginPhoneValue.text.toString()
val code = acetPhoneCodeValue.text.toString()
mPresenter?.gotoLogin(phone, code)
}
actvWelcomeLoginTitle.setOnClickListener {
continuousClick()
}
clMain.viewTreeObserver.addOnGlobalLayoutListener {
val rect = Rect()
clMain.getWindowVisibleDisplayFrame(rect)
val mainInvisibleHeight = clMain.rootView.height - rect.bottom
if (mainInvisibleHeight > 100) {
val outLocation = IntArray(2)
acbtnLogin.getLocationInWindow(outLocation)
val srollHeight = (outLocation[1] + acbtnLogin.height) - rect.bottom
if (srollHeight > 0) {
clMain.scrollTo(0, srollHeight)
}
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "滚动的距离:$srollHeight")
} else {
clMain.scrollTo(0, 0)
}
}
actvLoginGetCode.onClick {
mPresenter?.getPhoneCode(aceLoginPhoneValue.text.toString())
}
actvLoginShowSn.setOnLongClickListener {
val loginService = ARouter.getInstance().build(OchCommonConst.LOGINSERVICE)
.navigation() as LoginService
loginService.showUiModel(false)
dismissAllowingStateLoss()
true
}
aceLoginPhoneValue.addTextChangedListener {
it?.let { itEditable ->
if (itEditable.isNotEmpty()) {
inputPhoneNormal()
}
}
}
actvLoginShowSn.text = MoGoAiCloudClientConfig.getInstance().sn
}
/**
* 关闭键盘
*/
fun closeSoftInput() {
mRootView.requestFocus()
dialog?.window?.let {
KeyboardUtils.hideSoftInput(it)
}
}
fun setCountDownText(text: String, enable: Boolean) {
if (enable) {
actvLoginGetCode.setText(R.string.module_och_taxi_login_get_code)
actvLoginGetCode.isEnabled = true
} else {
actvLoginGetCode.text = text
actvLoginGetCode.isEnabled = false
}
}
fun inputPhoneError() {
aceLoginPhoneValue.text?.clear()
aceLoginPhoneValue.setHint(R.string.module_och_taxi_login_phone_error)
context?.let {
aceLoginPhoneValue.setHintTextColor(ContextCompat.getColor(it, R.color.taxi_EF262C))
aceLoginPhoneValue.setBackgroundResource(R.drawable.taxi_login_phone_error)
}
}
private fun inputPhoneNormal() {
//aceLoginPhoneValue.setHint(R.string.module_och_taxi_login_phone_hint_text)
context?.let {
aceLoginPhoneValue.setHintTextColor(ContextCompat.getColor(it, R.color.taxi_878890))
aceLoginPhoneValue.setBackgroundResource(R.drawable.taxi_login_phone_normal)
}
}
override fun createPresenter(): OchCommonLoginPresenter {
return OchCommonLoginPresenter(this)
}
override fun getTagName(): String {
return TAG
}
companion object {
private const val COUNTS = 4 // 点击次数
private const val DURATION: Long = 1000 // 规定有效时间
val TAG = TaxiLoginDialogFragment::class.java.simpleName
@JvmStatic
fun newInstance(): TaxiLoginDialogFragment {
val args = Bundle()
val fragment = TaxiLoginDialogFragment()
fragment.arguments = args
return fragment
}
}
private var mHits = LongArray(COUNTS)
private fun continuousClick() {
//每次点击时,数组向前移动一位
System.arraycopy(mHits, 1, mHits, 0, mHits.size - 1)
//为数组最后一位赋值
mHits[mHits.size - 1] = SystemClock.uptimeMillis()
if (mHits[0] >= (SystemClock.uptimeMillis() - DURATION)) {
mHits = LongArray(COUNTS) //重新初始化数组
mPresenter?.gotoLogin("13288888888", "8888")
}
}
override fun onKey(dialog: DialogInterface, keyCode: Int, event: KeyEvent): Boolean {
return keyCode == KeyEvent.KEYCODE_BACK
}
fun loginSuccess() {
dismissAllowingStateLoss()
}
fun setOnDismissListener(listener: DialogInterface.OnDismissListener?) {
mOnClickListener = listener
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
mOnClickListener?.onDismiss(dialog)
CallerHmiManager.setStatusBarDarkOrLight(false)
}
/**
* 重写父类show()方法
* 避免出现java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
*/
override fun show(manager: FragmentManager, tag: String?) {
try {
var cls = this.javaClass.superclass ?: return
while (cls != null) {
if (cls.name == "java.lang.Object") {
break
}
cls = cls.superclass!!
if (cls == DialogFragment::class.java) {
break
}
}
val mDismissed = cls.getDeclaredField("mDismissed")
val mShownByMe = cls.getDeclaredField("mShownByMe")
mDismissed.isAccessible = true
mShownByMe.isAccessible = true
mDismissed.setBoolean(this, false)
mShownByMe.setBoolean(this, true)
if (isAdded) { //解决方法就是添加这行代码如果已经添加了就移除掉然后再show就不会出现Fragment already added的错误了。
return
}
val ft: FragmentTransaction = manager.beginTransaction()
ft.add(this, tag)
ft.commitAllowingStateLoss()
} catch (e: Exception) {
Log.e("DialogFragment", "show", e.fillInStackTrace())
}
}
}

View File

@@ -0,0 +1,65 @@
package com.mogo.och.common.module.callback
import androidx.annotation.ColorRes
/**
* @author: wangmingjun
* @date: 2022/1/21
*/
interface IShadow {
//设置阴影半径
fun setShadowRadius(radius:Float): IShadow
//添加单位设置
fun setShadowRadius(unit:Int,radius: Float): IShadow
//设置应用颜色
fun setShadowColor(color:Int): IShadow
//设置阴影颜色资源文件id
fun setShadowColorRes(@ColorRes color: Int): IShadow
/**
* 设置模糊半径
* @param radius
*/
fun setBlurRadius(radius:Float): IShadow
/**
*
* @param unit @{@link android.util.TypedValue#TYPE_DIMENSION}
* @param radius 模糊半径
*/
fun setBlurRadius(unit:Int,radius:Float): IShadow
/**
* 设置水平方向的偏移量
* @param offset x轴偏移
*/
fun setXOffset(offset:Float): IShadow
/**
* 设置x方向的偏移量,设置单位
* @param unit @{@link android.util.TypedValue#TYPE_DIMENSION}
* @param offset x轴偏移
*/
fun setXOffset(unit:Int,offset:Float): IShadow
/**
* 设置竖直方向的偏移量
* @param offset y轴偏移
*/
fun setYOffset(offset:Float): IShadow
/**
* 设置竖直方向的偏移量,带单位
* @param unit @{@link android.util.TypedValue#TYPE_DIMENSION}
* @param offset y轴偏移
*/
fun setYOffset(unit:Int,offset:Float): IShadow
/**
* 更新绘制
*/
fun commit();
}

View File

@@ -0,0 +1,12 @@
package com.mogo.och.common.module.callback
/**
* @author: wangmingjun
* @date: 2022/11/9
*/
interface OchAdasStartFailureCallback {
fun onStartAutopilotFailure(startFailedCode : String, startFailedMessage : String)
fun brakeStatusChanged(isBrakeAvailable: Boolean)
}

View File

@@ -0,0 +1,112 @@
package com.mogo.och.common.module.manager
import android.Manifest
import android.content.Context
import com.mogo.commons.module.status.IMogoStatusChangedListener
import com.mogo.commons.module.status.MogoStatusManager
import com.mogo.commons.module.status.StatusDescriptor
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.i
import com.mogo.eagle.core.utilcode.util.NetworkUtils
import com.mogo.och.common.module.utils.PermissionUtil
import com.mogo.och.common.module.wigets.toast.ToastCharterUtils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit
import kotlin.properties.Delegates
/**
* @author: wangmingjun
* @date: 2022/12/2
*/
object AbnormalFactorsLoopManager : IMogoStatusChangedListener {
const val TAG = "AbnormalFactorsLoopManager"
private const val LOOP_TIME = 10 * 1000L
private const val LOOP_DELAY = 5 * 1000L
private var socketStatus by Delegates.observable(false) { _, _, newValue ->
if (AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)) {
return@observable
}
if (FunctionBuildConfig.isDemoMode) {
return@observable
}
if (newValue) {
ToastCharterUtils.showToastLong("长链接状态恢复")
} else {
ToastCharterUtils.showToastLong("长链接异常,请开启相应权限或者查看网络")
}
}
private var looperDisposable: Disposable? = null
fun startLoopAbnormalFactors(context: Context) {
if (looperDisposable != null && !looperDisposable!!.isDisposed) {
return
}
i(TAG, "startLoopAbnormalFactors()")
looperDisposable = Observable.interval(LOOP_DELAY, LOOP_TIME, TimeUnit.MILLISECONDS)
.map { aLong -> aLong + 1 }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { aLong -> checkAbnormalFactors(context) }
//长链接监听
MogoStatusManager.getInstance().registerStatusChangedListener(
TAG,
StatusDescriptor.CLOUD_SOCKET, this
)
}
private fun checkAbnormalFactors(context: Context) {
var locationStatusPermsStatus = false
var networkStatus = false
//定位权限
locationStatusPermsStatus =
!(!PermissionUtil.isLocServiceEnable(context) || !PermissionUtil.checkPermission(
context, *arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION
)
))
//网络状态或者网络权限是否打开
if (NetworkUtils.isConnected(context)) {
networkStatus = true
}
//长链接状态 socketStatus
var toastStr = ""
if (!locationStatusPermsStatus) toastStr += "定位服务异常 "
if (!networkStatus) toastStr += " 网络异常 "
i(TAG, "abnormal_factors_Str = $toastStr")
if (!FunctionBuildConfig.isDemoMode && toastStr !== "") {
ToastCharterUtils.showToastLong(toastStr + "请开启相应权限或者查看网络")
}
}
fun stopLoopAbnormalFactors() {
looperDisposable!!.dispose()
looperDisposable == null
MogoStatusManager.getInstance().unregisterStatusChangedListener(
TAG,
StatusDescriptor.CLOUD_SOCKET, this
)
}
override fun onStatusChanged(descriptor: StatusDescriptor?, isTrue: Boolean) {
//长链接监听、
if (StatusDescriptor.CLOUD_SOCKET == descriptor) {
socketStatus = isTrue
}
}
}

View File

@@ -0,0 +1,36 @@
package com.mogo.och.common.module.manager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
/**
* @author: wangmingjun
* @date: 2022/10/24
*/
object CharterSendTripInfoManager{
const val START_TRIP = 1
const val END_TRIP = 2
const val LEAVE_STATION = 3
const val ARRIVE_STATION = 4
/**
* 行程信息
* @param type 事件类型, 1:行程开始, 2:行程结束, 3:出站, 4:进站, 5:城市占道施工预警
* @param lineName 路线名, for type 1, 2
* @param departureStopName 出站站点名, for type 3, 4
* @param arrivalStopName 下一站到达站点名, for type 3, 4
* @param isLastStop 是否终点站(下一站或者要到达站)
* @return
*/
fun sendCharterTripInfo(type: Int, lineName: String,
departureStopName: String,
arrivalStopName: String,
isLastStop: Boolean) {
d(SceneConstant.M_BUS + "CharterSendTripInfoManager", "type: "+ type
+", lineName: "+ lineName +", departureStopName: "+ departureStopName
+ ", arrivalStopName: "+arrivalStopName+", isLastStop: "+isLastStop)
CallerAutoPilotControlManager.sendTripInfo(type,lineName,departureStopName, arrivalStopName, isLastStop)
}
}

View File

@@ -0,0 +1,76 @@
package com.mogo.och.common.module.manager
import android.annotation.SuppressLint
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager.sendOperatorSetAcceleratedSpeed
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager.sendOperatorSetHorn
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import java.util.*
/**
* 魔方连接状态和设备管理
*/
@SuppressLint("StaticFieldLeak")
class DriverMoFangFunctionManager private constructor() {
companion object {
val driverMoFangFunctionManager: DriverMoFangFunctionManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
DriverMoFangFunctionManager()
}
}
private val TAG = "DriverMoFangFunctionManager"
private var timerHorn: Timer? = null
private var timerAcc: Timer? = null
/**
* 发送加速和减速复位的时候isSend为false其他都是trueacc就是具体的值
* acc -1 是减速, -2是刹停 0是复位时候传
*/
@Synchronized
fun sendAcc(isSend: Boolean, acc: Double) {
if (isSend) {
if (timerAcc == null) {
timerAcc = Timer()
timerAcc!!.schedule(object : TimerTask() {
override fun run() {
d(SceneConstant.M_CHARTER_D + TAG,"sendOperatorSetAcceleratedSpeed $isSend $acc")
sendOperatorSetAcceleratedSpeed(acc)
}
}, 0, 500)
}
} else {
if (timerAcc != null) {
timerAcc!!.cancel()
timerAcc = null
}
d(SceneConstant.M_CHARTER_D + TAG,"sendOperatorSetAcceleratedSpeed $isSend $acc")
sendOperatorSetAcceleratedSpeed(acc)
}
}
fun reset(){
d(SceneConstant.M_CHARTER_D + TAG,"sendAcc false 0.0")
sendAcc(false,0.0)
}
/**
* 鸣笛,鸣笛需要手动结束
*/
fun sendOperatorSetHornByDriver(){
d(SceneConstant.M_CHARTER_D + TAG,"sendOperatorSetHorn(1.0)")
sendOperatorSetHorn(1.0)
if (timerHorn == null) {
timerHorn = Timer()
}
timerHorn!!.schedule(object : TimerTask() {
override fun run() {
d(SceneConstant.M_CHARTER_D + TAG,"sendOperatorSetHorn(2.0)")
sendOperatorSetHorn(2.0)
timerHorn = null
}
}, 500)
}
}

View File

@@ -0,0 +1,29 @@
package com.mogo.och.common.module.manager
import com.mogo.eagle.core.function.call.telematic.CallerTelematicManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.och.common.module.bean.dpmsg.BaseDPMsg
import com.mogo.och.common.module.biz.constant.OchCommonConst
object LanSocketManager {
private const val TAG = "LanSocket"
fun sendMsgToClient(msg: BaseDPMsg?) {
CallerLogger.d(SceneConstant.M_CHARTER_D + TAG, "sendMsgToClient" + GsonUtils.toJson(msg))
CallerTelematicManager.sendMsgToAllClients(
OchCommonConst.BUSINESS_STRING,
GsonUtils.toJson(msg).toByteArray()
)
}
fun sendMsgToServer(msg: BaseDPMsg?) {
CallerLogger.d(SceneConstant.M_CHARTER_D + TAG, "sendMsgToServer" + GsonUtils.toJson(msg))
CallerTelematicManager.sendMsgToServer(
OchCommonConst.BUSINESS_STRING,
GsonUtils.toJson(msg).toByteArray()
)
}
}

View File

@@ -0,0 +1,141 @@
package com.mogo.och.common.module.manager.autopilotmanager;
import android.content.Context;
import androidx.annotation.Nullable;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotActionsListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatisticsListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotActionsListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotStatisticsListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerParallelDrivingActionsListenerManager;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.util.ParseVersionUtils;
import com.mogo.och.common.module.callback.OchAdasStartFailureCallback;
import com.zhjt.mogo.adas.data.bean.AutopilotStatistics;
import com.zhjt.mogo.adas.data.bean.UnableLaunchData;
import com.zhjt.mogo.adas.data.bean.UnableLaunchReason;
import java.util.ArrayList;
/**
* Created on 2022/10/9
* 工控机状态信息回调(判断是否能否启动自动驾驶的回调)
* 目前定的是3秒回调一次
*/
public class OCHAdasAbilityManager implements IMoGoAutopilotActionsListener, IMoGoAutopilotStatisticsListener {
private static final String TAG = OCHAdasAbilityManager.class.getSimpleName();
private boolean isAutopilotAbility;
private UnableLaunchData unableLaunchData;
private ArrayList<UnableLaunchReason> unableAutopilotReasons;
private String startFailedCode = "";
private String startFailedMessage = "";
private OchAdasStartFailureCallback failureCallback = null;
private static final class SingletonHolder {
private static final OCHAdasAbilityManager INSTANCE = new OCHAdasAbilityManager();
}
public static OCHAdasAbilityManager getInstance() {
return SingletonHolder.INSTANCE;
}
public void init(Context context) {
this.isAutopilotAbility = CallerAutopilotActionsListenerManager.INSTANCE.isAutopilotAbility();
this.unableLaunchData = CallerAutopilotActionsListenerManager.INSTANCE.getUnableLaunchData();
this.unableAutopilotReasons = CallerAutopilotActionsListenerManager.INSTANCE.getUnableAutopilotReasons();
initListeners();
}
public void setAdasStartFailureCallback(OchAdasStartFailureCallback callback){
failureCallback = callback;
}
public boolean getAutopilotAbilityStatus(){
return isAutopilotAbility;
}
public String getOriginalData() {
return unableLaunchData == null ? "" : unableLaunchData.getJson();
}
public String getAutopilotUnAbilityReason(){
try {
if(unableAutopilotReasons==null||unableAutopilotReasons.isEmpty()){
return "未知异常";
}else {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < unableAutopilotReasons.size(); i++) {
stringBuilder.append(unableAutopilotReasons.get(i));
if(i<unableAutopilotReasons.size()-1){
stringBuilder.append("\n");
}
}
return stringBuilder.toString();
}
}catch (Exception e){
e.printStackTrace();
return "未知异常";
}
}
public String getStartFailedCode() {
return startFailedCode;
}
public String getStartFailedMessage() {
return startFailedMessage;
}
private void initListeners() {
//2022.10.9 工控机状态信息回调(判断是否能否启动自动驾驶的回调), 目前定的是3秒回调一次
CallerAutopilotActionsListenerManager.INSTANCE.addListener(TAG, this);
CallerAutopilotStatisticsListenerManager.INSTANCE.addListener(TAG,this);
}
private void releaseListeners() {
CallerAutopilotActionsListenerManager.INSTANCE.removeListener(this);
CallerAutopilotStatisticsListenerManager.INSTANCE.removeListener(this);
CallerParallelDrivingActionsListenerManager.INSTANCE.removeListener(TAG);
}
@Override
public void onAutopilotAbility(boolean isAutopilotAbility,@Nullable UnableLaunchData unableLaunchData, @Nullable ArrayList<UnableLaunchReason> unableAutopilotReasons) {
this.isAutopilotAbility = isAutopilotAbility;
this.unableLaunchData = unableLaunchData;
this.unableAutopilotReasons = unableAutopilotReasons;
Logger.d(TAG, "是否可以启动自动驾驶=" + isAutopilotAbility + " 原因=" + (unableAutopilotReasons == null ? null : unableAutopilotReasons.toString()) + " 原始数据=" + (unableLaunchData == null ? null : unableLaunchData.getJson()));
if (unableAutopilotReasons != null && getMapVersion() < 30600) {
//刹车变化回调
Logger.d(TAG,"onAutopilotAbility = " + isAutopilotAbility +
" onAutopilotAbility =" + unableAutopilotReasons.toString());
if (unableAutopilotReasons.toString().contains(UnableLaunchReason.SourceType.CHASSIS.name())
&& unableAutopilotReasons.toString().contains(UnableLaunchReason.UnableType.BRAKE.name())) {
failureCallback.brakeStatusChanged(isAutopilotAbility);
}
}
}
@Override
public void onAutopilotStatistics(@Nullable AutopilotStatistics statistics) {
if (statistics == null) return;
Logger.d(TAG, "AutopilotStatistics= " + statistics.status);
if (failureCallback != null && 1 == statistics.status){
startFailedCode = statistics.failedMessage.getCode();
startFailedMessage = statistics.failedMessage.getMsg();
failureCallback.onStartAutopilotFailure(startFailedCode, startFailedMessage);
Logger.d(TAG, String.format("statistics-startFailedCode = s%; startFailedMessage = s%",
startFailedCode, startFailedMessage));
}
}
private int getMapVersion(){
return ParseVersionUtils.parseVersion(true, CallerAutoPilotStatusListenerManager.INSTANCE.getDockerVersion());
}
public void release() {
releaseListeners();
}
}

View File

@@ -0,0 +1,65 @@
package com.mogo.och.common.module.manager.beautifymode
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.function.api.hmi.view.IViewControlListener
import com.mogo.eagle.core.function.api.telematic.IReceivedMsgListener
import com.mogo.eagle.core.function.call.hmi.CallerHmiViewControlListenerManager
import com.mogo.eagle.core.function.call.telematic.CallerTelematicListenerManager
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import java.util.concurrent.ConcurrentHashMap
object BeautifyManager : IViewControlListener, IReceivedMsgListener {
private val TAG = BeautifyManager::class.java.simpleName
enum class ChangeTypeEnum{
BEAUTIFY_TYPE,// 美化模式变化
ORDER_STATU_CHANGE,// 订单状态发生变化
ARRIVED_DEST,// 到站
STOPSITE_SUCCESS,// 靠边停车成功
STOPSITE_SUCCESS_RUN// 靠边停车成功后恢复为正在驾驶
}
private val orderStatusChangeListeners = ConcurrentHashMap<String, IBeautifyModeCallback>()
init {
if (AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
CallerHmiViewControlListenerManager.addListener(TAG,this)
} else if (AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)) {
CallerTelematicListenerManager.addListener(TAG,this)
}
}
fun setStatusChangeListener(tag: String, orderStatusChangeListener: IBeautifyModeCallback?) {
if (tag.isBlank()) return
if (orderStatusChangeListener == null) {
orderStatusChangeListeners.remove(tag)
return
}
orderStatusChangeListeners[tag] = orderStatusChangeListener
}
fun notifyViewChange(typeEnum: ChangeTypeEnum){
CallerLogger.d(TAG,"美化模式变化原因:${typeEnum}")
orderStatusChangeListeners.forEach {
it.value.dispatchStatus(typeEnum)
}
}
override fun onReceivedMsg(type: Int, byteArray: ByteArray) {
}
override fun onDemoMode(isDemoMode: Boolean) {
notifyViewChange(ChangeTypeEnum.BEAUTIFY_TYPE)
}
override fun updateFuncMode(tag: String, boolean: Boolean) {
if (tag == IViewControlListener.FUNC_MODE_DEMO) {
notifyViewChange(ChangeTypeEnum.BEAUTIFY_TYPE)
}
}
}

View File

@@ -0,0 +1,6 @@
package com.mogo.och.common.module.manager.beautifymode
interface IBeautifyModeCallback {
fun dispatchStatus(typeEnum: BeautifyManager.ChangeTypeEnum)
}

View File

@@ -0,0 +1,178 @@
package com.mogo.och.common.module.manager.debug
import android.content.DialogInterface
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.WindowManager
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import com.mogo.commons.mvp.MvpDialogFragment
import com.mogo.eagle.core.function.hmi.ui.setting.ToggleDebugView
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_BUS_P
import com.mogo.eagle.core.utilcode.util.AppStateManager
import com.mogo.eagle.core.utilcode.util.BarUtils
import com.mogo.eagle.core.utilcode.util.ClickUtils
import com.mogo.eagle.core.utilcode.util.KeyboardUtils
import com.mogo.och.common.module.R
import com.mogo.och.common.module.wigets.toast.ToastCharterUtils
import kotlinx.android.synthetic.main.m1_debugview_pass.*
/**
* @author: yangyakun
* @date: 2023/1/28
*/
class DebugViewWatchDogFragment :
MvpDialogFragment<DebugViewWatchDogFragment?, DebugWatchDogPresenter?>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.DialogFullScreen) //dialog全屏
}
override fun getLayoutId(): Int = R.layout.m1_debugview_pass
override fun getTagName(): String = TAG
override fun initViews() {
dialog?.setCancelable(false)
actv_password_submit.setOnClickListener {
val text = acet_close.text
if(text==null||text.isEmpty()){
ToastCharterUtils.showToastShort("请输入密码")
}else{
if(text.toString() == "123987"){
dismissAllowingStateLoss()
dialog?.window?.let {
KeyboardUtils.hideSoftInput(it)
}
ToggleDebugView.toggleDebugView.toggle(requireContext())
}else{
ToastCharterUtils.showToastShort("请输入正确密码")
}
}
}
actv_cancle.setOnClickListener {
dialog?.window?.let {
KeyboardUtils.hideSoftInput(it)
}
dismissAllowingStateLoss()
}
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
acet_close.text?.clear()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.window?.let {
BarUtils.hideStatusBarAndSticky(it)
it.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
dialog?.setOnShowListener { _ ->
it.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
BarUtils.hideStatusBarAndSticky(it)
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
}
override fun createPresenter(): DebugWatchDogPresenter =
DebugWatchDogPresenter(this)
/**
* 重写父类show()方法
* 避免出现java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
*/
override fun show(manager: FragmentManager, tag: String?) {
try {
var cls = this.javaClass.superclass ?: return
while (true) {
if (cls.name == "java.lang.Object") {
break
}
cls = cls.superclass!!
if (cls == DialogFragment::class.java) {
break
}
}
val mDismissed = cls.getDeclaredField("mDismissed")
val mShownByMe = cls.getDeclaredField("mShownByMe")
mDismissed.isAccessible = true
mShownByMe.isAccessible = true
mDismissed.setBoolean(this, false)
mShownByMe.setBoolean(this, true)
if (isAdded) { //解决方法就是添加这行代码如果已经添加了就移除掉然后再show就不会出现Fragment already added的错误了。
return
}
val ft: FragmentTransaction = manager.beginTransaction()
ft.add(this, tag)
ft.commitAllowingStateLoss()
} catch (e: Exception) {
Log.e("DialogFragment", "show", e.fillInStackTrace())
}
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
CallerLogger.d(M_BUS_P+ TAG, "onViewStateRestored")
super.onViewStateRestored(savedInstanceState)
}
companion object {
const val TAG = "DebugViewWatchDogFragment"
@JvmStatic
fun newInstance(): DebugViewWatchDogFragment {
val args = Bundle()
val fragment = DebugViewWatchDogFragment()
CallerLogger.d(M_BUS_P + TAG, "创建新的Fragment")
fragment.arguments = args
return fragment
}
fun showDebugView(
childFragmentManager: FragmentManager,
parentFragmentManager: FragmentManager,
debugViewWatchDogFragment: DebugViewWatchDogFragment?
) {
val fragmentByTag: Fragment? = childFragmentManager.findFragmentByTag(TAG)
if (fragmentByTag is DialogFragment) {
if (fragmentByTag.dialog != null && fragmentByTag.dialog!!.isShowing) {
CallerLogger.d(M_BUS_P + TAG, "正在展示")
return
}
if (fragmentByTag.dialog != null && fragmentByTag.isAdded) {
if (AppStateManager.currentActivity() == null) { // 没有在当前应用内 在启动页面关闭应用
CallerLogger.d(M_BUS_P + TAG, "权限验证")
return
}
}
}
if (debugViewWatchDogFragment != null) {
if (debugViewWatchDogFragment.dialog != null && debugViewWatchDogFragment.dialog!!.isShowing) {
CallerLogger.d(M_BUS_P + TAG, "正在展示")
return
}
if (ClickUtils.isFastClick()) {
if (debugViewWatchDogFragment.isAdded) { //解决方法就是添加这行代码如果已经添加了就移除掉然后再show就不会出现Fragment already added的错误了。
parentFragmentManager.beginTransaction().remove(debugViewWatchDogFragment)
.commitAllowingStateLoss()
CallerLogger.d(M_BUS_P + TAG, "已经添加正在移除")
}
debugViewWatchDogFragment.show(parentFragmentManager, TAG)
CallerLogger.d(M_BUS_P + TAG, "展示开关门")
} else {
CallerLogger.d(M_BUS_P + TAG, "dialog 1s内执行一次")
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
package com.mogo.och.common.module.manager.debug
import com.mogo.commons.mvp.Presenter
class DebugWatchDogPresenter(view: DebugViewWatchDogFragment?) :
Presenter<DebugViewWatchDogFragment?>(view){
companion object {
private const val TAG = "BusPassengerFunctionPresenter"
}
}

View File

@@ -0,0 +1,53 @@
package com.mogo.och.common.module.manager.devicemanage
import com.mogo.commons.voice.AIAssist
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.och.common.module.bean.dpmsg.AppConnectMsg
import com.mogo.och.common.module.biz.common.socketmessage.OCHSocketMessageManager
import com.mogo.och.common.module.manager.LanSocketManager
import com.mogo.och.common.module.utils.DateTimeUtil
import com.mogo.och.common.module.utils.RxUtils
import com.mogo.och.common.module.voice.VoiceNotice
import com.mogo.och.common.module.wigets.toast.ToastCharterUtils
import io.reactivex.disposables.Disposable
object LightAirconditionDoorManager {
private const val TAG = "LightAirconditionDoorManager"
private var dooorSubscribe: Disposable? = null
fun go2OpenDoor(go2Open:Boolean){
val canOpenOrCloseDoor = canOpenOrCloseDoor()
if(!canOpenOrCloseDoor.isNullOrBlank()){
ToastCharterUtils.showToastLong(canOpenOrCloseDoor)
return
}
RxUtils.disposeSubscribe(dooorSubscribe)
dooorSubscribe = RxUtils.createSubscribe(1000) {
CallerAutoPilotControlManager.sendRoboBusJinlvM1FrontDoorCmd(0)
}
if(go2Open) {
if (LightAirconditionDoorStatusManager.doorStatus.isOpen) {
return
}
CallerAutoPilotControlManager.sendRoboBusJinlvM1FrontDoorCmd(1)
}else{
if (!LightAirconditionDoorStatusManager.doorStatus.isOpen) {
return
}
CallerAutoPilotControlManager.sendRoboBusJinlvM1FrontDoorCmd(2)
}
}
private fun canOpenOrCloseDoor(): String? {
val location = CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()
return if(location.gnssSpeed<0.3){
null
}else{
"车辆行驶中不可以开关门哦~"
}
}
}

View File

@@ -0,0 +1,128 @@
package com.mogo.och.common.module.manager.devicemanage
import chassis.VehicleStateOuterClass
import com.mogo.eagle.core.function.api.autopilot.IMoGoRoboBusJinlvM1StatesListener
import com.mogo.eagle.core.function.call.autopilot.CallerRoboBusJinlvM1StatesListenerManager
import com.mogo.och.common.module.manager.devicemanage.callback.LightAirconditionDoorCallback
import com.mogo.och.common.module.manager.devicemanage.data.AirconditionStatus
import com.mogo.och.common.module.manager.devicemanage.data.DoorStatus
import com.mogo.och.common.module.manager.devicemanage.data.HeaterStatue
import com.mogo.och.common.module.manager.devicemanage.data.LightStatus
import java.util.concurrent.ConcurrentHashMap
object LightAirconditionDoorStatusManager : IMoGoRoboBusJinlvM1StatesListener {
private const val TAG = "OCHM1LightAirconditionDoorStatusManager"
init {
CallerRoboBusJinlvM1StatesListenerManager.addListener(TAG, this)
CallerRoboBusJinlvM1StatesListenerManager.setListenerHz(TAG,5)
}
val M_LISTENERS: ConcurrentHashMap<String, LightAirconditionDoorCallback> =
ConcurrentHashMap()
val airconditionStatus = AirconditionStatus(false, 0, 26, 2)
val heaterStatue = HeaterStatue(false, 2)
val doorStatus = DoorStatus(false)
val lightStatus = LightStatus(false, false, false)
fun addListener(
tag: String,
listener: LightAirconditionDoorCallback
) {
if (M_LISTENERS.containsKey(tag)) {
return
}
M_LISTENERS[tag] = listener
listener.onLightTop1Callback(lightStatus,true)
listener.onLightTop2Callback(lightStatus,true)
listener.onLightAtmosphereCallback(lightStatus,true)
listener.onAirconditionStatusCallback(heaterStatue.isOpen,airconditionStatus,true)
listener.onHeaterStatusCallback(airconditionStatus.isOpen,heaterStatue,true)
}
fun removeListener(tag: String) {
if (!M_LISTENERS.containsKey(tag)) {
return
}
M_LISTENERS.remove(tag)
}
/**
* 删除监听
* @param listener 要删除的监听对象
*/
fun removeListener(listener: LightAirconditionDoorCallback) {
if (!M_LISTENERS.containsValue(listener)) {
return
}
M_LISTENERS.forEach {
if (it.value == listener) {
M_LISTENERS.remove(it.key)
}
}
}
override fun onRoboBusJinlvM1States(states: VehicleStateOuterClass.RoboBusJinlvM1State) {
val airConditionerState = states.airConditionerState
val heaterState = states.heaterState
if (airConditionerState.isOn != airconditionStatus.isOpen ||
airConditionerState.mode != airconditionStatus.pattert ||
airConditionerState.temperature != airconditionStatus.temperature ||
airConditionerState.windSpeed != airconditionStatus.windSpeed
) {
airconditionStatus.isOpen = airConditionerState.isOn
airconditionStatus.pattert = airConditionerState.mode
airconditionStatus.temperature = airConditionerState.temperature
airconditionStatus.windSpeed = airConditionerState.windSpeed
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onAirconditionStatusCallback(heaterState.isOn,airconditionStatus,false)
}
}
if (heaterState.isOn != heaterStatue.isOpen ||
heaterState.windSpeed != heaterStatue.windSpeed
) {
heaterStatue.isOpen = heaterState.isOn
heaterStatue.windSpeed = heaterState.windSpeed
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onHeaterStatusCallback(airConditionerState.isOn,heaterStatue,false)
}
}
if (states.frontDoorState.isOn != doorStatus.isOpen) {
doorStatus.isOpen = states.frontDoorState.isOn
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onDoorStatusCallback(doorStatus.isOpen,false)
}
}
if(states.mainLamp1State.isOn != lightStatus.isOpenLight1){
lightStatus.isOpenLight1 = states.mainLamp1State.isOn
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onLightTop1Callback(lightStatus,false)
}
}
if(states.mainLamp2State.isOn != lightStatus.isOpenLight2){
lightStatus.isOpenLight2 = states.mainLamp2State.isOn
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onLightTop2Callback(lightStatus,false)
}
}
if(states.smallLampState.isOn != lightStatus.isOpenatmosphere){
lightStatus.isOpenatmosphere = states.smallLampState.isOn
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onLightAtmosphereCallback(lightStatus,false)
}
}
}
}

View File

@@ -0,0 +1,129 @@
package com.mogo.och.common.module.manager.devicemanage
import chassis.Chassis
import chassis.VehicleStateOuterClass
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisDoorStateListener
import com.mogo.eagle.core.function.call.autopilot.CallerChassisDoorStateListenerManager
import com.mogo.eagle.core.function.call.base.CallerBase
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.och.common.module.manager.devicemanage.callback.DoorStateCallback
import com.mogo.och.common.module.manager.devicemanage.data.DoorPosition
import com.mogo.och.common.module.manager.devicemanage.data.DoorState
object TaxiDoorStateManager : IMoGoChassisDoorStateListener,
CallerBase<DoorStateCallback>() {
private val TAG = TaxiDoorStateManager::class.java.simpleName
@Volatile
private var haveOpenDoor: Boolean? = null
init {
CallerChassisDoorStateListenerManager.addListener(TAG, this)
}
override fun doSomeAfterAddListener(tag: String, listener: DoorStateCallback) {
val doorList = CallerChassisDoorStateListenerManager.getDoorList()
if(doorList is MutableList<VehicleStateOuterClass.DoorStateV2>){
doSomeAfterAddListenerInner(doorList)
doorList.forEach {
onAutopilotSingleDoorState(it.number,it.status)
}
}else{
doSomeAfterAddListenerInner(mutableListOf())
}
}
private fun doSomeAfterAddListenerInner(doorList: MutableList<VehicleStateOuterClass.DoorStateV2>){
var have = false
doorList.forEach {
if (it.status == 1) {//0关闭 1开着 2未知
have = true
}
}
haveOpenDoor = have
invokeOpenState(have)
}
/**
* 主要用来判断是否有门开着
*/
override fun onAutopilotDoorState(doorList: MutableList<VehicleStateOuterClass.DoorStateV2>) {
var have = false
doorList.forEach {
if (it.status == 1) {//0关闭 1开着 2未知
have = true
}
}
if (have != haveOpenDoor) {
haveOpenDoor = have
invokeOpenState(have)
}
}
/**
* 判断单个车门是否开着
*/
override fun onAutopilotSingleDoorState(num: Chassis.DoorNumber, status: Int) {
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "门太变化:${num}--${status}")
when (status) {
0 -> { exchangeEnum(num, DoorState.CLOSE)}
1 -> {exchangeEnum(num, DoorState.OPEN)}
2 -> {exchangeEnum(num, DoorState.UNKNOWN)}
else -> {}
}
}
private fun exchangeEnum(num: Chassis.DoorNumber, state: DoorState) {
when (num) {
Chassis.DoorNumber.FRONT_LEFT -> {
invokeSingleDoorOpenState(DoorPosition.FRONT_LEFT, state)
}
Chassis.DoorNumber.FRONT_RIGHT -> {
invokeSingleDoorOpenState(DoorPosition.FRONT_RIGHT, state)
}
Chassis.DoorNumber.REAR_LEFT -> {
invokeSingleDoorOpenState(DoorPosition.REAR_LEFT, state)
}
Chassis.DoorNumber.REAR_RIGHT -> {
invokeSingleDoorOpenState(DoorPosition.REAR_RIGHT, state)
}
Chassis.DoorNumber.MIDDLE -> {
invokeSingleDoorOpenState(DoorPosition.MIDDLE, state)
}
else -> {}
}
}
/**
* @param have true 有车门开着
* false 没有车门开着(可能开着可能未知)
*/
@Synchronized
private fun invokeOpenState(have: Boolean) {
M_LISTENERS.forEach {
val listener = it.value
listener.hasOpenDoor(have)
}
}
/**
* @param doorPosition 车门位置
* @param doorState 车门状态
*/
@Synchronized
private fun invokeSingleDoorOpenState(doorPosition: DoorPosition,doorState: DoorState) {
M_LISTENERS.forEach {
val listener = it.value
listener.doorStateChangeCallback(doorPosition,doorState)
}
}
}

View File

@@ -0,0 +1,19 @@
package com.mogo.och.common.module.manager.devicemanage.callback
import com.mogo.och.common.module.manager.devicemanage.data.DoorPosition
import com.mogo.och.common.module.manager.devicemanage.data.DoorState
interface DoorStateCallback {
/**
* @param have true 有车门开着
* false 没有车门开着(可能开着可能未知)
*/
fun hasOpenDoor(have:Boolean){}
/**
* @param position 车门位置
* @param state 当前车门状态
*/
fun doorStateChangeCallback(position: DoorPosition,state: DoorState){}
}

View File

@@ -0,0 +1,26 @@
package com.mogo.och.common.module.manager.devicemanage.callback
import com.mogo.och.common.module.manager.devicemanage.data.AirconditionStatus
import com.mogo.och.common.module.manager.devicemanage.data.HeaterStatue
import com.mogo.och.common.module.manager.devicemanage.data.LightStatus
interface LightAirconditionDoorCallback {
fun onAirconditionStatusCallback(heaterIsOpen: Boolean, airconditionStatus: AirconditionStatus,
isFirst: Boolean) {
}
fun onHeaterStatusCallback(airconditionIsOpen: Boolean, heaterStatue: HeaterStatue,
isFirst: Boolean) {
}
fun onDoorStatusCallback(isOpen: Boolean, isFirst: Boolean) {}
fun onLightTop1Callback(lightStatus: LightStatus, isFirst: Boolean) {
}
fun onLightTop2Callback(lightStatus: LightStatus, isFirst: Boolean) {}
fun onLightAtmosphereCallback(lightStatus: LightStatus, isFirst: Boolean) {}
}

View File

@@ -0,0 +1,8 @@
package com.mogo.och.common.module.manager.devicemanage.data
data class AirconditionStatus(
var isOpen: Boolean,
var pattert: Int,
var temperature: Int,
var windSpeed: Int
)

View File

@@ -0,0 +1,12 @@
package com.mogo.och.common.module.manager.devicemanage.data
data class DoorStatus(var isOpen: Boolean)
enum class DoorPosition {
FRONT_LEFT, FRONT_RIGHT, REAR_LEFT, REAR_RIGHT, MIDDLE
}
enum class DoorState {
OPEN,CLOSE,UNKNOWN
}

View File

@@ -0,0 +1,3 @@
package com.mogo.och.common.module.manager.devicemanage.data
data class HeaterStatue(var isOpen: Boolean, var windSpeed: Int)

View File

@@ -0,0 +1,7 @@
package com.mogo.och.common.module.manager.devicemanage.data
data class LightStatus(
var isOpenLight1: Boolean,
var isOpenLight2: Boolean,
var isOpenatmosphere: Boolean
)

View File

@@ -0,0 +1,54 @@
package com.mogo.och.common.module.manager.distancemamager
data class DistanceDegree(var distance: Float, var degree: Double?, var isNext: Boolean?) :
Comparable<DistanceDegree> {
override fun compareTo(other: DistanceDegree): Int {
// 对比距离
if (distance == other.distance) {
return 0;
} else if (distance < other.distance) {
return -1;
}
return 1;
}
var next: DistanceDegree? = null
fun recycle() {
synchronized(sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool
sPool = this
sPoolSize++
}
//Logger.d("DistanceDegree","缓存对象个数${sPoolSize}个")
}
}
companion object {
var sPoolSync = Any()
private var sPool: DistanceDegree? = null
private var sPoolSize = 0
private var MAX_POOL_SIZE = 20
fun obtain(distance: Float, degree: Double?, isNext: Boolean?): DistanceDegree {
synchronized(sPoolSync) {
if (sPool != null) {
val m: DistanceDegree = sPool!!
sPool = m.next
m.next = null
m.distance = distance
m.degree = degree
m.isNext = isNext
sPoolSize--
//Logger.d("DistanceDegree","取出一个对象个数${sPoolSize}个")
return m
}
//Logger.d("DistanceDegree","创建一个对象 ${sPoolSize}个")
return DistanceDegree(distance, degree, isNext)
}
}
}
}

View File

@@ -0,0 +1,43 @@
package com.mogo.och.common.module.manager.distancemamager
import com.mogo.eagle.core.data.map.MogoLocation
interface IDistanceListener {
/**
* @param distance 距离终点坐标的距离(终点坐标设置错误可能为负值)
*/
fun distanceCallback(distance: Float){}
/**
* 两个站点之间的距离
*/
fun stationDistanceCallback(stationDistance:Float){}
}
interface ITrajectoryListener{
/**
* @param routeArrivied 已经走过的坐标
* @param routeArriving 没有走过的坐标
* @param location 车的坐标
* @return
*/
fun trajectoryCallback(
routeArrivied: MutableList<MogoLocation>,
routeArriving: MutableList<MogoLocation>,
location: MogoLocation
)
}
interface ITrajectoryWithStationListener{
/**
* @param routeArrivied 已经走过的坐标 第一个是开始站点坐标
* @param routeArriving 没有走过的坐标 最后一个是结束站点坐标
* @param location 车的坐标
* @return
*/
fun trajectoryWithStationCallback(
routeArrivied: MutableList<MogoLocation>,
routeArriving: MutableList<MogoLocation>,
location: MogoLocation
)
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.common.module.manager.distancemamager
import com.mogo.eagle.core.data.map.MogoLocation
data class StationAndIndex(
var stationPoint: MogoLocation?,// 站点坐标
var index: Int?,// 坐标对应轨迹中最近的点
var distance: Float?,//轨迹中最近的点
var isNext:Boolean?,// 最近的点在轨迹中是在站点的下一个还是上一个
var lineId:Long?,// 站点所属轨迹 todo 未来轨迹线路id和站点线路id 分别存储
)

View File

@@ -0,0 +1,775 @@
package com.mogo.och.common.module.manager.distancemamager
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoPlanningRottingListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningRottingListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.e
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_OCHCOMMON
import com.mogo.eagle.core.utilcode.util.CoordinateUtils
import com.mogo.eagle.core.utilcode.util.LocationUtils
import com.mogo.och.common.module.biz.constant.OchCommonConst
import com.mogo.och.common.module.manager.loopmanager.BizLoopManager
import com.mogo.och.common.module.manager.loopmanager.LoopInfo
import com.mogo.och.common.module.utils.CoordinateCalculateRouteUtil
import io.reactivex.schedulers.Schedulers
import mogo.telematics.pad.MessagePad
import java.util.concurrent.ConcurrentHashMap
/**
* 计算当前位置距离站点距离和走过的和未走过的轨迹点
*/
object TrajectoryAndDistanceManager : IMoGoPlanningRottingListener {
private val distanceListeners: ConcurrentHashMap<String, IDistanceListener> =
ConcurrentHashMap()
private val trajectoryListeners: ConcurrentHashMap<String, ITrajectoryListener> =
ConcurrentHashMap()
private val trajectoryWithStationListeners: ConcurrentHashMap<String, ITrajectoryWithStationListener> =
ConcurrentHashMap()
private const val TAG = "DistanceManager"
private const val DISTANCE = "BusPassengerModelDistance"
fun addDistanceListener(tag: String, listener: IDistanceListener) {
if (distanceListeners.containsKey(tag)) {
return
}
distanceListeners[tag] = listener
}
fun addTrajectoryListener(tag: String, listener: ITrajectoryListener) {
if (trajectoryListeners.containsKey(tag)) {
return
}
trajectoryListeners[tag] = listener
}
@Suppress("unused")
fun addTrajectoryWithStationListener(tag: String, listener: ITrajectoryWithStationListener) {
if (trajectoryWithStationListeners.containsKey(tag)) {
return
}
trajectoryWithStationListeners[tag] = listener
}
/**
* 删除监听
* @param tag 标记,用来注销监听使用
*/
fun removeListener(tag: String) {
distanceListeners.remove(tag)
trajectoryListeners.remove(tag)
trajectoryWithStationListeners.remove(tag)
}
/**
* 所有轨迹点
*/
@Volatile
private var mRoutePoints: MutableList<MogoLocation>? = ArrayList()
/**
* 0-1 1-2 2-3 各个轨迹点的距离
*/
@Volatile
private var mRoutePointsDistance: MutableList<Float>? = ArrayList()
/**
* 所有轨迹点距离的和
*/
@Volatile
private var maxDistanceAllPoint: Double = 0.0
/**
* 线路Id
*/
@Volatile
private var lineId: Long? = null
/**
* 结束站点
*/
@Volatile
private var endStationInfo: StationAndIndex = StationAndIndex(null, null, null, null, null)
/**
* 开始站点
*/
@Volatile
private var startStationInfo: StationAndIndex = StationAndIndex(null, null, null, null, null)
/**
* startStationInfo endStationInfo 站点之间的距离
*/
private val stationDistance: ConcurrentHashMap<String, Float> = ConcurrentHashMap()
/**
* 上一次(上一个tick)计算最近点的缓存
*/
private var preCarLocationIndexInTrajectory = 0
init {
CallerPlanningRottingListenerManager.addListener(TAG, this)
}
override fun onAutopilotRotting(globalPathResp: MessagePad.GlobalPathResp?) {
d(M_OCHCOMMON + TAG, "onAutopilotRotting: 收到轨迹")
globalPathResp?.wayPointsList?.let {
if (it.size > 0) {
d(
M_OCHCOMMON + TAG,
"收到轨迹:轨迹个数${it.size}第一个点${it[0]}最后一个点:${it.last()} 轨迹id:${globalPathResp.lineId}"
)
@Suppress("SENSELESS_COMPARISON")
if (globalPathResp.lineId != null) {// 适配低版本不传递lineId
if (globalPathResp.lineId == lineId && !mRoutePoints.isNullOrEmpty()) {
d(M_OCHCOMMON + TAG, "重复轨迹")
startCalculateDistanceLoop()
return
}else{
this.endStationInfo.index = null
this.endStationInfo.distance = null
this.endStationInfo.isNext = null
this.startStationInfo.index = null
this.startStationInfo.distance = null
this.startStationInfo.isNext = null
}
this.lineId = globalPathResp.lineId
}
endCalculateDistanceLoop()
updateRoutePoints(it)
preCarLocationIndexInTrajectory = 0
startCalculateDistanceLoop()
}
}
}
private fun updateRoutePoints(routePoints: List<MessagePad.Location>?) {
mRoutePoints = null
mRoutePointsDistance = null
val latLngModels = CoordinateCalculateRouteUtil
.coordinateConverterWgsToGcjLocations(AbsMogoApplication.getApp(), routePoints!!)
mRoutePoints = latLngModels
TrajectoryCache.writeLastTrajectory2jsonFile(latLngModels, lineId.toString())
mRoutePointsDistance = ArrayList()
maxDistanceAllPoint = 0.0
mRoutePoints?.forEachIndexed { index, current ->
if (mRoutePoints!!.last() != current) {
val next = mRoutePoints!![index + 1]
val distanceItem = CoordinateUtils.calculateLineDistance(
current.longitude,
current.latitude,
next.longitude,
next.latitude
)
mRoutePointsDistance?.add(distanceItem)
maxDistanceAllPoint += distanceItem
}
}
}
/**
* 设置或清理站点坐标
*/
fun setStationPoint(
startStationInfo: MogoLocation?,
endStationInfo: MogoLocation?,
lineId: Long?
) {
d(
M_OCHCOMMON + TAG,
"线路id:${lineId}设置站点:开始站点${startStationInfo}、结束站点:${endStationInfo}"
)
if (startStationInfo == null || endStationInfo == null || lineId == -1L) {
this.endStationInfo.index = null
this.endStationInfo.distance = null
this.endStationInfo.isNext = null
this.endStationInfo.lineId = null
this.startStationInfo.index = null
this.startStationInfo.distance = null
this.startStationInfo.isNext = null
this.startStationInfo.lineId = null
preCarLocationIndexInTrajectory = 0
endCalculateDistanceLoop()
mRoutePoints = null
mRoutePointsDistance = null
TrajectoryCache.deleteCatcheFile()
this.endStationInfo.stationPoint = null
this.startStationInfo.stationPoint = null
this.lineId = null
stationDistance.clear()
} else {
if (isSameStation(this.startStationInfo.stationPoint, startStationInfo) &&
isSameStation(this.endStationInfo.stationPoint, endStationInfo)
) {
if (this.lineId != lineId) {
this.endStationInfo.index = null
this.endStationInfo.distance = null
this.endStationInfo.isNext = null
this.endStationInfo.lineId = null
this.startStationInfo.index = null
this.startStationInfo.distance = null
this.startStationInfo.isNext = null
this.startStationInfo.lineId = null
mRoutePoints = null
mRoutePointsDistance = null
TrajectoryCache.deleteCatcheFile()
stationDistance.clear()
}
} else {
this.endStationInfo.index = null
this.endStationInfo.distance = null
this.endStationInfo.isNext = null
this.endStationInfo.lineId = null
this.startStationInfo.index = null
this.startStationInfo.distance = null
this.startStationInfo.isNext = null
this.startStationInfo.lineId = null
if (this.lineId == 0L || this.lineId == null) {
// 兼容老MAP 不返回轨迹id lineID
} else {
if (this.lineId != lineId) {// bus 切换站点不会切线路
mRoutePoints = null
mRoutePointsDistance = null
TrajectoryCache.deleteCatcheFile()
}
}
stationDistance.clear()
}
this.endStationInfo.stationPoint = endStationInfo
this.startStationInfo.stationPoint = startStationInfo
this.lineId = lineId
startCalculateDistanceLoop()
}
}
private fun isSameStation(stationOne: MogoLocation?, stationTwo: MogoLocation?): Boolean {
if (stationOne == null || stationTwo == null) {
return false
}
if (stationOne.longitude == stationTwo.longitude && stationOne.latitude == stationTwo.latitude) {
return true
}
return false
}
/**
* 根据两点距离和轨迹距离来计算真实距离
*/
private fun calculateDistance() {
//mLocation gcj坐标
CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02().let {
if (mRoutePoints.isNullOrEmpty() || endStationInfo.stationPoint == null) {
d(M_OCHCOMMON + TAG, "没有轨迹或站点坐标停止计算")
//结束距离计算
endCalculateDistanceLoop()
return
}
if (it.latitude == 0.0 && it.longitude == 0.0) {
return
}
calculateRouteSumLength(it)
}
}
/**
* 暂停路距计算
*/
fun suspendCalculate() {
endCalculateDistanceLoop()
}
@Suppress("unused")
fun reStartCalculate() {
startCalculateDistanceLoop()
}
/**
* 启动路距计算
*/
private fun startCalculateDistanceLoop() {
BizLoopManager.setLoopFunction(
DISTANCE,
LoopInfo(
1,
::calculateDistance,
scheduler = Schedulers.computation()
)
)
d(M_OCHCOMMON + TAG, "开始路距计算")
}
/**
* 结束启动路距计算
*/
private fun endCalculateDistanceLoop() {
BizLoopManager.removeLoopFunction(DISTANCE)
d(M_OCHCOMMON + TAG, "结束路距计算")
}
@Synchronized
fun calculateRouteSumLength(
location: MogoLocation,
) {
val autoPilotState = CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().state
val locationInfo =
"自动驾驶状态:$autoPilotState line信息${lineId}定位信息:${location.latitude},${location.longitude},${location.heading} 当前速度:${location.gnssSpeed}"
if (mRoutePoints.isNullOrEmpty()) return
// 计算起始站点在轨迹中的信息 这个是一个常量
if (startStationInfo.stationPoint != null
&& startStationInfo.isNext == null
&& startStationInfo.index == null
&& startStationInfo.distance == null
) {
//要前往的站在轨迹中对应的点的信息
val startStationInfo = CoordinateCalculateRouteUtil.getNearestPointInfo(
preCarLocationIndexInTrajectory,
mRoutePoints!!.size,
mRoutePoints!!,
startStationInfo.stationPoint!!,
1,
useHeading = false
)
this.startStationInfo.isNext = startStationInfo.second
this.startStationInfo.index = startStationInfo.first
this.startStationInfo.distance = startStationInfo.third
preCarLocationIndexInTrajectory = startStationInfo.first
val calculateData =
"距离起始站点最近的点:${startStationInfo.first} 点在站的后面:${startStationInfo.second} 距离点的距离:${startStationInfo.third}"
writeLog(calculateData, locationInfo)
}
// 计算结束站点在轨迹中的信息 这个是一个常量可以缓存
if (endStationInfo.stationPoint != null
&& endStationInfo.isNext == null
&& endStationInfo.index == null
&& endStationInfo.distance == null
) {
//要前往的站在轨迹中对应的点
val endStationInfo = CoordinateCalculateRouteUtil.getNearestPointInfo(
preCarLocationIndexInTrajectory,
mRoutePoints!!.size,
mRoutePoints!!,
endStationInfo.stationPoint!!,
3,
useHeading = false
)
this.endStationInfo.isNext = endStationInfo.second
this.endStationInfo.index = endStationInfo.first
this.endStationInfo.distance = endStationInfo.third
val calculateData =
"距离结束站点最近的点:${endStationInfo.first} 点在站的后面:${endStationInfo.second} 距离点的距离:${endStationInfo.third}"
writeLog(calculateData, locationInfo)
}
try {
if (endStationInfo.stationPoint != null
&& endStationInfo.isNext != null
&& endStationInfo.index != null
&& endStationInfo.distance != null
&& startStationInfo.stationPoint != null
&& startStationInfo.isNext != null
&& startStationInfo.index != null
&& startStationInfo.distance != null
) {
calculateStationDistance()
}
} catch (e: Exception) {
e(M_OCHCOMMON + TAG, "计算两个站点间的距离")
}
val carLocationInfo: Triple<Int, Boolean?, Float> = if (endStationInfo.isNext == true) {
// 计算车的位置在轨迹中的信息 这个是一个变量可以缓存
CoordinateCalculateRouteUtil.getNearestPointInfo(
preCarLocationIndexInTrajectory, endStationInfo.index!!, mRoutePoints!!, location, 2
)
} else {
CoordinateCalculateRouteUtil.getNearestPointInfo(
preCarLocationIndexInTrajectory,
endStationInfo.index!! + 1,
mRoutePoints!!,
location,
2
)
}
val calculateData =
"距离轨迹点最近的点:${carLocationInfo.first} 点在站的后面:${carLocationInfo.second} 距离点的距离:${carLocationInfo.third}"
writeLog(calculateData, locationInfo)
if (carLocationInfo.second == null || carLocationInfo.third > 1_000) {
preCarLocationIndexInTrajectory = 0
return
}
var maxDisatance = 0.0f
if (carLocationInfo.second == true) {
if (carLocationInfo.first > 0) {
maxDisatance = mRoutePointsDistance?.get(carLocationInfo.first - 1) ?: 0f
}
} else {
maxDisatance = mRoutePointsDistance?.get(carLocationInfo.first) ?: 0f
}
if (carLocationInfo.third > maxDisatance) {
preCarLocationIndexInTrajectory = 0
writeLog("到点的距离${carLocationInfo.third},最大距离${maxDisatance}", locationInfo)
return
}
preCarLocationIndexInTrajectory = carLocationInfo.first
// 距离回调
try {
if (distanceListeners.size > 0) {
invokeDistance(carLocationInfo, location, locationInfo)
}
} catch (e: Exception) {
e(M_OCHCOMMON + TAG, "距离计算错误")
}
// 不带站点轨迹回调
try {
if (trajectoryListeners.size > 0) {
invokeTrajectory(carLocationInfo, location)
}
} catch (e: Exception) {
e(M_OCHCOMMON + TAG, "轨迹线(轨迹两头)计算错误")
}
// 只展示站点之间轨迹
try {
if (trajectoryWithStationListeners.size > 0) {
invokeTrajectoryWithStation(carLocationInfo, location)
}
} catch (e: Exception) {
e(M_OCHCOMMON + TAG, "轨迹线(站点两头)计算错误")
}
}
/**
* 计算站点间距离
*/
private fun calculateStationDistance() {
var lastSumLength: Float
val key = getKey()
if (stationDistance[key] != null) {
return
}
val stationIndex = endStationInfo.index ?: 0
if (startStationInfo.index!! < stationIndex - 1) {
// subList 是[) 需要的是[]
val subList = mRoutePoints!!.subList(startStationInfo.index!!, stationIndex + 1)
// 轨迹点所有的距离
lastSumLength = CoordinateCalculateRouteUtil.calculateRouteSumLength(subList)
val stationDistance = endStationInfo.distance ?: 0f
if (endStationInfo.isNext == true) {// isNext = true
lastSumLength -= stationDistance
} else {// isNext = false
lastSumLength += stationDistance
}
if (startStationInfo.isNext == true) {// 是否是下一个 true 下一个
lastSumLength += startStationInfo.distance!!
} else {
lastSumLength -= startStationInfo.distance!!
}
} else {
val lastPoints = endStationInfo.stationPoint
lastSumLength = CoordinateUtils.calculateLineDistance(
startStationInfo.stationPoint!!.longitude, startStationInfo.stationPoint!!.latitude,
lastPoints!!.longitude, lastPoints.latitude
)
}
d(M_OCHCOMMON + TAG, "两站点距离:$lastSumLength")
stationDistance[key] = lastSumLength
if (distanceListeners.size > 0) {
distanceListeners.forEach {
//val tag = it.key
val listener = it.value
listener.stationDistanceCallback(lastSumLength)
}
}
}
private fun getKey(): String {
return "${startStationInfo.stationPoint!!.longitude}${startStationInfo.stationPoint!!.latitude}${endStationInfo.stationPoint!!.longitude}${endStationInfo.stationPoint!!.latitude}"
}
/**
* 到结束站点的距离 站点设置错误可能为负值
*/
private fun invokeDistance(
carLocationInfo: Triple<Int, Boolean?, Float>,
location: MogoLocation,
locationInfo: String
) {
var lastSumLength: Float
val stationIndex = endStationInfo.index ?: 0
if (carLocationInfo.first < stationIndex - 1) {
// subList 是[) 需要的是[]
val subList = mRoutePoints!!.subList(carLocationInfo.first, stationIndex + 1)
// 轨迹点所有的距离
lastSumLength = CoordinateCalculateRouteUtil.calculateRouteSumLength(subList)
val stationDistance = endStationInfo.distance ?: 0f
if (endStationInfo.isNext == true) {// isNext = true
lastSumLength -= stationDistance
} else {// isNext = false
lastSumLength += stationDistance
}
if (carLocationInfo.second == true) {// 是否是下一个 true 下一个
lastSumLength += carLocationInfo.third
} else {
lastSumLength -= carLocationInfo.third
}
} else {
val lastPoints = endStationInfo.stationPoint
lastSumLength = CoordinateUtils.calculateLineDistance(
lastPoints!!.longitude, lastPoints.latitude,
location.longitude, location.latitude
)
}
d(M_OCHCOMMON + TAG, "距离终点:$lastSumLength")
if (lastSumLength > maxDistanceAllPoint) {
// 大于最大值需要需要删除此次计算
writeLog("距离终点:$lastSumLength", locationInfo)
return
}
if (distanceListeners.size > 0) {
distanceListeners.forEach {
//val tag = it.key
val listener = it.value
listener.distanceCallback(lastSumLength)
}
}
}
/**
* 计算站点之间的轨迹(从轨迹起点算起到轨迹结束点结束)
* routeArrivied 已经走过的轨迹点
* routeArriving 还没有走过的轨迹
* location 车的轨迹点
*/
private fun invokeTrajectory(
carLocationInfo: Triple<Int, Boolean?, Float>,
location: MogoLocation
) {
if (mRoutePoints.isNullOrEmpty()) return
val routeArrivied = mutableListOf<MogoLocation>()
val routeArriving = mutableListOf<MogoLocation>()
if (carLocationInfo.second == true) {// isNext = true
routeArrivied.addAll(mRoutePoints!!.subList(0, carLocationInfo.first))
routeArriving.addAll(mRoutePoints!!.subList(carLocationInfo.first, mRoutePoints!!.size))
} else {// isNext = false
val indexNext = carLocationInfo.first + 1
routeArrivied.addAll(mRoutePoints!!.subList(0, indexNext))
routeArriving.addAll(mRoutePoints!!.subList(indexNext, mRoutePoints!!.size))
}
if (trajectoryListeners.size > 0) {
trajectoryListeners.forEach {
//val tag = it.key
val listener = it.value
listener.trajectoryCallback(routeArrivied, routeArriving, location)
}
}
}
/**
* 计算站点之间的轨迹(从开始站点算起到结束站点)
* routeArrivied 已经走过的轨迹点
* routeArriving 还没有走过的轨迹
* location 车的轨迹点
*/
private fun invokeTrajectoryWithStation(
carLocationInfo: Triple<Int, Boolean?, Float>,
location: MogoLocation
) {
if (mRoutePoints.isNullOrEmpty()) return
val routeArrivied = mutableListOf<MogoLocation>()
val routeArriving = mutableListOf<MogoLocation>()
var fromCut = 0
var endCut = mRoutePoints!!.size
if (startStationInfo.stationPoint != null
&& startStationInfo.isNext != null
&& startStationInfo.index != null
&& startStationInfo.distance != null
) {
if (startStationInfo.isNext == true) {
fromCut = startStationInfo.index!!
} else {
fromCut = startStationInfo.index!! + 1
}
}
if (endStationInfo.stationPoint != null
&& endStationInfo.isNext != null
&& endStationInfo.index != null
&& endStationInfo.distance != null
) {
if (endStationInfo.isNext == true) {
endCut = endStationInfo.index!!
} else {
endCut = endStationInfo.index!! + 1
}
}
d(M_OCHCOMMON + TAG, "根据站点切个:第一个点:$fromCut 最后一个点$endCut")
if (carLocationInfo.second == true) {// isNext = true
routeArrivied.addAll(mRoutePoints!!.subList(fromCut, carLocationInfo.first))
routeArriving.addAll(mRoutePoints!!.subList(carLocationInfo.first, endCut))
} else {// isNext = false
val indexNext = carLocationInfo.first + 1
routeArrivied.addAll(mRoutePoints!!.subList(fromCut, indexNext))
routeArriving.addAll(mRoutePoints!!.subList(indexNext, endCut))
}
routeArrivied.add(0, startStationInfo.stationPoint!!)
routeArriving.add(endStationInfo.stationPoint!!)
if (trajectoryWithStationListeners.size > 0) {
trajectoryWithStationListeners.forEach {
//val tag = it.key
val listener = it.value
listener.trajectoryWithStationCallback(routeArrivied, routeArriving, location)
}
}
}
// @ChainLog(
// linkChainLog = ChainConstant.CHAIN_TYPE_OCH,
// linkCode = ChainConstant.CHAIN_SOURCE_OCH,
// nodeAliasCode = ChainConstant.CHAIN_CODE_OCH_COMMON_DISTANCE,
// paramIndexes = [0,1]
// )
private fun writeLog(carLocationInfo: String, location: String) {
d(M_OCHCOMMON + TAG, carLocationInfo)
d(M_OCHCOMMON + TAG, location)
}
/**
* 返回空为可启动自驾
* 返回其他不可启动自驾 返回为原因
*/
fun canStartAutopilot(lineId: Long?): String {
if (lineId == null) {
return "请确认线路ID"
}
try {
if (mRoutePoints.isNullOrEmpty()) {
// 判断距离起始站的距离
// 没有收到过轨迹
// 收到过轨迹 且底盘没有在自动驾驶中(没办法申请轨迹) 自驾中重启底盘的形式
val redCatche = TrajectoryCache.redCatche(lineId.toString())
return if (redCatche.isNullOrEmpty()) {
distanceWithStartStation()
} else {
val currentPoint =
CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()
distanceWithTrajectory(redCatche,currentPoint)
}
} else {
return if (this.lineId == 0L || this.lineId == null) {
val currentPoint =
CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()
distanceWithTrajectory(mRoutePoints!!,currentPoint)
} else {
if (lineId != this.lineId) {
// 判断距离起始站的距离
distanceWithStartStation()
} else {
val currentPoint =
CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()
distanceWithTrajectory(mRoutePoints!!,currentPoint)
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return ""
}
/**
* 距离站点的距离
*/
private fun distanceWithStartStation(): String {
if (startStationInfo.stationPoint == null) {
return "请选择起始结束站点"
}
val currentPoint = CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()
val distance = CoordinateUtils.calculateLineDistance(
currentPoint.longitude,
currentPoint.latitude,
startStationInfo.stationPoint!!.longitude,
startStationInfo.stationPoint!!.latitude
)
return if (distance <= OchCommonConst.AUTOMATIC_PLANNING_MAX_DISTANCE) {
""
} else {
"距离起始站点过远:${distance}"
}
}
/**
* 距离轨迹的距离
*/
fun distanceWithTrajectory(redCatche: MutableList<MogoLocation>,currentPoint:MogoLocation): String {
// 判断距离轨迹的距离
val carLocationInfo: Triple<Int, Boolean?, Float> =
CoordinateCalculateRouteUtil.getNearestPointInfo(
0,
redCatche.size - 1,
redCatche,
currentPoint,
2
)
if (carLocationInfo.third <= OchCommonConst.AUTOMATIC_PLANNING_MAX_DISTANCE) {
return ""// 可以启动自驾
} else {
// 判断距离线段的距离 垂足的距离
val nextPoint: MogoLocation
val prePoint: MogoLocation
// isNext true 最近的点是在下一个
// isNext false 最近的点是在上一个
if (carLocationInfo.second == true) {
if (carLocationInfo.first > 0) {
nextPoint = redCatche[carLocationInfo.first]
prePoint = redCatche[carLocationInfo.first - 1]
} else {
// 距离第一个点大于15m 过远
return "距离轨迹线超过15m${carLocationInfo.first}米,无法启动自驾"
}
} else {
if (carLocationInfo.first + 1 < redCatche.size) {
nextPoint = redCatche[carLocationInfo.first + 1]
prePoint = redCatche[carLocationInfo.first]
} else {
nextPoint = redCatche[carLocationInfo.first]
prePoint = redCatche[carLocationInfo.first - 1]
}
}
val pointToLine = LocationUtils.pointToLine(
prePoint.longitude,
prePoint.latitude,
nextPoint.longitude,
nextPoint.latitude,
currentPoint.longitude,
currentPoint.latitude
)
return if (pointToLine <= OchCommonConst.AUTOMATIC_PLANNING_MAX_DISTANCE) {
""
} else {
"距离轨迹线超过15m,无法启动自驾"
}
}
}
}

View File

@@ -0,0 +1,67 @@
package com.mogo.och.common.module.manager.distancemamager
import com.elegant.network.utils.GsonUtil
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_OCHCOMMON
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.IOException
object TrajectoryCache {
private const val TAG = "TrajectoryCache"
private val dir = "${AbsMogoApplication.getApp().cacheDir}/trajectory/"
fun writeLastTrajectory2jsonFile(latLngModels: MutableList<MogoLocation>,name:String) {
val jsonFromObject = GsonUtil.jsonFromObject(latLngModels)
try {
val path = File(dir)
if(!path.exists()){
path.mkdir()
}else {
path.deleteRecursively()
CallerLogger.d(M_OCHCOMMON + TAG,"删除缓存")
path.mkdir()
}
val writer = FileWriter("${path.path}/${name}")
writer.write(jsonFromObject)
writer.close()
CallerLogger.d(M_OCHCOMMON + TAG,"写入缓存")
} catch (e: IOException) {
e.printStackTrace()
}
}
fun deleteCatcheFile(){
try {
val path = File(dir)
if(path.exists()){
path.deleteRecursively()
CallerLogger.d(M_OCHCOMMON + TAG,"删除缓存")
}
} catch (e: IOException) {
e.printStackTrace()
}
}
fun redCatche(name: String): MutableList<MogoLocation>? {
try {
val path = File(dir)
if(!path.exists()){
return null
}
val fileReader = FileReader("${path.path}/${name}")
val readText = fileReader.readText()
fileReader.close()
return GsonUtil.arrayFromJson(readText, MogoLocation::class.java)
} catch (e: IOException) {
e.printStackTrace()
}
return null
}
}

View File

@@ -0,0 +1,21 @@
package com.mogo.och.common.module.manager.lightmanager
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLamplightListener
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLamplightListenerManager
object BreakLightManager : IMoGoChassisLamplightListener {
private const val TAG = "BreakLightManager"
init {
CallerChassisLamplightListenerManager.addListener(TAG, this)
}
// 刹车灯
override fun onAutopilotBrakeLightData(brakeLight: Boolean) {
super.onAutopilotBrakeLightData(brakeLight)
}
enum class BreakLightStatus{
BREAK_LIGHT,
BREAK_NONE,
}
}

View File

@@ -0,0 +1,66 @@
package com.mogo.och.common.module.manager.lightmanager
import chassis.Chassis
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLamplightListener
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLamplightListenerManager
import java.util.concurrent.ConcurrentHashMap
import kotlin.properties.Delegates
object TurnLightManager : IMoGoChassisLamplightListener {
private const val TAG = "TurnLightManager"
private val lightStatusChange: ConcurrentHashMap<String, TurnLightListener> =
ConcurrentHashMap()
private var turnLightStatus:TurnLightStatus by Delegates.observable(TurnLightStatus.TURN_LIGHT_NONE) { _, oldValue, newValue ->
if (oldValue != newValue) {
if(lightStatusChange.size>0){
lightStatusChange.forEach {
it.value.statusChange(newValue)
}
}
}
}
init {
CallerChassisLamplightListenerManager.addListener(TAG, this)
}
fun addTurnLightStatusChangeListener(tag: String, listener: TurnLightListener) {
if (lightStatusChange.containsKey(tag)) {
return
}
lightStatusChange[tag] = listener
listener.statusChange(turnLightStatus)
}
// 转向灯
override fun onAutopilotLightSwitchData(lightSwitch: Chassis.LightSwitch?) {
super.onAutopilotLightSwitchData(lightSwitch)
lightSwitch?.let {
when (it.number) {
Chassis.LightSwitch.LIGHT_LEFT_VALUE -> {
turnLightStatus = TurnLightStatus.TURN_LIGHT_LEFT
}
Chassis.LightSwitch.LIGHT_RIGHT_VALUE -> {
turnLightStatus = TurnLightStatus.TURN_LIGHT_RIGHT
}
Chassis.LightSwitch.LIGHT_NONE_VALUE -> {
turnLightStatus = TurnLightStatus.TURN_LIGHT_NONE
}
else -> {}
}
}
}
interface TurnLightListener{
fun statusChange(newStatus: TurnLightStatus)
}
enum class TurnLightStatus{
TURN_LIGHT_LEFT,
TURN_LIGHT_RIGHT,
TURN_LIGHT_NONE,
}
}

View File

@@ -0,0 +1,104 @@
package com.mogo.och.common.module.manager.loopmanager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_BUS_P
import com.mogo.och.common.module.utils.RxUtils
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
/**
* Created on 2021/11/22
*
* 管理轮询逻辑(订单轮询、新单轮询、新单抢单结果轮询等等)
*/
object BizLoopManager {
private val TAG = "BusPassengerModelLoopManager"
private val mControllerStatusCallbackMap = ConcurrentHashMap<String, LoopInfo>()
const val LOOP_LINE_2S = 2 * 1000L
const val LOOP_LINE_1S = 1 * 1000L
const val LOOP_DELAY = 100L
fun setLoopFunction(tag: String, function: LoopInfo) {
if (tag.isBlank()) return
if(mControllerStatusCallbackMap.contains(tag)){
return
}
mControllerStatusCallbackMap[tag] = function
if(function.immediately){
function.function.invoke()
}
if (mControllerStatusCallbackMap.size > 0) {
startLineLoop()
}
}
fun removeLoopFunction(tag: String) {
if (tag.isBlank()) return
mControllerStatusCallbackMap.remove(tag)
if (mControllerStatusCallbackMap.size == 0) {
stopLineLoop()
}
}
// 订单  轮训
// 订单时间轮训
// 车辆状态轮训
// 计算距离轮训
private var mQueryLineDisposable: Disposable? = null
private fun startLineLoop() {
if (mQueryLineDisposable != null && !mQueryLineDisposable!!.isDisposed) {
return
}
CallerLogger.i(M_BUS_P + TAG, "startQueryDriverLineLoop()")
mQueryLineDisposable = Observable.interval(LOOP_DELAY, LOOP_LINE_1S, TimeUnit.MILLISECONDS)
.map { aLong: Long -> aLong + 1 }
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe { aLong: Long? ->
mControllerStatusCallbackMap.forEach { (tag, loopInfo) ->
aLong?.let {
if (it % loopInfo.interval == 0L) {
try {
if(loopInfo.scheduler!=null) {
Observable.just(tag).observeOn(loopInfo.scheduler)
.subscribe(object :Observer<String>{
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {}
override fun onComplete() {
try {
loopInfo.function.invoke()
}catch (e:Throwable){
CallerLogger.e(TAG,"$tag:--$e")
}
CallerLogger.d(TAG, "${aLong}正在执行方法${tag}_${Thread.currentThread().name}")
}
override fun onNext(t: String) {}
})
}else{
loopInfo.function.invoke()
CallerLogger.d(TAG, "${aLong}正在执行方法${tag}_${Thread.currentThread().name}")
}
}catch (e:Throwable){
e.printStackTrace()
CallerLogger.e(TAG,"$tag:--$e")
}
}
}
}
}
}
private fun stopLineLoop() {
CallerLogger.i(M_BUS_P + TAG, "stopQueryDriverLineLoop()")
RxUtils.disposeSubscribe(mQueryLineDisposable)
}
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.common.module.manager.loopmanager
import io.reactivex.Scheduler
/**
* interval 轮训间隔
* function 轮询执行的方法
* immediately 是否立刻执行
* scheduler 轮训执行的线程
*/
data class LoopInfo(val interval:Long,val function: () -> Unit,val immediately:Boolean=false,var scheduler: Scheduler? = null)

View File

@@ -0,0 +1,52 @@
package com.mogo.och.common.module.manager.orderlogmanager
import android.text.TextUtils
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.debug.DebugConfig
import com.mogo.commons.utils.MogoAnalyticUtils
import com.mogo.eagle.core.data.app.AppConfigInfo
import com.mogo.eagle.core.data.deva.chain.ChainConstant
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.DateTimeUtils
import com.zhjt.service.chain.ChainLog
object OchChainLogManager {
private val TAG = OchChainLogManager::class.java.simpleName
const val EVENT_KEY_INFE_WITH_CHANGE = "event_key_och_common_info_and_changeinfo"
const val EVENT_KEY_INFE_WITH_TRAJECTORY = "event_key_och_trajectory_info"
/**
* @param Info 订单详细信息
* @param changeInfo 变化信息
*/
@ChainLog(
linkChainLog = ChainConstant.CHAIN_TYPE_OCH,
linkCode = ChainConstant.CHAIN_SOURCE_OCH,
nodeAliasCode = ChainConstant.CHAIN_CODE_OCH_COMMON_DISTANCE,
paramIndexes = [0,1]
)
@JvmStatic
fun writeChainLog(Info: String, changeInfo: String,upload:Boolean = true,eventID:String=EVENT_KEY_INFE_WITH_CHANGE) {
try {
d(SceneConstant.M_OCHCOMMON + TAG, Info)
d(SceneConstant.M_OCHCOMMON + TAG, changeInfo)
if(upload) {
val plateNum = AppConfigInfo.plateNumber
val params = HashMap<String, Any>()
params["sn"] = MoGoAiCloudClientConfig.getInstance().sn
params["env"] = DebugConfig.getNetMode()
params["plate_number"] = if (TextUtils.isEmpty(plateNum)) "" else plateNum
params["time"] = DateTimeUtils.getTimeText(DateTimeUtils.yyyy_MM_dd_HH_mm_ss)
params["info"] = Info
params["changeInfo"] = changeInfo
MogoAnalyticUtils.track(eventID, params)
}
}catch (e:Exception){
e.printStackTrace()
}
}
}

View File

@@ -0,0 +1,27 @@
package com.mogo.och.common.module.manager.orderlogmanager
import com.mogo.eagle.core.data.deva.chain.ChainConstant
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.zhjt.service.chain.ChainLog
object OrderChainLogManager {
private val TAG = OrderChainLogManager::class.java.simpleName
/**
* @param orderInfo 订单详细信息
* @param changeInfo 变化信息
*/
@ChainLog(
linkChainLog = ChainConstant.CHAIN_TYPE_OCH,
linkCode = ChainConstant.CHAIN_SOURCE_OCH,
nodeAliasCode = ChainConstant.CHAIN_CODE_OCH_COMMON_DISTANCE,
paramIndexes = [0,1]
)
fun writeChainLog(orderInfo: String, changeInfo: String) {
d(SceneConstant.M_OCHCOMMON + TAG, orderInfo)
d(SceneConstant.M_OCHCOMMON + TAG, changeInfo)
}
}

View File

@@ -0,0 +1,5 @@
package com.mogo.och.common.module.manager.stopsidemanager
interface OCHPlanningActionsCallback {
fun onStartAutopilotFailure(actionStatus: StopSideStatus, stopSideStatus : Boolean?, errorInfo : String?)
}

View File

@@ -0,0 +1,200 @@
package com.mogo.och.common.module.manager.stopsidemanager
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotPlanningActionsListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningActionsListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import mogo.telematics.pad.MessagePad
import java.util.concurrent.ConcurrentHashMap
object StopSideManager : IMoGoAutopilotPlanningActionsListener {
private const val TAG = "OCHPlanningActionsManager"
init {
CallerPlanningActionsListenerManager.addListener(TAG, this)
CallerPlanningActionsListenerManager.setListenerHz(TAG,5)
}
var stopSiteStatus = StopSideStatus.NOTHING
val M_LISTENERS: ConcurrentHashMap<String, OCHPlanningActionsCallback> = ConcurrentHashMap()
fun addListener(
tag: String,
listener: OCHPlanningActionsCallback
) {
if (M_LISTENERS.containsKey(tag)) {
return
}
M_LISTENERS[tag] = listener
}
fun removeListener(tag: String) {
if (!M_LISTENERS.containsKey(tag)) {
return
}
M_LISTENERS.remove(tag)
}
/**
* 删除监听
* @param listener 要删除的监听对象
*/
fun removeListener(listener: OCHPlanningActionsCallback) {
if (!M_LISTENERS.containsValue(listener)) {
return
}
M_LISTENERS.forEach {
if (it.value == listener) {
M_LISTENERS.remove(it.key)
}
}
}
fun stopSide(){
// 靠边停车
CallerAutoPilotControlManager.sendPlanningCmd(1)
}
fun resetStopSide(){
// 重新起步
CallerAutoPilotControlManager.sendPlanningCmd(2)
}
override fun pncActions(planningActionMsg: MessagePad.PlanningActionMsg) {
val drivingState = planningActionMsg.parkScenarioAction.actionMsg.drivingState
val drivingAction = planningActionMsg.parkScenarioAction.actionMsg.drivingAction
when (drivingState) {
MessagePad.ParkScenarioDrivingState.PARK_SENARIO_FORCE_PULL_OVER_ON -> {
when (drivingAction) {
MessagePad.DrivingAction.DRIVING_ACTION_STATE_ONE -> {
// 表示开始靠边停车
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
if(stopSiteStatus == StopSideStatus.START){
// 靠边停车中
CallerLogger.d(SceneConstant.M_BUS+ TAG,"靠边停车中")
listener.onStartAutopilotFailure(StopSideStatus.DOING,null,null)
}else{
// 开始靠边停车
stopSiteStatus = StopSideStatus.START
CallerLogger.d(SceneConstant.M_BUS+ TAG,"开始靠边停车")
listener.onStartAutopilotFailure(StopSideStatus.START,null,null)
}
}
}
MessagePad.DrivingAction.DRIVING_ACTION_STATE_TWO -> {
//表示靠边停车成功
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
if(stopSiteStatus == StopSideStatus.EndingSuccess){
// 只响应第一次
}else{
stopSiteStatus = StopSideStatus.EndingSuccess
CallerLogger.d(SceneConstant.M_BUS+ TAG,"靠边停车成功")
listener.onStartAutopilotFailure(StopSideStatus.EndingSuccess,true,null)
}
}
}
MessagePad.DrivingAction.DRIVING_ACTION_STATE_THREE -> {
//靠边停车失败
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
if(stopSiteStatus == StopSideStatus.EndingFaile){
// 只响应第一次
}else{
stopSiteStatus = StopSideStatus.EndingFaile
CallerLogger.d(SceneConstant.M_BUS+ TAG,"靠边停车失败")
listener.onStartAutopilotFailure(StopSideStatus.EndingFaile,false,null)
}
}
}
else -> {}
}
}
MessagePad.ParkScenarioDrivingState.PARK_SENARIO_FORCE_PULL_OVER_OFF -> {
when (drivingAction) {
MessagePad.DrivingAction.DRIVING_ACTION_STATE_ONE -> {
// 表示距离前方站点100m
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
if(stopSiteStatus == StopSideStatus.NOSTART){
// 只响应第一次
}else{
stopSiteStatus = StopSideStatus.NOSTART
CallerLogger.d(SceneConstant.M_BUS+ TAG,"靠边停车失败_原因距离前方站点100m")
listener.onStartAutopilotFailure(StopSideStatus.NOSTART,false,"距离前方站点100m,请稍后再试")
}
}
}
MessagePad.DrivingAction.DRIVING_ACTION_STATE_TWO -> {
//表示距离路口100m
CallerLogger.d(SceneConstant.M_BUS+ TAG,"靠边停车失败_原因距离路口100m")
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
if(stopSiteStatus == StopSideStatus.NOSTART){
// 只响应第一次
}else{
stopSiteStatus = StopSideStatus.NOSTART
CallerLogger.d(SceneConstant.M_BUS+ TAG,"靠边停车失败_原因距离路口100m,请稍后再试")
listener.onStartAutopilotFailure(StopSideStatus.NOSTART,false,"距离路口100m,请稍后再试")
}
}
}
MessagePad.DrivingAction.DRIVING_ACTION_STATE_THREE -> {
//正在变道
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
if(stopSiteStatus == StopSideStatus.NOSTART){
// 只响应第一次
}else{
stopSiteStatus = StopSideStatus.NOSTART
CallerLogger.d(SceneConstant.M_BUS+ TAG,"靠边停车失败_原因正在变道")
listener.onStartAutopilotFailure(StopSideStatus.NOSTART,false,"因车辆正在变道无法靠边停车,请稍后再试")
}
}
}
else -> {
//未知问题
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
if(stopSiteStatus == StopSideStatus.NOSTART){
// 只响应第一次
}else{
stopSiteStatus = StopSideStatus.NOSTART
CallerLogger.d(SceneConstant.M_BUS+ TAG,"靠边停车失败_原因未知问题")
listener.onStartAutopilotFailure(StopSideStatus.NOSTART,false,"靠边停车失败,请稍后再试")
}
}
}
}
}
MessagePad.ParkScenarioDrivingState.PARK_SENARIO_LANKE_KEEP ->{
if(stopSiteStatus == StopSideStatus.NOTHING){
// 只响应第一次
}else{
stopSiteStatus = StopSideStatus.NOTHING
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
CallerLogger.d(SceneConstant.M_BUS+ TAG,"进入正常驾驶")
listener.onStartAutopilotFailure(StopSideStatus.NOTHING,false,"进入正常行驶中")
}
}
}
else -> {}
}
}
}

View File

@@ -0,0 +1,10 @@
package com.mogo.och.common.module.manager.stopsidemanager
enum class StopSideStatus {
START(),// 开始靠边停车
DOING(),// 正在靠边停车
EndingSuccess(),// 靠边停车成功
EndingFaile(),// 靠边停车失败
NOSTART(),// 没有响应靠边停车
NOTHING()// 默认状态
}

View File

@@ -0,0 +1,370 @@
package com.mogo.och.common.module.map;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI;
import android.Manifest;
import android.content.Context;
import android.widget.Toast;
import com.amap.api.navi.AMapNavi;
import com.amap.api.navi.AMapNaviListener;
import com.amap.api.navi.enums.NaviType;
import com.amap.api.navi.model.AMapCalcRouteResult;
import com.amap.api.navi.model.AMapLaneInfo;
import com.amap.api.navi.model.AMapModelCross;
import com.amap.api.navi.model.AMapNaviCameraInfo;
import com.amap.api.navi.model.AMapNaviCross;
import com.amap.api.navi.model.AMapNaviLocation;
import com.amap.api.navi.model.AMapNaviRouteNotifyData;
import com.amap.api.navi.model.AMapNaviTrafficFacilityInfo;
import com.amap.api.navi.model.AMapServiceAreaInfo;
import com.amap.api.navi.model.AimLessModeCongestionInfo;
import com.amap.api.navi.model.AimLessModeStat;
import com.amap.api.navi.model.NaviInfo;
import com.amap.api.navi.model.NaviLatLng;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.NetworkUtils;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
import com.mogo.och.common.module.utils.PermissionUtil;
import com.mogo.och.common.module.wigets.toast.ToastCharterUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author: wangmingjun
* @date: 2021/12/6
*/
public class AmapNaviToDestinationModel implements AMapNaviListener {
public static final String TAG = "NaviToDestinationModel";
private AMapNavi mAMapNavi = null;
protected final List<NaviLatLng> sList = new ArrayList<NaviLatLng>();
protected final List<NaviLatLng> eList = new ArrayList<NaviLatLng>();
protected List<NaviLatLng> mWayPointList = new ArrayList<NaviLatLng>();
private volatile ICommonNaviChangedCallback mNaviChangedCallback;
private final AtomicInteger errorCount = new AtomicInteger(0);
private boolean isPlay;
public static AmapNaviToDestinationModel getInstance(Context context) {
return SingletonHolder.INSTANCE;
}
private static final class SingletonHolder {
private static final AmapNaviToDestinationModel INSTANCE = new AmapNaviToDestinationModel();
}
private AmapNaviToDestinationModel() {
}
public void initAMapNavi(NaviLatLng startLatLng, NaviLatLng endLatLng) {
ThreadUtils.getSinglePool().execute(() -> {
try {
CallerLogger.i(TAG, "initAMapNavi()");
mAMapNavi = AMapNavi.getInstance(AbsMogoApplication.getApp());
mAMapNavi.addAMapNaviListener(this);
mAMapNavi.setUseInnerVoice(true, true);
sList.add(startLatLng);
eList.add(endLatLng);
} catch (Exception e) {
e.printStackTrace();
}
});
}
public void setVoiceIsMute(boolean isPlay) {
ThreadUtils.getSinglePool().execute(() -> {
if (mAMapNavi == null) return;
CallerLogger.i(TAG, "setVoiceIsMute()--" + isPlay);
this.isPlay = isPlay;
if (isPlay) {
mAMapNavi.startSpeak();
} else {
mAMapNavi.stopSpeak();
}
});
}
public AMapNavi getmAMapNavi() {
return mAMapNavi;
}
public void destroyAmaNavi() {
ThreadUtils.getSinglePool().execute(() -> {
CallerLogger.i(TAG, "destroyAmaNavi()");
if (mAMapNavi != null) {
isPlay = false;
sList.clear();
eList.clear();
mAMapNavi.stopNavi();
mAMapNavi.destroy();
mAMapNavi = null;
mNaviChangedCallback = null;
}
});
}
public void setTaxiNaviChangedCallback(ICommonNaviChangedCallback callback) {
CallerLogger.i(TAG, "setTaxiNaviChangedCallback()");
this.mNaviChangedCallback = callback;
}
@Override
public void onInitNaviFailure() {
Toast.makeText(AbsMogoApplication.getApp(), "init navi Failed", Toast.LENGTH_SHORT).show();
}
@Override
public void onInitNaviSuccess() {
//初始化成功
/**
* 方法: int strategy=mAMapNavi.strategyConvert(congestion, avoidhightspeed, cost, hightspeed, multipleroute); 参数:
*
* @congestion 躲避拥堵
* @avoidhightspeed 不走高速
* @cost 避免收费
* @hightspeed 高速优先
* @multipleroute 多路径
*
* 说明: 以上参数都是boolean类型其中multipleroute参数表示是否多条路线如果为true则此策略会算出多条路线。
* 注意: 不走高速与高速优先不能同时为true 高速优先与避免收费不能同时为true
*/
ThreadUtils.getSinglePool().execute(() -> {
int strategy = 0;
try {
//再次强调最后一个参数为true时代表多路径否则代表单路径
strategy = mAMapNavi.strategyConvert(true, false, false, false, false);
} catch (Exception e) {
e.printStackTrace();
}
mAMapNavi.calculateDriveRoute(sList, eList, mWayPointList, strategy);
});
}
@Override
public void onCalculateRouteSuccess(int[] ints) {
//多路径算路成功回调
ThreadUtils.getSinglePool().execute(() -> mAMapNavi.startNavi(NaviType.GPS));
}
@Override
public void onNaviInfoUpdate(NaviInfo naviinfo) {
//导航过程中的信息更新请看NaviInfo的具体说明
CallerLogger.i(TAG, "距离=" + naviinfo.getPathRetainDistance() + ", 剩余时间 " + naviinfo.getPathRetainTime());
if (null != mNaviChangedCallback) {
mNaviChangedCallback.onCurrentNaviDistAndTimeChanged(naviinfo.getPathRetainDistance(), naviinfo.getPathRetainTime());// 米、秒
}
}
@Override
public void onCalculateRouteSuccess(AMapCalcRouteResult aMapCalcRouteResult) {
errorCount.set(0);
}
@Override
public void onCalculateRouteFailure(AMapCalcRouteResult result) {
//路线计算失败
//多路径算路成功回调
if (errorCount.get() < 5) {
errorCount.getAndIncrement();
if (mNaviChangedCallback != null) {
mNaviChangedCallback.reInitNaviAmap(isPlay, true);
}
}
if (!NetworkUtils.isConnected(AbsMogoApplication.getApp()) || result.getErrorCode() == 2) {
ToastCharterUtils.showToastShort("网络异常,请重试");
if (mNaviChangedCallback != null) {
mNaviChangedCallback.reInitNaviAmap(isPlay, false);
}
return;
}
if (!PermissionUtil.isLocServiceEnable(AbsMogoApplication.getApp()) || !PermissionUtil.checkPermission(AbsMogoApplication.getApp(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) {
ToastCharterUtils.showToastShort("请开启车机定位后重试");
if (mNaviChangedCallback != null) {
mNaviChangedCallback.reInitNaviAmap(isPlay, false);
}
return;
}
CallerLogger.i(M_TAXI + TAG, "路线计算失败:错误码=" + result.getErrorCode() + ",Error Message= " + result.getErrorDetail());
CallerLogger.i(M_TAXI + TAG, "错误码详细链接见http://lbs.amap.com/api/android-navi-sdk/guide/tools/errorcode/");
}
@Override
public void onStartNavi(int type) {
//开始导航回调
}
@Override
public void onTrafficStatusUpdate() {
//
}
@Override
public void onLocationChange(AMapNaviLocation location) {
//当前位置回调
// CallerLogger.i( TAG, "AMapNaviLocation = NaviLatLng = "+
// location.getCoord().getLatitude()+ ", "+ location.getCoord().getLongitude()
// + ", bearing = " +location.getBearing()
// +", time = " + DateTimeUtil.formatLongToString(location.getTime(),DateTimeUtil.HH_mm)
// + ", locationType = "+ location.getLocationType());
}
@Override
public void onGetNavigationText(int type, String text) {
//播报类型和播报文字回调
}
@Override
public void onGetNavigationText(String s) {
}
@Override
public void onEndEmulatorNavi() {
//结束模拟导航
}
@Override
public void onArriveDestination() {
//到达目的地
}
@Override
public void onCalculateRouteFailure(int errorInfo) {
}
@Override
public void onReCalculateRouteForYaw() {
//偏航后重新计算路线回调
}
@Override
public void onReCalculateRouteForTrafficJam() {
//拥堵后重新计算路线回调
}
@Override
public void onArrivedWayPoint(int wayID) {
//到达途径点
}
@Override
public void onGpsOpenStatus(boolean enabled) {
//GPS开关状态回调
}
@Override
public void showModeCross(AMapModelCross aMapModelCross) {
}
@Override
public void hideModeCross() {
}
@Override
public void updateIntervalCameraInfo(AMapNaviCameraInfo aMapNaviCameraInfo, AMapNaviCameraInfo aMapNaviCameraInfo1, int i) {
}
@Override
public void showLaneInfo(AMapLaneInfo aMapLaneInfo) {
//显示车道信息
// StringBuffer sb = new StringBuffer();
// sb.append("共" + aMapLaneInfo.frontLane.length + "车道");
// for (int i = 0; i < aMapLaneInfo.frontLane.length; i++) {
// //当前车道可以选择的动作
// int background = aMapLaneInfo.backgroundLane[i];
// //当前用户要执行的动作
// int recommend = aMapLaneInfo.frontLane[i];
//
// CallerLogger.e(M_TAXI + "ggb", "---->>> background is " + background + " ; recommend is " + recommend);
// //根据文档中每个动作对应的枚举类型,显示对应的图片
// try {
// sb.append(",第" + (i + 1) + "车道为" + array[background]);
// if (recommend != 255) {
// sb.append(",当前车道可 " + actions[recommend]);
// }
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// CallerLogger.e(M_TAXI + "showLaneInfo", sb.toString());
}
@Override
public void onNaviRouteNotify(AMapNaviRouteNotifyData aMapNaviRouteNotifyData) {
}
@Override
public void onGpsSignalWeak(boolean b) {
}
@Override
public void updateCameraInfo(AMapNaviCameraInfo[] aMapCameraInfos) {
}
@Override
public void onServiceAreaUpdate(AMapServiceAreaInfo[] amapServiceAreaInfos) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo aMapNaviTrafficFacilityInfo) {
//已过时
}
@Override
public void showCross(AMapNaviCross aMapNaviCross) {
//显示放大图回调
}
@Override
public void hideCross() {
//隐藏放大图回调
}
@Override
public void showLaneInfo(AMapLaneInfo[] laneInfos, byte[] laneBackgroundInfo, byte[] laneRecommendedInfo) {
//过时
}
@Override
public void hideLaneInfo() {
//隐藏车道信息
}
@Override
public void notifyParallelRoad(int i) {
}
@Override
public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo[] aMapNaviTrafficFacilityInfos) {
//更新交通设施信息
}
@Override
public void updateAimlessModeStatistics(AimLessModeStat aimLessModeStat) {
//更新巡航模式的统计信息
}
@Override
public void updateAimlessModeCongestionInfo(AimLessModeCongestionInfo aimLessModeCongestionInfo) {
//更新巡航模式的拥堵信息
}
@Override
public void onPlayRing(int i) {
}
}

View File

@@ -0,0 +1,190 @@
package com.mogo.och.common.module.map
import android.content.Context
import android.graphics.BitmapFactory
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.RelativeLayout
import com.amap.api.maps.AMap
import com.amap.api.maps.model.CustomMapStyleOptions
import com.amap.api.navi.AMapNaviView
import com.amap.api.navi.AMapNaviViewListener
import com.amap.api.navi.AMapNaviViewOptions
import com.amap.api.navi.model.RouteOverlayOptions
import com.mogo.eagle.core.utilcode.mogo.MapAssetStyleUtils
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.och.common.module.R
import kotlinx.android.synthetic.main.taxi_common_amap_navi_view.view.*
import kotlin.concurrent.thread
/**
* @author: wangmingjun
* @date: 2022/10/23
*/
class CommonAmapNaviVIew @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0)
: RelativeLayout(context,attrs,defStyleAttr), AMapNaviViewListener {
private val TAG : String = "TaxiAmapNaviVIew"
var aMap : AMap? = null
init {
LayoutInflater.from(context).inflate(R.layout.taxi_common_amap_navi_view,this,true)
initMapView()
}
private fun initMapView(){
amapNaviView.setAMapNaviViewListener(this)
//车头向上模式
amapNaviView.naviMode = AMapNaviView.CAR_UP_MODE
aMap = amapNaviView.map
//关闭地图文字显示
aMap!!.showMapText(false)
// 设置导航地图模式aMap是地图控制器对象。
aMap!!.mapType = AMap.MAP_TYPE_NIGHT
// 关闭显示实时路况图层aMap是地图控制器对象。
aMap!!.isTrafficEnabled = false
setMapViewOptions()
thread(start = true){
try {
Thread.sleep(1000L)
} catch (e : InterruptedException) {
e.printStackTrace()
}
if (null == context){
return@thread
}
aMap!!.setCustomMapStyle(
CustomMapStyleOptions()
.setEnable(true)
.setStyleData(MapAssetStyleUtils.getAssetsStyle(context, "over_view_style.data"))
.setStyleExtraData(MapAssetStyleUtils.getAssetsExtraStyle(context, "over_view_style_extra.data"))
)
}
}
private fun setMapViewOptions() {
val options = AMapNaviViewOptions()
options.tilt = 0 //设置 2D 模式
options.isLayoutVisible = false //设置导航界面UI是否显示。
options.isTrafficBarEnabled = false //设置路况光柱条是否显示(只适用于驾车导航,需要联网)。
options.isAutoLockCar = true //设置6秒后是否自动锁车
options.isAutoDisplayOverview = true //设置是否自动全览模式,即在算路成功后自动进入全览模式
options.isTrafficLine = false
options.setModeCrossDisplayShow(true) //设置是否显示路口放大图(路口模型图)
options.isAutoChangeZoom = true //自动缩放级别
options.carBitmap =
BitmapFactory.decodeResource(context.resources, R.drawable.taxi_navi_arrow_icon)
options.fourCornersBitmap =
BitmapFactory.decodeResource(this.resources, R.drawable.taxi_navi_direction_icon)
options.isAutoChangeZoom = true //设置是否开启动态比例尺 (锁车态下自动进行地图缩放变化)
// options.setZoom(18);//14-18
// options.setTrafficInfoUpdateEnabled(false);
// options.setTrafficLayerEnabled(false);//设置[实时交通图层开关按钮]是否显示(只适用于驾车导航,需要联网)。
// options.setCameraInfoUpdateEnabled(false);
// options.setCompassEnabled(false);//设置指南针图标否在导航界面显示,默认显示。
// options.setLaneInfoShow(false);// 设置是否显示道路信息view
// options.setNaviArrowVisible(false);//设置路线转向箭头隐藏和显示
// options.setRealCrossDisplayShow(false);//设置是否显示路口放大图(实景图)
// options.setRouteListButtonShow(true);//设置导航界面是否显示路线全览按钮。
// options.setSettingMenuEnabled(false);//设置菜单按钮是否在导航界面显示。
// options.setZoom(18);//14-18
// options.setTrafficInfoUpdateEnabled(false);
// options.setTrafficLayerEnabled(false);//设置[实时交通图层开关按钮]是否显示(只适用于驾车导航,需要联网)。
// options.setCameraInfoUpdateEnabled(false);
// options.setCompassEnabled(false);//设置指南针图标否在导航界面显示,默认显示。
// options.setLaneInfoShow(false);// 设置是否显示道路信息view
// options.setNaviArrowVisible(false);//设置路线转向箭头隐藏和显示
// options.setRealCrossDisplayShow(false);//设置是否显示路口放大图(实景图)
// options.setRouteListButtonShow(true);//设置导航界面是否显示路线全览按钮。
// options.setSettingMenuEnabled(false);//设置菜单按钮是否在导航界面显示。
val routeOverlayOptions = RouteOverlayOptions()
// routeOverlayOptions.setArrowOnTrafficRoute(BitmapFactory.decodeResource(getResources(),R.drawable.custtexture_aolr));
// routeOverlayOptions.setArrowOnTrafficRoute(BitmapFactory.decodeResource(getResources(),R.drawable.custtexture_aolr));
routeOverlayOptions.normalRoute =
BitmapFactory.decodeResource(resources, R.drawable.taxi_navi_line_icon)
options.routeOverlayOptions = routeOverlayOptions
amapNaviView.viewOptions = options
}
override fun onNaviSetting() {
//底部导航设置点击回调
}
override fun onNaviCancel() {
}
override fun onNaviBackClick(): Boolean {
return false
}
override fun onNaviMapMode(p0: Int) {
//导航态车头模式0:车头朝上状态1:正北朝上模式。
}
override fun onNaviTurnClick() {
//转弯view的点击回调
}
override fun onNextRoadClick() {
//下一个道路View点击回调
}
override fun onScanViewButtonClick() {
//全览按钮点击回调
}
override fun onLockMap(p0: Boolean) {
//锁地图状态发生变化时回调
}
override fun onNaviViewLoaded() {
d(SceneConstant.M_TAXI + TAG, "导航页面加载成功")
}
override fun onMapTypeChanged(p0: Int) {
}
override fun onNaviViewShowMode(p0: Int) {
}
fun onCreate(savedInstanceState : Bundle?){
amapNaviView.onCreate(savedInstanceState)
}
fun onResume(){
amapNaviView.onResume()
}
fun onPause(){
amapNaviView.onPause()
}
fun onDestroy(){
amapNaviView.onDestroy()
}
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.common.module.map;
/**
* @author: wangmingjun
* @date: 2021/12/3
*/
public interface ICommonNaviChangedCallback {
// 当前位置距离上车点的距离(米)、预估时间(秒)
void onCurrentNaviDistAndTimeChanged(int meters, long timeInSecond);
void reInitNaviAmap(boolean isPlay,boolean isRestart);
}

View File

@@ -0,0 +1,287 @@
package com.mogo.och.common.module.utils;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author: wangmingjun
* @date: 2022/7/20
*/
public class AnimatorDrawableUtil {
private static final int MSG_START = 0xf1;
private static final int MSG_STOP = 0xf2;
private static final int STATE_STOP = 0xf3;
private static final int STATE_RUNNING = 0xf4;
//运行状态
private int mState = STATE_RUNNING;
//显示图片的View
private ImageView mImageView = null;
//图片资源的ID列表
private List<Integer> mResourceIdList = null;
//图片bitmap列表
private List<Bitmap> mBitmapList = null;
//定时任务器
private final Timer mTimer = new Timer();
//定时任务
private AnimTimerTask mTimeTask = null;
//记录播放位置
private int mFrameIndex = 0;
//播放形式
private boolean isLooping = false;
public AnimatorDrawableUtil() {
// mTimer = new Timer();
}
/**
* 设置动画播放资源
*/
public void setAnimation(ImageView imageview, List<Integer> resourceIdList) {
mImageView = imageview;
if(mResourceIdList==null){
mResourceIdList = new ArrayList<Integer>();
mBitmapList = new ArrayList<>();
}else{
mResourceIdList.clear();
mBitmapList.clear();
}
mResourceIdList.addAll(resourceIdList);
//在初始化时候就将资源文件decode
for (int i = 0; i <= resourceIdList.size()-1; i++){
mBitmapList.add(readBitMap(mImageView.getContext(),resourceIdList.get(i)));
}
}
/**
* 设置动画播放资源
*/
public void setAnimation(Context context, int resourceId, ImageView imageview) {
this.mImageView = imageview;
if(mResourceIdList==null){
mResourceIdList = new ArrayList<Integer>();
mBitmapList = new ArrayList<>();
}else{
mResourceIdList.clear();
mBitmapList.clear();
}
loadFromXml(context, resourceId, new OnParseListener() {
@Override
public void onParse(List<Integer> res) {
mResourceIdList.addAll(res);
for (int i = 0; i <= res.size()-1; i++){
mBitmapList.add(readBitMap(mImageView.getContext(),res.get(i)));
}
}
});
}
/**
* 解析xml
*
* @param context
* @param resourceId 资源id
*/
private void loadFromXml(final Context context, final int resourceId,
final OnParseListener onParseListener) {
if (context == null) {
return;
}
final List<Integer> res = new ArrayList<Integer>();
XmlResourceParser parser = context.getResources().getXml(resourceId);
try {
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_DOCUMENT) {
} else if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("item")) {
for (int i = 0; i < parser.getAttributeCount(); i++) {
if (parser.getAttributeName(i).equals("drawable")) {
int resId = Integer.parseInt(parser.getAttributeValue(i).substring(1));
res.add(resId);
}
}
}
} else if (eventType == XmlPullParser.END_TAG) {
} else if (eventType == XmlPullParser.TEXT) {
}
eventType = parser.next();
}
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
} catch (XmlPullParserException e2) {
// TODO: handle exception
e2.printStackTrace();
} finally {
parser.close();
}
if (onParseListener != null) {
onParseListener.onParse(res);
}
}
private AnimationLisenter lisenter;
/**
* 开始播放动画
*
* @param loop 是否循环播放
* @param duration 动画播放时间间隔
*/
public void start(boolean loop, int duration, AnimationLisenter lisenter) {
this.lisenter = lisenter;
// stop();
if (mResourceIdList == null || mResourceIdList.size() == 0) {
return;
}
// if (mTimer == null) {
// mTimer = new Timer();
// }
try {
isLooping = loop;
mFrameIndex = 0;
mState = STATE_RUNNING;
if (mTimeTask != null){
return;
}
mTimeTask = new AnimTimerTask();
mTimer.schedule(mTimeTask, 0, duration);
}catch (Exception e){
e.printStackTrace();
}
if (lisenter != null){
lisenter.startAnimation();
}
}
/**
* 停止动画播放
*/
public void stop() {
if (mTimer != null) {
mTimer.purge();
mTimer.cancel();
// mTimer = null;
}
if (mTimeTask != null) {
mFrameIndex = 0;
mState = STATE_STOP;
mTimeTask.cancel();
mTimeTask = null;
}
//移除Handler消息
if (AnimHandler != null) {
AnimHandler.removeMessages(MSG_START);
AnimHandler.removeMessages(MSG_STOP);
AnimHandler.removeCallbacksAndMessages(null);
}
}
/**
* 定时器任务
*/
class AnimTimerTask extends TimerTask {
@Override
public void run() {
if (mFrameIndex < 0 || mState == STATE_STOP) {
return;
}
if (mFrameIndex < mResourceIdList.size()) {
Message msg = AnimHandler.obtainMessage(MSG_START, 0, 0, null);
msg.sendToTarget();
} else {
mFrameIndex = 0;
if (!isLooping) {
Message msg = AnimHandler.obtainMessage(MSG_STOP, 0, 0, null);
msg.sendToTarget();
}
}
}
}
/**
* Handler
*/
private Handler AnimHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START: {
if (mFrameIndex >= 0 && mFrameIndex < mResourceIdList.size() && mState == STATE_RUNNING) {
//这里不能使用image.setImageResource 因为源码中也是创建了bitmap 所以这里我们自己创建
if (mBitmapList != null && mBitmapList.size()-1 >= mFrameIndex){
Bitmap bitmap= mBitmapList.get(mFrameIndex);
mImageView.setImageBitmap(bitmap);
}else {
Bitmap bitmap=readBitMap(mImageView.getContext(),mResourceIdList.get(mFrameIndex));
mImageView.setImageBitmap(bitmap);
}
mFrameIndex++;
}
}
break;
case MSG_STOP: {
if (mTimeTask != null) {
mFrameIndex = 0;
mTimer.purge();
mTimeTask.cancel();
mState = STATE_STOP;
mTimeTask = null;
if (lisenter !=null){
lisenter.endAnimation();
}
if (isLooping) {
mImageView.setImageResource(0);
}
}
}
break;
default:
break;
}
}
};
public static Bitmap readBitMap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
public interface OnParseListener {
void onParse(List<Integer> res);
}
public interface AnimationLisenter {
void startAnimation();
void endAnimation();
}
}

View File

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

View File

@@ -0,0 +1,45 @@
package com.mogo.och.common.module.utils;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.widget.ImageView;
/**
* @author: wangmingjun
* @date: 2022/9/23
*/
public class BlinkAnimationUtil {
//实现图片闪烁效果
public static void setAnimation(ImageView imageView) {
Object animObject = imageView.getTag(imageView.getId());
if (animObject instanceof AnimatorSet){
AnimatorSet animatorSet = (AnimatorSet)animObject;
animatorSet.start();
return;
}
AnimatorSet animationSet = new AnimatorSet();
imageView.setTag(imageView.getId(),animationSet);
ObjectAnimator valueAnimator = ObjectAnimator.ofFloat(imageView, "alpha",0.3f, 1.0f);
ObjectAnimator valueAnimatorDisappare = ObjectAnimator.ofFloat(imageView, "alpha",1.0f, 0.3f);
valueAnimator.setDuration(1000);
valueAnimatorDisappare.setDuration(800);
valueAnimator.setRepeatCount(-1);
valueAnimatorDisappare.setRepeatCount(-1);
animationSet.playTogether(valueAnimatorDisappare, valueAnimator);
animationSet.start();
}
//消除动画
public static void clearAnimation(ImageView imageView){
Object animObject = imageView.getTag(imageView.getId());
if (animObject instanceof AnimatorSet){
AnimatorSet animatorSet = (AnimatorSet)animObject;
animatorSet.end();
}
}
}

View File

@@ -0,0 +1,40 @@
package com.mogo.och.common.module.utils
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_BUS
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_BUS_P
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_CHARTER_D
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_SWEEPER
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_TAXI
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_TAXI_P
object CallerLoggerUtils {
var flavorTag =if (AppIdentityModeUtils.isTaxi(FunctionBuildConfig.appIdentityMode)&&
AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)){
//出租车司机
M_TAXI
}else if (AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode) &&
AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)){
//小巴车司机
M_BUS
}else if (AppIdentityModeUtils.isTaxi(FunctionBuildConfig.appIdentityMode)&&
AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)){
//出租车乘客
M_TAXI_P
}else if (AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode)&&
AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode)){
//小巴车乘客
M_BUS_P
}else if (AppIdentityModeUtils.isSweeper(FunctionBuildConfig.appIdentityMode)){
//清扫车
M_SWEEPER
}else if (AppIdentityModeUtils.isCharter(FunctionBuildConfig.appIdentityMode) &&
AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)){
//M1司机端
M_CHARTER_D
}
else{
""
}
}

View File

@@ -0,0 +1,856 @@
package com.mogo.och.common.module.utils
import android.content.Context
import android.location.Location
import com.amap.api.maps.CoordinateConverter
import com.amap.api.maps.model.LatLng
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.CoordinateUtils
import com.mogo.eagle.core.utilcode.util.DrivingDirectionUtils
import com.mogo.och.common.module.manager.distancemamager.DistanceDegree
import mogo.telematics.pad.MessagePad
import java.util.TreeMap
import kotlin.math.acos
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
/**
* @author: wangmingjun
* @date: 2022/3/28
*/
object CoordinateCalculateRouteUtil {
@JvmStatic
fun <T> calculateRouteSumLength(points: List<T>?): Float {
if (null == points || points.size == 0) return 0f
var sumLength = 0f
if (points[0] is MogoLocation) {
//计算全路径总距离
var i = 0
while (i + 1 < points.size) {
val locationPre = points[i] as MogoLocation
val location = points[i + 1] as MogoLocation
val preLat = locationPre.latitude
val preLon = locationPre.longitude
val laLat = location.latitude
val laLon = location.longitude
val length = CoordinateUtils.calculateLineDistance(laLon, laLat, preLon, preLat)
sumLength += length
i++
}
} else if (points[0] is Location) {
//计算全路径总距离
var i = 0
while (i + 1 < points.size) {
val locationPre = points[i] as Location
val location = points[i + 1] as Location
val preLat = locationPre.latitude
val preLon = locationPre.longitude
val laLat = location.latitude
val laLon = location.longitude
val length = CoordinateUtils.calculateLineDistance(laLon, laLat, preLon, preLat)
sumLength += length
i++
}
} else if (points[0] is LatLng) {
var i = 0
while (i + 1 < points.size) {
val locationPre = points[i] as LatLng
val location = points[i + 1] as LatLng
val preLat = locationPre.latitude
val preLon = locationPre.longitude
val laLat = location.latitude
val laLon = location.longitude
val length = CoordinateUtils.calculateLineDistance(laLon, laLat, preLon, preLat)
sumLength += length
i++
}
}
return sumLength
}
fun calculateRouteSumLength(
mRoutePoints: List<MogoLocation>?,
location: MogoLocation,
station: MogoLocation
): Float {
if (null == mRoutePoints || mRoutePoints.size == 0) return 0f
var lastSumLength = 0f
//当前位置距离轨迹中最近的点
val currentRouteIndex = getArrivedPointIndexNew(
0, mRoutePoints, location.longitude, location.latitude
)
// 距离当前位置轨迹中最近的轨迹点坐标
val currentPoint = mRoutePoints[currentRouteIndex]
// 当前位置距离最近的点的距离
val calculateCurrentdex = CoordinateUtils.calculateLineDistance(
location.longitude, location.latitude,
currentPoint.longitude, currentPoint.latitude
)
//要前往的站在轨迹中对应的点
val stationPointInRouteIndex = getArrivedPointIndexNew(
currentRouteIndex, mRoutePoints,
station.longitude,
station.latitude
)
// 距离站点最近的轨迹点
val stationPointInRoute = mRoutePoints[stationPointInRouteIndex]
// 站点距离轨迹中最近点的距离
val calculateLineDistance = CoordinateUtils.calculateLineDistance(
stationPointInRoute.longitude, stationPointInRoute.latitude,
station.longitude, station.latitude
)
if (currentRouteIndex < stationPointInRouteIndex) {
// subList 是[) 需要的是[]
val subList = mRoutePoints.subList(currentRouteIndex, stationPointInRouteIndex + 1)
// 轨迹点所有的距离
lastSumLength = calculateRouteSumLength(subList)
// region 站点坐标和 站点坐标对应轨迹点的坐标距离
// 需要加距离 和下一个轨迹点成钝角
if (stationPointInRouteIndex + 1 < mRoutePoints.size) {
val lastPointsNext = mRoutePoints[stationPointInRouteIndex + 1]
val degree = getDegree(
station.longitude, station.latitude,
stationPointInRoute.longitude, stationPointInRoute.latitude,
lastPointsNext.longitude, lastPointsNext.latitude
).toDouble()
if (degree > 90) {
lastSumLength = lastSumLength + calculateLineDistance
}
}
// 需要减距离 和上一个轨迹点成钝角
if (stationPointInRouteIndex - 1 >= 0) {
val lastPointsPre = mRoutePoints[stationPointInRouteIndex - 1]
val degree = getDegree(
station.longitude, station.latitude,
stationPointInRoute.longitude, stationPointInRoute.latitude,
lastPointsPre.longitude, lastPointsPre.latitude
).toDouble()
if (degree > 90) {
lastSumLength = lastSumLength - calculateLineDistance
}
}
// endregion
// region 当前位置和 对应轨迹点的坐标距离
// 需要加距离 和下一个轨迹点成钝角
if (currentRouteIndex + 1 < stationPointInRouteIndex) {
val currentPointsNext = mRoutePoints[currentRouteIndex + 1]
val degree = getDegree(
location.longitude, location.latitude,
currentPoint.longitude, currentPoint.latitude,
currentPointsNext.longitude, currentPointsNext.latitude
).toDouble()
if (degree > 90) {
lastSumLength = lastSumLength - calculateCurrentdex
}
}
// 需要减距离 和上一个轨迹点成钝角
if (currentRouteIndex - 1 >= 0) {
val lastPointsPre = mRoutePoints[currentRouteIndex - 1]
val degree = getDegree(
location.longitude, location.latitude,
currentPoint.longitude, currentPoint.latitude,
lastPointsPre.longitude, lastPointsPre.latitude
).toDouble()
if (degree > 90) {
lastSumLength = lastSumLength + calculateCurrentdex
}
}
// endregion
} else {
val lastPoints = mRoutePoints[stationPointInRouteIndex]
lastSumLength = CoordinateUtils.calculateLineDistance(
lastPoints.longitude, lastPoints.latitude,
location.longitude, location.latitude
)
}
return lastSumLength
}
@JvmStatic
fun coordinateConverterWgsToGcjListCommon(
mContext: Context?,
models: List<MessagePad.Location>
): List<LatLng> {
//转成MogoLatLng集合
val list: MutableList<LatLng> = ArrayList()
for (m in models) {
val mogoLatLng = coordinateConverterWgsToGcj(mContext, m)
list.add(mogoLatLng)
}
return list
}
@JvmStatic
fun coordinateConverterWgsToGcj(
mContext: Context?,
mogoLatLng: MessagePad.Location
): LatLng {
val mCoordinateConverter =
CoordinateConverter(mContext)
mCoordinateConverter.from(CoordinateConverter.CoordType.GPS)
mCoordinateConverter.coord(
LatLng(
mogoLatLng.latitude,
mogoLatLng.longitude
)
)
return mCoordinateConverter.convert()
}
@JvmStatic
fun coordinateConverterWgsToGcj(
mContext: Context?,
lon: Double,
lat: Double
): LatLng {
val mCoordinateConverter =
CoordinateConverter(mContext)
mCoordinateConverter.from(CoordinateConverter.CoordType.GPS)
mCoordinateConverter.coord(LatLng(lat, lon))
return mCoordinateConverter.convert()
}
/**
* 简单粗暴 直接比较 todo 需要优化
* @param mRoutePoints
* @param realLon
* @param realLat
* @return
*/
fun getRemainPointListByCompare(
mRoutePoints: List<LatLng>,
realLon: Double,
realLat: Double
): List<LatLng> {
val latePoints: MutableList<LatLng> = ArrayList()
var currentIndex = 0 //记录疑似点
if (mRoutePoints.size > 0) {
//基础点
val baseLatLng = mRoutePoints[0]
if (baseLatLng != null) {
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLon, realLat, baseLatLng.longitude, baseLatLng.latitude
) // lon,lat, prelon, prelat
for (i in 1 until mRoutePoints.size) {
val latLng = mRoutePoints[i]
val diff = CoordinateUtils.calculateLineDistance(
realLon, realLat, latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff) {
// Logger.d(M_TAXI + "calculateRouteSumLength", "点:"+i+"-------先记录点----- ");
baseDiffDis = diff
currentIndex = i
}
}
// Logger.d(M_TAXI + "calculateRouteSumLength", "点:"+currentIndex+"-------是最近的点------ ");
if (currentIndex == mRoutePoints.size - 1) {
latePoints.add(mRoutePoints[currentIndex])
} else if (currentIndex < mRoutePoints.size - 1) {
latePoints.addAll(mRoutePoints.subList(currentIndex, mRoutePoints.size))
}
return latePoints
}
}
return latePoints
}
/**
* 简单粗暴 直接比较 todo 需要优化
* @param mRoutePoints
* @param realLon
* @param realLat
* @return 返回已经到达点的index
*/
fun getArrivedPointIndex(mRoutePoints: List<LatLng>, realLon: Double, realLat: Double): Int {
var currentIndex = 0 //记录疑似点
if (mRoutePoints.size > 0) {
//基础点
val baseLatLng = mRoutePoints[0]
if (baseLatLng != null) {
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLon, realLat, baseLatLng.longitude, baseLatLng.latitude
) // lon,lat, prelon, prelat
for (i in 1 until mRoutePoints.size) {
val latLng = mRoutePoints[i]
val diff = CoordinateUtils.calculateLineDistance(
realLon, realLat, latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff) {
// Logger.d(M_TAXI + "calculateRouteSumLength", "点:"+i+"-------先记录点----- ");
baseDiffDis = diff
currentIndex = i
}
}
return currentIndex
}
}
return currentIndex
}
fun calculateRouteSumLengthByLocation(points: List<Location>?): Float {
if (points.isNullOrEmpty()) return 0f
var sumLength = 0f
//计算全路径总距离
var i = 0
while (i + 1 < points.size) {
val preLat = points[i].latitude
val preLon = points[i].longitude
val laLat = points[i + 1].latitude
val laLon = points[i + 1].longitude
val length = CoordinateUtils.calculateLineDistance(laLon, laLat, preLon, preLat)
sumLength += length
i++
}
return sumLength
}
@JvmStatic
fun coordinateConverterWgsToGcjLocations(
mContext: Context?,
models: List<MessagePad.Location>
): MutableList<MogoLocation> {
//转成MogoLatLng集合
val list = mutableListOf<MogoLocation>()
for (m in models) {
val mogoLatLng = coordinateConverterWgsToGcj(mContext, m)
val location = MogoLocation()
location.heading = m.heading.toFloat().toDouble()
location.latitude = mogoLatLng.latitude
location.longitude = mogoLatLng.longitude
list.add(location)
}
return list
}
@JvmStatic
fun coordinateConverterLatlngToLocation(models: List<LatLng>): List<MogoLocation> {
//转成MogoLatLng集合
val list: MutableList<MogoLocation> = ArrayList()
for (m in models) {
val location = MogoLocation()
location.latitude = m.latitude
location.longitude = m.longitude
list.add(location)
}
return list
}
@JvmStatic
fun coordinateConverterLocationToLatLng(
mContext: Context?,
models: List<MogoLocation>
): List<LatLng> {
//转成MogoLatLng集合
val list: MutableList<LatLng> = ArrayList()
for (m in models) {
val mogoLatLng = LatLng(m.latitude, m.longitude)
list.add(mogoLatLng)
}
return list
}
/**
* 根据前一个index经纬度航向角确认剩余轨迹
* @param preIndex
* @param mRoutePoints
* @param realLocation
* @return
*/
@JvmStatic
fun getRemainPointListByCompareNew(
preIndex: Int,
mRoutePoints: List<MogoLocation>,
realLocation: MogoLocation
): Map<Int, List<MogoLocation>> {
val routePonits: MutableMap<Int, List<MogoLocation>> = HashMap()
val latePoints: MutableList<MogoLocation> = ArrayList() // 剩余轨迹集合
var currentIndex = 0 //记录疑似点
if (mRoutePoints.size > 0) {
//基础点
val baseLatLng = mRoutePoints[0]
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLocation.longitude,
realLocation.latitude, baseLatLng.longitude, baseLatLng.longitude
) // lon,lat, prelon, prelat
for (i in mRoutePoints.indices) {
val latLng = mRoutePoints[i]
//todo 先看index对应点的方向和realLocation方向是否一致 方向角度不能过90度
if (realLocation.heading == realLocation.heading - latLng.heading ||
Math.abs(realLocation.heading - latLng.heading) <= 90
) {
val diff = CoordinateUtils.calculateLineDistance(
realLocation.longitude,
realLocation.latitude,
latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff) {
baseDiffDis = diff
currentIndex = i
}
}
}
Logger.d("calculateRouteSumLength", "点:$currentIndex-------是最近的点------ ")
if (currentIndex == mRoutePoints.size - 1) {
val location = mRoutePoints[currentIndex]
// LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
latePoints.add(location)
} else {
val locations = mRoutePoints.subList(currentIndex, mRoutePoints.size)
for (location in locations) {
// LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
latePoints.add(location)
}
}
routePonits[currentIndex] = latePoints
return routePonits
}
return routePonits
}
@JvmStatic
fun getArrivedPointIndexNew(
preIndex: Int, mRoutePoints: List<MogoLocation>,
realLocation: MogoLocation
): Int {
var currentIndex = 0 //记录疑似点 //基础点
val baseLatLng = mRoutePoints[0]
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLocation.longitude,
realLocation.latitude, baseLatLng.longitude, baseLatLng.longitude
) // lon,lat, prelon, prelat
for (i in mRoutePoints.indices) {
val latLng = mRoutePoints[i]
if (realLocation.heading == realLocation.heading - latLng.heading ||
Math.abs(realLocation.heading - latLng.heading) <= 90
) {
val diff = CoordinateUtils.calculateLineDistance(
realLocation.longitude,
realLocation.latitude,
latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff && i > currentIndex) {
baseDiffDis = diff
currentIndex = i
}
}
}
Logger.d("calculateRouteSumLength", "点:$currentIndex-------是最近的点------ ")
return currentIndex
}
@JvmStatic
fun getArrivedPointIndexNew(
preIndex: Int, mRoutePoints: List<MogoLocation>,
realLon: Double, realLat: Double
): Int {
var currentIndex = preIndex //记录疑似点 //基础点
val baseLatLng = mRoutePoints[0]
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLon,
realLat, baseLatLng.longitude, baseLatLng.longitude
) // lon,lat, prelon, prelat
for (i in preIndex until mRoutePoints.size) {
val latLng = mRoutePoints[i]
// if (realLocation.getBearing() == realLocation.getBearing() - latLng.getBearing() ||
// Math.abs(realLocation.getBearing() - latLng.getBearing()) <= 90){
val diff = CoordinateUtils.calculateLineDistance(
realLon,
realLat,
latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff && i > currentIndex) {
// Logger.d(M_TAXI + "calculateRouteSumLength", "点:"+i+"-------先记录点----- ");
baseDiffDis = diff
currentIndex = i
}
// }
}
Logger.d("calculateRouteSumLength", "点:$currentIndex-------是最近的点------ ")
return currentIndex
}
/**
* https://blog.csdn.net/Jeanne_0523/article/details/106056255
* @param vertexPointX
* @param vertexPointY
* @param point0X 角
* @param point0Y 角
* @param point1X
* @param point1Y
* @return
*/
fun getDegree(
vertexPointX: Double,
vertexPointY: Double,
point0X: Double,
point0Y: Double,
point1X: Double,
point1Y: Double
): Int {
//向量的点乘
val vector =
(point0X - vertexPointX) * (point1X - vertexPointX) + (point0Y - vertexPointY) * (point1Y - vertexPointY)
//向量的模乘
val sqrt = Math.sqrt(
(Math.abs((point0X - vertexPointX) * (point0X - vertexPointX)) + Math.abs((point0Y - vertexPointY) * (point0Y - vertexPointY)))
* (Math.abs((point1X - vertexPointX) * (point1X - vertexPointX)) + Math.abs((point1Y - vertexPointY) * (point1Y - vertexPointY)))
)
//反余弦计算弧度
val radian = Math.acos(vector / sqrt)
//弧度转角度制
return (180 * radian / Math.PI).toInt()
}
private fun ball2xyz(thera: Double, fie: Double, r: Double): Triple<Double, Double, Double> {
val x = r * cos(thera) * cos(fie)
val y = r * cos(thera) * sin(fie)
val z = r * sin(thera)
return Triple(x, y, z)
}
/**
* https://blog.csdn.net/reborn_lee/article/details/82497577
* 将地理经纬度转换成笛卡尔坐标系
*/
private fun geo2xyz(lat: Double, lng: Double): Triple<Double, Double, Double> {
val thera = Math.PI * lat / 180
val fie = Math.PI * lng / 180
return ball2xyz(thera, fie, 6400.0)
}
/**
* 计算3个地理坐标点之间的夹角
* @param l1 顶点坐标
* @param l2
* @param l3
* @return l1为顶点的角度 精度没有angleOflocation高
*/
fun getDegree(l2: MogoLocation, l1: MogoLocation, l3: MogoLocation): Double {
val (x1, y1, z1) = geo2xyz(l1.latitude, l1.longitude)
val (x2, y2, z2) = geo2xyz(l2.latitude, l2.longitude)
val (x3, y3, z3) = geo2xyz(l3.latitude, l3.longitude)
// 计算向量 P2P1 和 P2P3 的夹角 https://www.zybang.com/question/3379a30c0dd3041b3ef966803f0bf758.html
val p1P2 = sqrt((x2 - x1).pow(2.0) + (y2 - y1).pow(2.0) + (z2 - z1).pow(2.0))
val p2p3 = sqrt((x3 - x2).pow(2.0) + (y3 - y2).pow(2.0) + (z3 - z2).pow(2.0))
val p = (x1 - x2) * (x3 - x2) + (y1 - y2) * (y3 - y2) + (z1 - z2) * (z3 - z2) //P2P1*P2P3
return acos(p / (p1P2 * p2p3)) / Math.PI * 180
}
fun getHeadingAngle(location: MogoLocation, nextPoint: MogoLocation): Double {
return getHeadingAngle(
location.longitude,
location.latitude,
nextPoint.longitude,
nextPoint.latitude
)
}
fun getHeadingAngle(location: LatLng, nextPoint: LatLng): Double {
return getHeadingAngle(
location.longitude,
location.latitude,
nextPoint.longitude,
nextPoint.latitude
)
}
fun getHeadingAngle(locationLongitude: Double, locationLatitude: Double,
nextPointLongitude: Double, nextPointLatitude: Double): Double {
val y = sin(nextPointLongitude - locationLongitude) * cos(nextPointLatitude)
val x = cos(locationLatitude) * sin(nextPointLatitude) - sin(locationLatitude) *
cos(nextPointLatitude) * cos(nextPointLongitude - locationLongitude)
var bearing = atan2(y, x)
bearing = Math.toDegrees(bearing)
if (bearing < 0) {
bearing += 360.0
}
return 360-bearing
}
/**
* 支持带航行角的和不带航向角的
* 带航向角的会删除 航向角差大于90°的点
* @param preIndex 上次计算缓存
* @param mRoutePoints 轨迹点
* @param location 目标坐标
* @param type 1 开始站点 2 定位点 3 结束站点
* @return Triple<Int,Boolean?,Float>
* 距离目标坐标最近的轨迹点下标、
* 最近点坐标是目标坐标的上一个点还是下一个点
* 目标到最近轨迹点的距离
*/
@JvmStatic
fun getNearestPointInfo(
preIndex: Int,
endIndex: Int,
mRoutePoints: List<MogoLocation>,
location: MogoLocation,
type:Int,
size:Int = 4,
useHeading:Boolean = true,
): Triple<Int,Boolean?,Float> {
val startTime = System.currentTimeMillis()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"参数:[$preIndex $endIndex) mRoutePoints:${mRoutePoints.size} type:$type size:$size" +
" location:(${location.latitude},${location.longitude},${location.heading})")
var currentIndex:Int = preIndex //记录疑似点 //基础点
// 轨迹中的点和定位点的距离集合
val distanceMap: TreeMap<DistanceDegree, Int> = TreeMap()
for (index in preIndex until endIndex) {
val latLngIndex = mRoutePoints[index]
val distance = CoordinateUtils.calculateLineDistance(
location.longitude,
location.latitude,
latLngIndex.longitude,
latLngIndex.latitude
)
distanceMap[DistanceDegree.obtain(distance, null,null)] = index
if (distanceMap.size > size) {
distanceMap.pollLastEntry()?.key?.recycle()
}
}
distanceMap.forEach {
val distanceDegree = it.key
val pointIndex = it.value
val currentPoint = mRoutePoints[pointIndex]// 疑似最近的点
var nextDegree = 0.0
var preDegree = 0.0
if (pointIndex + 1 < mRoutePoints.size) {
val nextPoint = mRoutePoints[pointIndex + 1]
nextDegree = getDegree(location, currentPoint, nextPoint)
}else{
nextDegree = 135.0
}
// 需要减距离 和上一个轨迹点成钝角
if (pointIndex - 1 >= 0) {
val prePoint = mRoutePoints[pointIndex - 1]
preDegree = getDegree(
location,
currentPoint,
prePoint
)
}else{// 第一个个点处理
preDegree = 135.0
}
fun getDegreeNext(){
if (pointIndex + 1 < mRoutePoints.size) {
val nextPoint = mRoutePoints[pointIndex + 1]
val headingAngle = getHeadingAngle(currentPoint, nextPoint)
distanceDegree.degree = headingAngle
distanceDegree.isNext = false
}else{
val prePoint = mRoutePoints[pointIndex - 1]
val headingAngle = getHeadingAngle(prePoint,currentPoint)
distanceDegree.degree = headingAngle
distanceDegree.isNext = true
}
}
fun getDegreePre(){
if (pointIndex - 1 >= 0) {
val prePoint = mRoutePoints[pointIndex - 1]
val headingAngle = getHeadingAngle(prePoint, currentPoint)
distanceDegree.degree = headingAngle;
distanceDegree.isNext = true
}else{
val nextPoint = mRoutePoints[pointIndex + 1]
val headingAngle = getHeadingAngle(currentPoint,nextPoint)
distanceDegree.degree = headingAngle
distanceDegree.isNext = false
}
}
if(nextDegree>90){
getDegreeNext()
}
if(preDegree>90){
getDegreePre()
}
if(preDegree<90&&nextDegree<90&&preDegree+nextDegree>90){
if (pointIndex + 1 < mRoutePoints.size&&pointIndex - 1 >= 0) {
val nextPoint = mRoutePoints[pointIndex + 1]
val prePoint = mRoutePoints[pointIndex - 1]
val degree = getDegree(currentPoint, prePoint, nextPoint)
if(degree>90) {
getDegreePre()
// isNext 值无所谓了 就 ture和false都行
// 通过航向角过滤一遍
if(distanceDegree.degree!=null&&DrivingDirectionUtils.getAngleDiff(location.heading, distanceDegree.degree!!)<90){
currentIndex = pointIndex
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
return Triple(currentIndex,distanceDegree.isNext,distanceDegree.distance)
}
}
}
}
}
// 根据角度来排除一些点
val iterator = distanceMap.iterator()
// 有航向角比较航向角
while (iterator.hasNext()) {
val next = iterator.next()
val key = next.key
if (key.degree == null) {
key.recycle()
iterator.remove()
} else {
if (location.heading != 0.0 && useHeading) {
key.degree?.let {
val dexAngle = DrivingDirectionUtils.getAngleDiff(location.heading, it)
if (dexAngle > 90) {
key.recycle()
iterator.remove()
}
}
}
}
}
if(distanceMap.size==0&&size<16){
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return getNearestPointInfo(preIndex,endIndex,mRoutePoints,location,type,size+2)
}
// 最近点中包含上次计算的点和上次计算的最近的一个点
if(distanceMap.containsValue(preIndex)&&distanceMap.containsValue(preIndex+1)&&type==1){
var preIndexDistance:DistanceDegree?=null
var preIndexNextDistance:DistanceDegree?=null
distanceMap.iterator().forEach { en ->
val key = en.key
val value = en.value
if(value==preIndex){
preIndexDistance = key
}else if(value==preIndex+1){
preIndexNextDistance = key
}
}
if(preIndexDistance!=null&&preIndexNextDistance!=null){
if(preIndexDistance!!.distance<preIndexNextDistance!!.distance){
currentIndex = preIndex
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return Triple(currentIndex,preIndexDistance?.isNext,preIndexDistance!!.distance)
}else{
currentIndex = preIndex+1
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return Triple(currentIndex,preIndexNextDistance?.isNext,preIndexNextDistance!!.distance)
}
}
}
var maxIndex = 0
var minIndex = Int.MAX_VALUE
distanceMap.iterator().forEach { en ->
val value = en.value
if(value<minIndex){
minIndex = value
}
if(value>maxIndex){
maxIndex = value
}
}
val middleVale =minIndex + (maxIndex-minIndex)/2
when (type) {
1 -> {// 求开始站点
val iteratorRemove = distanceMap.iterator()
while (iteratorRemove.hasNext()) {
val next = iteratorRemove.next()
val key = next.key
if(next.value>middleVale){
key.recycle()
iteratorRemove.remove()
}
}
}
3 -> {// 求结束站点
val iteratorRemove = distanceMap.iterator()
while (iteratorRemove.hasNext()) {
val next = iteratorRemove.next()
val key = next.key
if(next.value<middleVale){
key.recycle()
iteratorRemove.remove()
}
}
}
else -> {}
}
// 根据距离来计算 最近的点 只有一个前面的点
var tempDistance = Float.MAX_VALUE
var isNext:Boolean? = null
distanceMap.iterator().forEach { en ->
val key = en.key
val value = en.value
// 排除没有第一个值0是
if(value==preIndex+1&&preIndex!=0&&type==1){
currentIndex = value
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return Triple(currentIndex,key.isNext,key.distance)
}
key.distance.let {
if (it < tempDistance) {
tempDistance = it
currentIndex = value
isNext = key.isNext
}
}
}
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return Triple(currentIndex,isNext,tempDistance)
}
}

View File

@@ -0,0 +1,166 @@
package com.mogo.och.common.module.utils;
import com.mogo.eagle.core.utilcode.util.DateTimeUtils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
/**
* @author: wangmingjun
* @date: 2022/5/6
*/
public class DateTimeUtil {
public static final String HH_mm = "HH:mm";
public static final String MM_dd = "MM-dd";
public static final String MM_dd_HH_mm = "MM-dd HH:mm";
public static final String yyyy_MM_dd = "yyyy-MM-dd";
public static final String yyyyMMdd = "yyyy-MM-dd";
public static final String yy_MM_dd = "yy.MM.dd";
public static final String yyyy_MM_dd_HH_mm = "yyyy-MM-dd HH:mm";
public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
public static String formatCalendarToString(Calendar calendar, String format){
if (calendar == null) return "";
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
return dateFormat.format(calendar.getTime());
}catch (Exception e){
e.printStackTrace();
}
return "";
}
public static boolean compareDateIsCurrentDay(Calendar targetCalendar){
Calendar currentCale = DateTimeUtils.getCurrentDateTime();
String currentDay = formatCalendarToString(currentCale, yyyy_MM_dd);
return currentDay.equals(formatCalendarToString(targetCalendar, yyyy_MM_dd));
}
public static Calendar formatLongToCalendar(long time){
Calendar calendar = null;
try {
calendar = Calendar.getInstance();
calendar.setTimeInMillis(time);
}catch (Exception e){
e.printStackTrace();
}
return calendar;
}
public static String formatLongToString(long time, String format){
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
return dateFormat.format(time);
}catch (Exception e){
e.printStackTrace();
}
return "";
}
public static String getYMDTime(long time){//格式为 2021.8.21
try {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(time);
int month = calendar.get(Calendar.MONTH) + 1;
return calendar.get(Calendar.YEAR)+"."+month+"."+ calendar.get(Calendar.DAY_OF_MONTH);
}catch (Exception e){
e.printStackTrace();
}
return "";
}
/**
*
* @param seconds 60
* @return 1 时
*/
public static String secondsToHourStr(long seconds){//秒数转成相应的 小时分钟数
if (seconds >= 3600){
int hours = (int)seconds/3600;
return String.valueOf(hours);
}
return "";
}
/**
*
* @param seconds 60
* @return 1 时
*/
public static String secondsToMinuteStr(long seconds){//秒数转成相应的 小时分钟数
int minute = (int)(seconds % 3600)/60;
return String.valueOf(minute);
}
/**
* 返回当前时间
* @return
*/
public static long getCurrentTimeStamp(){
return System.currentTimeMillis();
}
public static long getTodayStartTime(){
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+8"));
calendar.set(Calendar.HOUR_OF_DAY,0);
calendar.set(Calendar.MINUTE,0);
calendar.set(Calendar.SECOND,0);
return calendar.getTimeInMillis();
}
public static String getAfterSecondTime(int timeInSecond,String formate){
Calendar beforeTime = Calendar.getInstance();
beforeTime.add(Calendar.SECOND, timeInSecond);
return formatCalendarToString(beforeTime, formate);
}
public static String getAfterSecondTime(int timeInSecond){
Calendar beforeTime = Calendar.getInstance();
beforeTime.add(Calendar.SECOND, timeInSecond);
return formatCalendarToString(beforeTime, DateTimeUtil.HH_mm);
}
/**
* @param second 秒
* @description: 秒转换为时分秒 HH:mm:ss 格式 仅当小时数大于0时 展示HH
*/
public static String second2Time(Long second) {
if (second == null || second < 0) {
return "00:00";
}
long h = second / 3600;
long m = (long) Math.ceil((second % 3600) / 60.0);// 向上取整
long s = second % 60;
StringBuilder stringBuffer = new StringBuilder();
if(m==60){
m=0;
h = h+1;
}
if (h > 0) {
stringBuffer.append(h < 10 ? ("0" + h) : h).append(":");
}else {
stringBuffer.append("00:");
}
stringBuffer.append(m < 10 ? ("0" + m) : m);
//str += (s < 10 ? ("0" + s) : s);
return stringBuffer.toString();
}
public static String second2MMSS(Long second) {
if (second == null || second < 0) {
return "00:00";
}
long m = (long) Math.floor((second % 3600) / 60.0);// 向上取整
long s = second % 60;
StringBuilder stringBuffer = new StringBuilder();
if (m > 0) {
stringBuffer.append(m < 10 ? ("0" + m) : m).append(":");
}else {
stringBuffer.append("00:");
}
stringBuffer.append(s < 10 ? ("0" + s) : s);
return stringBuffer.toString();
}
}

View File

@@ -0,0 +1,13 @@
package com.mogo.och.common.module.utils
import android.content.res.Resources
/**
* @author: wangmingjun
* @date: 2022/1/21
*/
object DimenUtil{
fun dp2px(value:Float):Float{
return (0.5f + value * Resources.getSystem().displayMetrics.density)
}
}

View File

@@ -0,0 +1,37 @@
package com.mogo.och.common.module.utils;
import android.text.TextUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
public class FieldUtils {
public static Field getDeclaredField(final Class<?> cls, final String fieldName, final
boolean forceAccess) {
if (cls == null || TextUtils.isEmpty(fieldName)) {
return null;
}
try {
// only consider the specified class by using getDeclaredField()
final Field field = cls.getDeclaredField(fieldName);
if (!isAccessible(field)) {
if (forceAccess) {
field.setAccessible(true);
} else {
return null;
}
}
return field;
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
private static boolean isAccessible(final Member m) {
return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic();
}
}

View File

@@ -0,0 +1,98 @@
package com.mogo.och.common.module.utils
/**
* @author aibingbing
* @date: 2023/9/13
* @desc Flow Bus
*/
import android.util.Log
import androidx.lifecycle.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
/**
* FlowBus消息总线
*/
object FlowBus {
private const val TAG = "FlowBus"
private val busMap = mutableMapOf<String, EventBus<*>>()
private val busStickMap = mutableMapOf<String, StickEventBus<*>>()
@Synchronized
fun <T> with(key: String): EventBus<T> {
var eventBus = busMap[key]
if (eventBus == null) {
eventBus = EventBus<T>(key)
busMap[key] = eventBus
}
return eventBus as EventBus<T>
}
@Synchronized
fun <T> withStick(key: String): StickEventBus<T> {
var eventBus = busStickMap[key]
if (eventBus == null) {
eventBus = StickEventBus<T>(key)
busStickMap[key] = eventBus
}
return eventBus as StickEventBus<T>
}
//真正实现类
open class EventBus<T>(private val key: String) : LifecycleObserver {
//私有对象用于发送消息
private val _events: MutableSharedFlow<T> by lazy {
obtainEvent()
}
//暴露的公有对象用于接收消息
private val events = _events.asSharedFlow()
open fun obtainEvent(): MutableSharedFlow<T> =
MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST)
//主线程接收数据
fun register(lifecycleOwner: LifecycleOwner, action: (t: T) -> Unit) {
lifecycleOwner.lifecycle.addObserver(this)
lifecycleOwner.lifecycleScope.launch {
events.collect {
try {
action(it)
} catch (e: Exception) {
e.printStackTrace()
Log.e(TAG, "FlowBus - Error:$e")
}
}
}
}
//协程中发送数据
suspend fun post(event: T) {
_events.emit(event)
}
//主线程发送数据
fun post(scope: CoroutineScope, event: T) {
scope.launch {
_events.emit(event)
}
}
//自动销毁
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
Log.w(TAG, "FlowBus - 自动onDestroy")
val subscriptCount = _events.subscriptionCount.value
if (subscriptCount <= 0)
busMap.remove(key)
}
}
class StickEventBus<T>(key: String) : EventBus<T>(key) {
override fun obtainEvent(): MutableSharedFlow<T> =
MutableSharedFlow(1, 1, BufferOverflow.DROP_OLDEST)
}
}

View File

@@ -0,0 +1,211 @@
package com.mogo.och.common.module.utils
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.os.Handler
import android.os.Looper
import android.widget.ImageView
import com.mogo.commons.AbsMogoApplication
import java.lang.RuntimeException
import java.lang.ref.SoftReference
class FrameAnimatorContainer (resId: Int,
fps: Int,
imageView: ImageView,
sequence: Boolean = true,// 播放顺序 true 正序 false 倒序
isOnce: Boolean = false,// 一次性的 true 值播放一次 false 重复播放
initFirstFrame:Boolean = true,
width:Int = -1,
height:Int = -1){
private val TAG = "FrameAnimatorContainer"
private lateinit var mFrames: IntArray // 帧数组
private var mIndex = 0 // 当前帧
private var mShouldRun = false // 开始/停止播放用
private var mIsRunning = false // 动画是否正在播放,防止重复播放
private var mSoftReferenceImageView: SoftReference<ImageView>? = null // 软引用ImageView以便及时释放掉
private var mHandler: Handler? = null
private var mDelayMillis = 0
private var mOnAnimationStoppedListener: OnAnimationStoppedListener? = null//播放停止监听
private var mBitmap: Bitmap? = null
private var mBitmapOptions: BitmapFactory.Options? = null //Bitmap管理类可有效减少Bitmap的OOM问题
var isOnce:Boolean = false
var sequence:Boolean = true
init {
createAnimation(imageView, getData(resId), fps,initFirstFrame,width,height)
this.isOnce = isOnce
this.sequence = sequence
}
private fun createAnimation(
imageView: ImageView,
frames: IntArray,
fps: Int,
initFirstFrame: Boolean,
width: Int,
height: Int
) {
mHandler = Handler(Looper.myLooper()!!)
mFrames = frames
mIndex = -1
mSoftReferenceImageView = SoftReference(imageView)
mShouldRun = false
mIsRunning = false
mDelayMillis = 1000 / fps //帧动画时间间隔,毫秒
if(initFirstFrame) {
imageView.setImageResource(mFrames[0])
}
var widthImage = -1
var heightImage = -1
var config = Bitmap.Config.ARGB_8888
if(width>0&&height>0){
widthImage = width
heightImage = height
}else{
try {
val bmp = (imageView.drawable as BitmapDrawable).bitmap
widthImage = bmp.width
heightImage = bmp.height
config = bmp.config
}catch (e:Exception){
throw RuntimeException("请设置图片或传递大小")
}
}
// 当图片大小类型相同时进行复用避免频繁GC
mBitmap = Bitmap.createBitmap(widthImage, heightImage, config)
mBitmapOptions = BitmapFactory.Options()
//设置Bitmap内存复用
mBitmapOptions!!.inBitmap = mBitmap //Bitmap复用内存块类似对象池避免不必要的内存分配和回收
mBitmapOptions!!.inMutable = true //解码时返回可变Bitmap
mBitmapOptions!!.inSampleSize = 1 //缩放比例
}
//循环读取下一帧
private val next: Int
get() {
mIndex++
if (mIndex >= mFrames.size){
mIndex = 0
if(isOnce){// 一次性动画 播放完毕后直接结束
stop()
}
}
if(!sequence){// 倒叙
return mFrames[mFrames.size-1-mIndex]
}
return mFrames[mIndex]
}
@Synchronized
fun reStart(){
mIndex = -1
mIsRunning = false
start()
}
/**
* 播放动画,同步锁防止多线程读帧时,数据安全问题
*/
@Synchronized
fun start() {
mShouldRun = true
if (mIsRunning) return
mHandler?.removeCallbacksAndMessages(null)
val runnable: Runnable = object : Runnable {
override fun run() {
val imageView = mSoftReferenceImageView!!.get()
if (!mShouldRun || imageView == null) {
mIsRunning = false
if (mOnAnimationStoppedListener != null) {
mOnAnimationStoppedListener!!.AnimationStopped()
}
return
}
mIsRunning = true
//新开线程去读下一帧
if (imageView.isShown) {
val imageRes: Int = next
if (!mShouldRun || imageView == null) {
mIsRunning = false
if (mOnAnimationStoppedListener != null) {
mOnAnimationStoppedListener!!.AnimationStopped()
}
return
}
mHandler?.postDelayed(this, mDelayMillis.toLong())
if (mBitmap != null) { // so Build.VERSION.SDK_INT >= 11
var bitmap: Bitmap? = null
try {
bitmap = BitmapFactory.decodeResource(
imageView.resources,
imageRes,
mBitmapOptions
)
} catch (e: Exception) {
e.printStackTrace()
}
if (bitmap != null) {
imageView.setImageBitmap(bitmap)
} else {
imageView.setImageResource(imageRes)
mBitmap!!.recycle()
mBitmap = null
}
} else {
imageView.setImageResource(imageRes)
}
}
}
}
mHandler!!.post(runnable)
}
/**
* 停止播放
*/
@Synchronized
fun stop() {
mShouldRun = false
}
fun isPlaying():Boolean{
return mShouldRun
}
/**
* 设置停止播放监听
* @param listener 设置监听
*/
fun setOnAnimStopListener(listener: OnAnimationStoppedListener?) {
mOnAnimationStoppedListener = listener
}
/**
* 从xml中读取帧数组
* @param resId
* @return
*/
fun getData(resId: Int): IntArray {
val array = AbsMogoApplication.getApp().resources.obtainTypedArray(resId)
val len = array.length()
val intArray = IntArray(array.length())
for (i in 0 until len) {
intArray[i] = array.getResourceId(i, 0)
}
array.recycle()
return intArray
}
fun setData(mFrames: IntArray){
this.mFrames = mFrames
}
/**
* 停止播放监听
*/
interface OnAnimationStoppedListener {
fun AnimationStopped()
}
}

View File

@@ -0,0 +1,41 @@
package com.mogo.och.common.module.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* @author: wangmingjun
* @date: 2022/5/6
*/
public class NumberFormatUtil {
/**
* 有小数1位 没有小数保留整数
* @param d
* @return
*/
public static String formatLong(double d) {
BigDecimal bg = BigDecimal.valueOf(d).setScale(1, RoundingMode.HALF_UP);
double num = bg.doubleValue();
if (Math.ceil(num) - num == 0) {
return String.valueOf((long) num);
}
return String.valueOf(num);
}
/**
* 截取小数点后cutNum位 不进行四舍五入
* @param num
* @param cutNum
* @return
*/
public static String cutOutNumber(double num,int cutNum){
try{
//注将double类型转成String类型再处理截取。 使用double部分数据截取有问题
BigDecimal bg = new BigDecimal(String.valueOf(num)).setScale(cutNum, RoundingMode.DOWN);
return String.valueOf(bg.doubleValue());
}catch (Exception e){
}
return "";
}
}

View File

@@ -0,0 +1,139 @@
package com.mogo.och.common.module.utils;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class OCHThreadPoolManager<T> {
/**
* 根据cpu的数量动态的配置核心线程数和最大线程数
*/
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
/**
* 核心线程数 = CPU核心数 + 1
*/
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
/**
* 线程池最大线程数 = CPU核心数 * 2 + 1
*/
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
/**
* 非核心线程闲置时超时1s
*/
private static final int KEEP_ALIVE = 1;
/**
* 线程池的对象
*/
private ThreadPoolExecutor executor;
/**
* 要确保该类只有一个实例对象,避免产生过多对象消费资源,所以采用单例模式
*/
private OCHThreadPoolManager() {
}
private volatile static OCHThreadPoolManager INSTANCE;
private static final byte[] obj = new byte[0];
public static OCHThreadPoolManager getsInstance() {
if (INSTANCE == null) {
synchronized (obj) {
if (INSTANCE == null) {
INSTANCE = new OCHThreadPoolManager();
}
}
}
return INSTANCE;
}
/**
* 开启一个无返回结果的线程
*/
public void execute(Runnable r) {
if (executor == null) {
/*
* corePoolSize:核心线程数
* maximumPoolSize线程池所容纳最大线程数(workQueue队列满了之后才开启)
* keepAliveTime非核心线程闲置时间超时时长
* unitkeepAliveTime的单位
* workQueue等待队列存储还未执行的任务
* threadFactory线程创建的工厂
* handler异常处理机制
*
*/
executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,
KEEP_ALIVE, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
}
// 把一个任务丢到了线程池中
try {
executor.execute(r);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 开启一个有返回结果的线程
*/
public Future<T> submit(Callable<T> r) {
if (executor == null) {
/*
* corePoolSize:核心线程数
* maximumPoolSize线程池所容纳最大线程数(workQueue队列满了之后才开启)
* keepAliveTime非核心线程闲置时间超时时长
* unitkeepAliveTime的单位
* workQueue等待队列存储还未执行的任务
* threadFactory线程创建的工厂
* handler异常处理机制
*
*/
executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,
KEEP_ALIVE, TimeUnit.SECONDS, new ArrayBlockingQueue<>(20),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
}
// 把一个任务丢到了线程池中
return executor.submit(r);
}
/**
* 开启一个无返回结果的线程
*/
public Future submit(Runnable r) {
if (executor == null) {
/*
* corePoolSize:核心线程数
* maximumPoolSize线程池所容纳最大线程数(workQueue队列满了之后才开启)
* keepAliveTime非核心线程闲置时间超时时长
* unitkeepAliveTime的单位
* workQueue等待队列存储还未执行的任务
* threadFactory线程创建的工厂
* handler异常处理机制
*
*/
executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,
KEEP_ALIVE, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
}
return executor.submit(r);
}
/**
* 把任务移除等待队列
*/
public void cancel(Runnable r) {
if (r != null) {
executor.getQueue().remove(r);
}
}
}

View File

@@ -0,0 +1,35 @@
package com.mogo.och.common.module.utils;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import androidx.core.content.ContextCompat;
/**
* @author: wangmingjun
* @date: 2021/12/7
*/
public class PermissionUtil {
public static boolean checkPermission(Context context,String... permissons) {
for (String permisson : permissons) {
if ((ContextCompat.checkSelfPermission(context,
permisson) != PackageManager.PERMISSION_GRANTED)) {
return false;
}
}
return true;
}
public static boolean isLocServiceEnable(Context context) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (gps || network) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,16 @@
package com.mogo.och.common.module.utils;
/**
* @author: wangmingjun
* @date: 2021/11/26
*/
public class PinYinUtil {
/**
* 得到中文字符串首字母
* @param str 需要转化的中文字符串
* @return 大写首字母缩写的字符串
*/
public static String getPinYinHeadChar(String str) {
return str;
}
}

View File

@@ -0,0 +1,117 @@
package com.mogo.och.common.module.utils
import android.graphics.Bitmap
import android.graphics.Canvas
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.common.BitMatrix
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import java.util.*
/**
* isDeleteWhite 是否删除白边
*/
fun createQRCode(address: String, width: Int, height: Int,isDeleteWhite: Boolean): Bitmap? {
val hints = Hashtable<EncodeHintType, Any>()
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H
hints[EncodeHintType.MARGIN] = if (isDeleteWhite) 1 else 0
var bitMatrix = QRCodeWriter().encode(
address,
BarcodeFormat.QR_CODE, width, height, hints
)
if (isDeleteWhite) {
//删除白边
bitMatrix = deleteWhite(bitMatrix)
}
val widthNew = bitMatrix.width
val heightNew = bitMatrix.height
val pixels = IntArray(widthNew * heightNew)
//下面这里按照二维码的算法,逐个生成二维码的图片,
//两个for循环是图片横列扫描的结果
for (y in 0 until heightNew) {
for (x in 0 until widthNew) {
if (bitMatrix.get(x, y)) {
pixels[y * widthNew + x] = -0x1000000
} else {
pixels[y * widthNew + x] = -0x1
}
}
}
//生成二维码图片的格式使用ARGB_8888
var bitmap = Bitmap.createBitmap(widthNew, heightNew, Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, widthNew, 0, 0, widthNew, heightNew)
return bitmap
}
/**
* 删除白色边框
*
* @param matrix matrix
* @return BitMatrix
*/
private fun deleteWhite(matrix: BitMatrix): BitMatrix? {
val rec = matrix.enclosingRectangle
val resWidth = rec[2] + 1
val resHeight = rec[3] + 1
val resMatrix = BitMatrix(resWidth, resHeight)
resMatrix.clear()
for (i in 0 until resWidth) {
for (j in 0 until resHeight) {
if (matrix[i + rec[0], j + rec[1]]) resMatrix[i] = j
}
}
return resMatrix
}
fun createQRCodeWithPicture(bmCenter: Bitmap,address: String, width: Int, height: Int,isDeleteWhite: Boolean): Bitmap?{
var qrCode = createQRCode(address,width,height,isDeleteWhite)
//8创建一个bitmap对象用于作为其图标
qrCode?.let {
val resultBitmap = addLogo(it,bmCenter)
if (resultBitmap != null){
return resultBitmap
}
}
return null
}
/**
* 用于向创建的二维码中添加一个logo
* @param bmQr
* @param bmCenter
* @return
*/
fun addLogo(bmQr: Bitmap, bmCenter:Bitmap) :Bitmap?{
if (bmQr == null) {
return null
}
if (bmCenter == null) {
return bmQr
}
//获取图片的宽高
val bmQrWidth = bmQr.width
val bmQrHeight = bmQr.height
val bmCenterWidth = bmCenter.width
val bmCenterHeight = bmCenter.height
var bitmap = Bitmap.createBitmap(bmQrWidth, bmQrHeight, Bitmap.Config.ARGB_8888)
try {
var canvas = Canvas(bitmap)
canvas.drawBitmap(bmQr, 0f, 0f, null)
canvas.drawBitmap(bmCenter, ((bmQrWidth-bmCenterWidth)/2-bmCenterWidth/2).toFloat(),
((bmQrHeight-bmCenterHeight)/2-bmCenterHeight/2).toFloat(), null)
canvas.save()
canvas.restore()
} catch (e: Exception) {
bitmap = null
e.stackTrace
}
return bitmap
}

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