feat(servicing): 优化订单详情页面布局和功能

-调整订单信息显示布局,增加订单来源显示
- 优化地图标记添加逻辑,提高地图展示效果
- 改进路径规划功能,优化预计到达时间计算方式- 更新车辆损伤照片加载逻辑,提高数据展示效率
- 修复部分页面样式问题,提升用户体验
This commit is contained in:
songzhiling
2025-04-25 18:01:44 +08:00
parent c606ed95cd
commit b0c2f7352d
20 changed files with 794 additions and 851 deletions

View File

@ -73,7 +73,7 @@ publishing {
release(MavenPublication) {
groupId = 'io.github.szl9'
artifactId = 'zd_servicing'
version = "1.0.1.9"
version = "1.0.1.9.9.4"
pom {
packaging = "aar"

View File

@ -9,6 +9,7 @@
android:protectionLevel="signature" /> <!-- 位置相关权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 存储相关权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@ -75,7 +76,10 @@
</intent>
</queries>
<application>
<application
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true"
tools:targetApi="24">
<activity
android:name="com.za.ui.main.ServiceLauncherActivity"
android:exported="true"
@ -115,16 +119,19 @@
<service
android:name="com.za.ui.order_report.ReportFloatingManager"
android:enabled="true"
android:exported="false" />
android:exported="false"
android:foregroundServiceType="location" />
<activity
android:name="com.za.ui.order_report.HistoryReportActivity"
android:exported="false"
android:screenOrientation="portrait" />
android:screenOrientation="portrait"
android:theme="@style/Theme.Dealer" />
<activity
android:name="com.za.ui.order_report.OrderReportActivity"
android:exported="false"
android:screenOrientation="portrait" />
android:screenOrientation="portrait"
android:theme="@style/Theme.Dealer" />
<activity
android:name="com.za.ui.h5.CommonH5Activity"
android:exported="false"
@ -152,7 +159,8 @@
android:theme="@style/Theme.Dealer" />
<activity
android:name="com.za.signature.GridPaintActivity"
android:exported="false" />
android:exported="false"
android:theme="@style/Theme.Dealer" />
<activity
android:name="com.za.ui.servicing.operation.InOperationActivity"
android:exported="false"
@ -161,8 +169,8 @@
<activity
android:name="com.za.ui.camera.ZdCameraXActivity"
android:exported="false"
android:theme="@style/Theme.Dealer"
android:screenOrientation="portrait">
android:screenOrientation="portrait"
android:theme="@style/Theme.Dealer">
<meta-data
android:name="android.app.lib_name"
android:value="" />

View File

@ -35,6 +35,8 @@ data class JpushBean(
val distLng: Double? = null,
val importantTip: String? = null,
val tipContent: String? = null,
val settleType: Int?=null,//结算类型 1 月结 2 现金
var orderSource: String? = null, // "orderSource":"中道救援-比亚迪道路救援项目"
val hasReplaceBatteryCapable: Int? = null ,//是否有更换电瓶的能力 1 搭电可以更换电瓶 2搭电不可以更换电瓶 其他的不展示
var voiceType : Int?=null //语音提示类型 1小修单 2拖车单 3困境单
) : Serializable {

View File

@ -2,6 +2,7 @@ package com.za.net
import android.net.ParseException
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.ThreadUtils
import com.blankj.utilcode.util.ToastUtils
import com.google.gson.JsonParseException
import com.za.base.Const
@ -22,90 +23,95 @@ import javax.net.ssl.SSLHandshakeException
* Created by DoggieX on 2017/7/26.
*/
abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
override fun onSubscribe(d: Disposable) {
// if (!NetworkUtils.isAvailable()) {
// doFailure(999, "网络无法使用")
// d.dispose()
// }
}
override fun onSubscribe(d : Disposable) { // if (!NetworkUtils.isAvailable()) {
// doFailure(999, "网络无法使用")
// d.dispose()
// }
}
override fun onNext(tBaseResponse: BaseResponse<T>) {
if (tBaseResponse.isOk) {
doSuccess(tBaseResponse.result)
} else {
when (tBaseResponse.code) {
3, 401 -> handlerTokenExpired()
// 4 -> RsaRouter.navigate(context, "/page/Standby")
}
if (null != tBaseResponse.msg) {
doFailure(tBaseResponse.code, tBaseResponse.msg)
} else if (null != tBaseResponse.message) {
doFailure(tBaseResponse.code, tBaseResponse.message)
} else {
doFailure(tBaseResponse.code, "error")
}
}
}
override fun onNext(tBaseResponse : BaseResponse<T>) {
if (tBaseResponse.isOk) {
doSuccess(tBaseResponse.result)
} else {
when (tBaseResponse.code) {
3, 401 -> handlerTokenExpired()
}
val errMsg = if (null != tBaseResponse.msg) {
tBaseResponse.msg
} else if (null != tBaseResponse.message) {
tBaseResponse.message
} else {
"error"
}
doFailure(tBaseResponse.code, errMsg)
if (errMsg?.contains("请下载新版本app!") == true) {
handlerTokenExpired()
}
}
}
override fun onError(e: Throwable) {
LogUtil.print("net error", e)
when (e) {
is JsonParseException -> {
doFailure(1, "数据解析错误")
}
override fun onError(e : Throwable) {
LogUtil.print("net error", e)
when (e) {
is JsonParseException -> {
doFailure(1, "数据解析错误")
}
is JSONException -> {
doFailure(1, "数据解析错误")
}
is JSONException -> {
doFailure(1, "数据解析错误")
}
is ParseException -> {
doFailure(1, "数据解析错误")
}
is ParseException -> {
doFailure(1, "数据解析错误")
}
is ConnectException -> {
doFailure(Const.NetWorkException, "与服务器断开连接")
}
is ConnectException -> {
doFailure(Const.NetWorkException, "与服务器断开连接")
}
is UnknownHostException -> {
doFailure(Const.NetWorkException, "与服务器断开连接")
}
is UnknownHostException -> {
doFailure(Const.NetWorkException, "与服务器断开连接")
}
is SSLHandshakeException -> {
doFailure(1, "证书验证失败")
LogUtil.print("SSLHandshakeException", e)
}
is SSLHandshakeException -> {
doFailure(1, "证书验证失败")
LogUtil.print("SSLHandshakeException", e)
}
is TimeoutException -> {
doFailure(Const.NetWorkException, "网络连接超时1")
LogUtil.print("TimeoutException", e)
}
is TimeoutException -> {
doFailure(Const.NetWorkException, "网络连接超时1")
LogUtil.print("TimeoutException", e)
}
is SocketTimeoutException -> {
doFailure(Const.NetWorkException, "网络连接超时2")
LogUtil.print("SocketTimeoutException2", e)
}
else -> {
doFailure(1, "error:" + e.message)
LogUtil.print("other error", e)
}
}
}
is SocketTimeoutException -> {
doFailure(Const.NetWorkException, "网络连接超时2")
LogUtil.print("SocketTimeoutException2", e)
}
override fun onComplete() {
}
else -> {
doFailure(1, "error:" + e.message)
LogUtil.print("other error", e)
}
}
}
abstract fun doSuccess(it: T?)
override fun onComplete() {
}
abstract fun doFailure(code: Int, msg: String?)
abstract fun doSuccess(it : T?)
private fun handlerTokenExpired() {
ToastUtils.showShort("登陆信息已过期,请重新登录")
try {
GlobalData.clearUserCache()
ZdLocationManager.stopContinuousLocation()
ActivityUtils.finishAllActivities()
} catch (e: Exception) {
LogUtil.print("handlerTokenExpired", e)
}
}
abstract fun doFailure(code : Int, msg : String?)
private fun handlerTokenExpired() {
ThreadUtils.runOnUiThread {
try {
ToastUtils.showShort("登陆信息已过期,请重新登录")
GlobalData.clearUserCache()
ZdLocationManager.stopContinuousLocation()
ActivityUtils.startLauncherActivity()
} catch (e : Exception) {
LogUtil.print("handlerTokenExpired", e)
}
}
}
}

View File

@ -16,7 +16,6 @@ import com.za.common.log.LogUtil
import com.za.common.util.DeviceUtil
import com.za.service.mqtt.MyMqttClient
import com.za.servicing.R
import java.util.concurrent.atomic.AtomicReference
interface PushListener {
fun newOrderMsg(jpushBean : JpushBean)
@ -30,7 +29,7 @@ interface PushListener {
data class LastJPushBean(val msg : Int, val time : Long = System.nanoTime())
object ServiceManager {
private val pushListener = AtomicReference<PushListener>()
private var pushListener : PushListener? = null
private var lastJPushBean : LastJPushBean? = null
private const val DUPLICATE_MSG_THRESHOLD = 3000L // 3秒
@ -56,16 +55,15 @@ object ServiceManager {
// Register push listener
fun registerPushListener(listener : PushListener?) {
listener?.let {
pushListener.set(it)
LogUtil.print("ServiceManager", "Registered push listener: ${it.javaClass.simpleName}")
}
this.pushListener = listener
LogUtil.print("ServiceManager",
"Registered push listener: ${pushListener?.javaClass?.simpleName}")
}
// Handle incoming push messages
@Synchronized
fun handlerPushMsg(msg : String) {
LogUtil.print("JpushMessage", "Received push message: $msg")
LogUtil.print("handlerPushMsg", "Received push message: $msg")
// 优化后的重复消息判断
lastJPushBean?.let {
@ -76,12 +74,12 @@ object ServiceManager {
}
if (msg.startsWith("broadcast:")) {
lastJPushBean = LastJPushBean(msg.hashCode())
lastJPushBean = LastJPushBean(msg = msg.hashCode())
handleBroadcast(msg)
return
}
try {
lastJPushBean = LastJPushBean(msg.hashCode())
lastJPushBean = LastJPushBean(msg = msg.hashCode())
val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java)
sendSystemNotificationFromMessage(jpushOrderInfoBean)
when (jpushOrderInfoBean.pushType) {
@ -103,7 +101,7 @@ object ServiceManager {
private fun handleBroadcast(msg : String) {
try {
val content = msg.substring(10)
pushListener.get()?.broadcast(content)
pushListener?.broadcast(content)
sendNotification(GlobalData.application, content)
LogUtil.print("JpushMessage", "Broadcast content: $content")
} catch (e : Exception) {
@ -124,7 +122,9 @@ object ServiceManager {
// Handle new order messages
private fun newOrderMsg(jpushOrderBean : JpushBean) {
try {
pushListener.get()?.newOrderMsg(jpushOrderBean)
LogUtil.print("JpushMessage",
"Handling new order message: $pushListener ${pushListener?.javaClass?.simpleName}")
pushListener?.newOrderMsg(jpushOrderBean)
} catch (e : Exception) {
LogUtil.print("JpushMessage", "Failed to handle new order message: ${e.message}")
}
@ -132,22 +132,22 @@ object ServiceManager {
// Handle give up order messages
private fun giveUpOrder(jpushOrderBean : JpushBean) {
pushListener.get()?.giveUpOrder(jpushOrderBean)
pushListener?.giveUpOrder(jpushOrderBean)
}
// Handle revoke order messages
private fun revokeOrder(jpushOrderBean : JpushBean) {
pushListener.get()?.revokeOrder(jpushOrderBean)
pushListener?.revokeOrder(jpushOrderBean)
}
// Handle re-dispatch order messages
private fun reDispatchOrder(jpushOrderBean : JpushBean) {
pushListener.get()?.reDispatchOrder(jpushOrderBean)
pushListener?.reDispatchOrder(jpushOrderBean)
}
// Handle important tip messages
private fun importantTip(jpushOrderBean : JpushBean) {
pushListener.get()?.importantTip(jpushOrderBean)
pushListener?.importantTip(jpushOrderBean)
}
// Disconnect from JPush and MQTT

View File

@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
@ -50,7 +49,9 @@ import com.blankj.utilcode.util.ActivityUtils
import com.za.base.BaseActivity
import com.za.base.theme.bgColor
import com.za.base.view.CommonButton
import com.za.base.view.EmptyView
import com.za.base.view.HeadView
import com.za.base.view.LoadError
import com.za.bean.db.order.OrderInfo
import com.za.common.GlobalData
import com.za.common.util.DeviceUtil
@ -129,23 +130,35 @@ private fun ServicingMainScreen(jobCode : String? = null,
.fillMaxSize()
.background(bgColor)
.padding(it)) {
if (uiState.value.state == 2) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CommonButton(text = "订单获取异常,点击重新获取") {
vm.dispatch(ServicingMainVm.Action.Init(jobCode,
phone,
taskCode,
vehicleId,
deviceId,
rescueVehicle))
when (uiState.value.state) {
1 -> {
Box(modifier = Modifier
.fillMaxSize()
.padding(top = 10.dp),
contentAlignment = Alignment.TopCenter) {
OrderItemView(GlobalData.currentOrder)
}
}
} else {
Box(modifier = Modifier
.fillMaxSize()
.padding(top = 10.dp),
contentAlignment = Alignment.TopCenter) {
OrderItemView(GlobalData.currentOrder)
2 -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
LoadError(message = "订单获取异常", onRetry = {
vm.dispatch(ServicingMainVm.Action.Init(jobCode,
phone,
taskCode,
vehicleId,
deviceId,
rescueVehicle))
})
}
}
3 -> { //
EmptyView()
}
else -> {
}
}
}

View File

@ -46,11 +46,14 @@ class ServicingMainVm : BaseVm<ServicingMainVm.Action, ServicingMainVm.UiState>(
CommonMethod.queryOrderList(context = ActivityUtils.getTopActivity(),
success = { orderInfo : OrderInfo?, orderInfos : List<OrderInfo>? ->
LoadingManager.hideLoading()
if (orderInfo == null) {
ToastUtils.showShort("未查询到订单")
GlobalData.clearAllOrderCache()
updateState(uiState.value.copy(state = 3))
return@queryOrderList
}
GlobalData.currentOrder = orderInfo
updateState(uiState.value.copy(state = 1))
if (orderInfos.isNullOrEmpty()) {
ToastUtils.showShort("未查询到订单")
}
},
failed = {
updateState(uiState.value.copy(state = 2))
@ -106,6 +109,6 @@ class ServicingMainVm : BaseVm<ServicingMainVm.Action, ServicingMainVm.UiState>(
}
data class UiState(
val state : Int? = null, //1 成功 2 失败
val state : Int? = null, //1 成功 2 失败 3:订单为空
)
}

View File

@ -57,6 +57,7 @@ import coil.compose.AsyncImage
import com.amap.api.location.AMapLocationClient
import com.amap.api.maps.CameraUpdateFactory
import com.amap.api.maps.MapView
import com.amap.api.maps.model.BitmapDescriptorFactory
import com.amap.api.maps.model.LatLng
import com.amap.api.maps.model.LatLngBounds
import com.amap.api.maps.model.MarkerOptions
@ -234,18 +235,18 @@ private fun AcceptOrderScreen(jpushBean : JpushBean?, vm : NewOrderVm = viewMode
}
// 添加距离和时间信息
// 距离和时间信息
Row(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
Text(text = "预计到达: ${uiState.value.estimatedArrivalTime}",
color = Color(0xFF666666),
fontSize = 14.sp)
Text(text = if (uiState.value.estimatedArrivalTime.isNotEmpty()) "预计到达: ${uiState.value.estimatedArrivalTime}"
else "计算中...", color = Color(0xFF666666), fontSize = 14.sp)
Text(text = "总里程: %.1fkm".format(uiState.value.remainingDistance / 1000f),
color = Color(0xFFFF4D4F),
fontSize = 14.sp)
Text(text = if (uiState.value.remainingDistance > 0) "距离救援地: %.1fkm".format(
uiState.value.remainingDistance / 1000f)
else "计算中...", color = Color(0xFFFF4D4F), fontSize = 14.sp)
}
HorizontalDivider(modifier = Modifier
@ -256,8 +257,26 @@ private fun AcceptOrderScreen(jpushBean : JpushBean?, vm : NewOrderVm = viewMode
Column(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)) { // 订单标签
Text(text = uiState.value.jpushBean?.serviceTypeName ?: "",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black)
Spacer(modifier = Modifier.height(8.dp))
// 订单标签
Row(verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Box(modifier = Modifier
.background(Color(0xFF9BA1B2), RoundedCornerShape(4.dp))
.padding(horizontal = 6.dp, vertical = 2.dp)) {
Text(text = "月结".takeIf { uiState.value.jpushBean?.settleType == 1 }
?: "现金", color = Color.White, fontSize = 12.sp)
}
Text(text = uiState.value.jpushBean?.orderSource ?: "",
color = Color.Black,
fontSize = 12.sp)
Text(text = uiState.value.jpushBean?.addressProperty ?: "",
color = Color(0xFFFD8205),
@ -416,10 +435,9 @@ private fun AcceptOrderScreen(jpushBean : JpushBean?, vm : NewOrderVm = viewMode
// 绘制路线
uiState.value.routePoints?.let { points ->
if (points.isNotEmpty()) {
mapView.map.addPolyline(PolylineOptions().addAll(points).width(15f)
.color(Color(0xFF3D4B7C).toArgb()).zIndex(1f))
}
mapView.map.addPolyline(PolylineOptions().addAll(points).width(15f)
.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.icon_road_green_arrow))
.zIndex(1f))
}
// 添加标记点

View File

@ -157,7 +157,7 @@ class EleSignVm : IServicingVm<EleSignVm.Action, EleSignVm.UiState>() {
val photoList = RoomHelper.db?.eleCarDamagePhotoDao()?.getEleCarDamagePhotos(getCurrentOrder()?.taskId
?: 0)
updateState(uiState.value.copy(eleWorkOrderBean = it, damagePhoto = photoList, orderInfo = getCurrentOrder()))
LogUtil.print("电子表单更新车辆损伤照片", "eleWorkOrderBean==${photoList.toJson()}")
LogUtil.print("电子表单更新车辆损伤照片", "eleWorkOrderBean==${it.toJson()}")
},
failed = {
LoadingManager.hideLoading()

View File

@ -170,30 +170,33 @@ fun GoAccidentSiteScreen(vm : GoAccidentSiteVm = viewModel()) {
Column(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)) {
Row(modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
Text(text = uiState.value.orderInfo?.serviceTypeName ?: "",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically) {
Box(modifier = Modifier
.background(Color(0xFF9BA1B2), RoundedCornerShape(4.dp))
.padding(horizontal = 6.dp, vertical = 2.dp)) {
Text(text = "月结".takeIf { uiState.value.orderInfo?.settleType == 1 }
?: "现金", color = Color.White, fontSize = 12.sp)
}
Text(text = uiState.value.orderInfo?.serviceTypeName ?: "",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black)
Text(text = uiState.value.orderInfo?.addressProperty ?: "",
color = Color(0xFFFD8205),
fontSize = 12.sp,
fontWeight = FontWeight.Medium)
Spacer(modifier = Modifier.height(8.dp))
// 订单标签
Row(verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Box(modifier = Modifier
.background(Color(0xFF9BA1B2), RoundedCornerShape(4.dp))
.padding(horizontal = 6.dp, vertical = 2.dp)) {
Text(text = "月结".takeIf { uiState.value.orderInfo?.settleType == 1 }
?: "现金", color = Color.White, fontSize = 12.sp)
}
}
Text(text = uiState.value.orderInfo?.orderSource ?: "",
color = Color.Black,
fontSize = 12.sp)
Text(text = uiState.value.orderInfo?.addressProperty ?: "",
color = Color(0xFFFD8205),
fontSize = 12.sp,
fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.height(12.dp))
Row(verticalAlignment = Alignment.CenterVertically,
@ -333,7 +336,7 @@ fun GoAccidentSiteScreen(vm : GoAccidentSiteVm = viewModel()) {
// 添加当前位置标记
if (GlobalData.currentLocation != null) {
mapView.map.addMarker(MarkerOptions().position(LatLng(GlobalData.currentLocation?.latitude !!,
GlobalData.currentLocation?.longitude !!)).title("当前位置")
GlobalData.currentLocation?.longitude !!)).title("当前位置")
.icon(ImageUtil.vectorToBitmap(context, R.drawable.ic_current_location))
.anchor(0.5f, 0.5f).visible(true))
}
@ -342,7 +345,7 @@ fun GoAccidentSiteScreen(vm : GoAccidentSiteVm = viewModel()) {
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0) {
mapView.map.addMarker(MarkerOptions().position(LatLng(order.lat !!,
order.lng !!)).title("救援地点")
order.lng !!)).title("救援地点")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.anchor(0.5f, 0.5f))
}
@ -350,7 +353,7 @@ fun GoAccidentSiteScreen(vm : GoAccidentSiteVm = viewModel()) {
// 添加目的地标记
if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0) {
mapView.map.addMarker(MarkerOptions().position(LatLng(order.distLat !!,
order.distLng !!)).title("目的地")
order.distLng !!)).title("目的地")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_dist_map))
.anchor(0.5f, 0.5f))
}

View File

@ -34,6 +34,7 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
class GoAccidentSiteVm : IServicingVm<GoAccidentSiteVm.Action, GoAccidentSiteVm.UiState>() {
@ -136,7 +137,7 @@ class GoAccidentSiteVm : IServicingVm<GoAccidentSiteVm.Action, GoAccidentSiteVm.
updateState(uiState = uiState.value.copy(orderInfo = getCurrentOrder()))
buildMarkers(getCurrentOrder())
searchDrivingRoute(getCurrentOrder())
// dispatch(Action.StartTimer)
startTimer()
}
private fun buildMarkers(orderInfo : OrderInfo?) {
@ -182,14 +183,13 @@ class GoAccidentSiteVm : IServicingVm<GoAccidentSiteVm.Action, GoAccidentSiteVm.
val points = path.steps.flatMap { step ->
step.polyline.map { LatLng(it.latitude, it.longitude) }
}
val durationInSeconds = path.duration
val arrivalTime = SimpleDateFormat("HH:mm:ss",
Locale.getDefault()).format(System.currentTimeMillis() + durationInSeconds * 1000)
LogUtil.print("arrivalTime", "arrivalTime: arrivalTime=$arrivalTime")
LogUtil.print("distance", "distance: distance=${path.distance.div(1000)}km")
updateState(uiState.value.copy(routePoints = points, estimatedArrivalTime = arrivalTime, remainingDistance = path.distance))
val duration = path.duration
val arrivalTime = System.currentTimeMillis() + duration * 1000
val dateFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
val estimatedTime = dateFormat.format(Date(arrivalTime))
updateState(uiState.value.copy(routePoints = points,
estimatedArrivalTime = estimatedTime,
remainingDistance = path.distance))
} else {
LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode")
}
@ -233,16 +233,16 @@ class GoAccidentSiteVm : IServicingVm<GoAccidentSiteVm.Action, GoAccidentSiteVm.
private var timerJob : Job? = null
private fun startTimer() {
timerJob?.cancel()
timerJob = viewModelScope.launch {
while (isActive) {
val (distance, arrivalTime) = calculateRemainingDistance()
_uiState.update {
it.copy(remainingDistance = distance, estimatedArrivalTime = arrivalTime)
}
delay(1000)
}
}
// timerJob?.cancel()
// timerJob = viewModelScope.launch {
// while (isActive) {
// val (distance, arrivalTime) = calculateRemainingDistance()
// _uiState.update {
// it.copy(remainingDistance = distance, estimatedArrivalTime = arrivalTime)
// }
// delay(1000)
// }
// }
}
override fun onCleared() {

View File

@ -70,488 +70,370 @@ import com.za.servicing.R
import com.za.ui.servicing.view.InServicingHeadView
class GoToDestinationActivity : BaseActivity() {
@Composable
override fun ContentView() {
GoToDestinationScreen()
}
@Composable
override fun ContentView() {
GoToDestinationScreen()
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GoToDestinationScreen(vm: GoToDestinationVm = viewModel()) {
val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val mapView = remember { MapView(context) }
// 添加协程作用域
val scope = rememberCoroutineScope()
fun GoToDestinationScreen(vm : GoToDestinationVm = viewModel()) {
val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val mapView = remember { MapView(context) }
// 修改 BottomSheet 状态
val bottomSheetState = rememberStandardBottomSheetState(
initialValue = SheetValue.PartiallyExpanded,
confirmValueChange = { true }
)
val scaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = bottomSheetState
)
// 添加协程作用域
val scope = rememberCoroutineScope()
// 优化状态管理
val isExpanded by remember {
derivedStateOf { bottomSheetState.currentValue == SheetValue.Expanded }
}
// 修改 BottomSheet 状态
val bottomSheetState =
rememberStandardBottomSheetState(initialValue = SheetValue.PartiallyExpanded,
confirmValueChange = { true })
val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
// 记忆化常用值,减少重组
val orderInfo = remember(uiState.value.orderInfo) { uiState.value.orderInfo }
val estimatedTime = remember(uiState.value.estimatedArrivalTime) { uiState.value.estimatedArrivalTime }
val remainingDistance = remember(uiState.value.remainingDistance) { uiState.value.remainingDistance }
// 优化状态管理
val isExpanded by remember {
derivedStateOf { bottomSheetState.currentValue == SheetValue.Expanded }
}
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_CREATE -> {
mapView.onCreate(Bundle())
vm.dispatch(GoToDestinationVm.Action.Init)
}
// 记忆化常用值,减少重组
val orderInfo = remember(uiState.value.orderInfo) { uiState.value.orderInfo }
val estimatedTime =
remember(uiState.value.estimatedArrivalTime) { uiState.value.estimatedArrivalTime }
val remainingDistance =
remember(uiState.value.remainingDistance) { uiState.value.remainingDistance }
Lifecycle.Event.ON_RESUME -> mapView.onResume()
Lifecycle.Event.ON_PAUSE -> mapView.onPause()
Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
else -> {}
}
}
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_CREATE -> {
mapView.onCreate(Bundle())
vm.dispatch(GoToDestinationVm.Action.Init)
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
Lifecycle.Event.ON_RESUME -> mapView.onResume()
Lifecycle.Event.ON_PAUSE -> mapView.onPause()
Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
else -> {}
}
}
// 对话框处理
if (uiState.value.goNextPage != null) {
goNextPage(uiState.value.goNextPage?.nextState, context)
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
if (uiState.value.isGoNextPageDialog == true) {
CommonDialog(
cancelText = "取消",
confirmText = "前往下一步",
title = "是否前往下一步?",
cancelEnable = true,
cancel = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
},
dismiss = { vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false))) },
confirm = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
vm.dispatch(GoToDestinationVm.Action.UpdateTask)
}
)
}
// 对话框处理
if (uiState.value.goNextPage != null) {
goNextPage(uiState.value.goNextPage?.nextState, context)
}
if (uiState.value.showOfflineDialog == true) {
CommonDialog(
cancelText = "取消",
confirmText = "离线上传",
title = "任务提交失败,是否离线提交?",
cancelEnable = true,
cancel = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(showOfflineDialog = false)))
},
dismiss = { vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(showOfflineDialog = false))) },
confirm = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(showOfflineDialog = false)))
vm.dispatch(GoToDestinationVm.Action.UploadOffline)
}
)
}
if (uiState.value.isGoNextPageDialog == true) {
CommonDialog(cancelText = "取消",
confirmText = "前往下一步",
title = "是否前往下一步?",
cancelEnable = true,
cancel = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(
isGoNextPageDialog = false)))
},
dismiss = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(
isGoNextPageDialog = false)))
},
confirm = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(
isGoNextPageDialog = false)))
vm.dispatch(GoToDestinationVm.Action.UpdateTask)
})
}
BottomSheetScaffold(
scaffoldState = scaffoldState,
topBar = {
InServicingHeadView(
title = "作业完成,正在拖往目的地",
onBack = { context.finish() },
orderInfo = orderInfo
)
},
sheetContent = {
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState())
) {
// 滑动指示器
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.width(32.dp)
.height(4.dp)
.background(
color = Color(0xFFE0E0E0),
shape = RoundedCornerShape(2.dp)
)
)
}
if (uiState.value.showOfflineDialog == true) {
CommonDialog(cancelText = "取消",
confirmText = "离线上传",
title = "任务提交失败,是否离线提交?",
cancelEnable = true,
cancel = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(
showOfflineDialog = false)))
},
dismiss = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(
showOfflineDialog = false)))
},
confirm = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(
showOfflineDialog = false)))
vm.dispatch(GoToDestinationVm.Action.UploadOffline)
})
}
// 距离和时间信息
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (estimatedTime.isNotEmpty())
"预计到达: $estimatedTime"
else
"计算中...",
color = Color(0xFF666666),
fontSize = 14.sp
)
BottomSheetScaffold(scaffoldState = scaffoldState,
topBar = {
InServicingHeadView(title = "作业完成,正在拖往目的地",
onBack = { context.finish() },
orderInfo = orderInfo)
},
sheetContent = {
Column(modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState())) { // 滑动指示器
Box(modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
contentAlignment = Alignment.Center) {
Box(modifier = Modifier
.width(32.dp)
.height(4.dp)
.background(color = Color(0xFFE0E0E0), shape = RoundedCornerShape(2.dp)))
}
Text(
text = if (remainingDistance > 0)
"目的地距离: %.1fkm".format(remainingDistance / 1000f)
else
"计算中...",
color = Color(0xFFFF4D4F),
fontSize = 14.sp
)
}
// 距离和时间信息
Row(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
Text(text = if (estimatedTime.isNotEmpty()) "预计到达: $estimatedTime"
else "计算中...", color = Color(0xFF666666), fontSize = 14.sp)
// 使用 HorizontalDivider 替代 Box
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.alpha(0.1f)
)
Text(text = if (remainingDistance > 0) "目的地距离: %.1fkm".format(
remainingDistance / 1000f)
else "计算中...", color = Color(0xFFFF4D4F), fontSize = 14.sp)
}
// 订单信息
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = uiState.value.orderInfo?.serviceTypeName ?: "",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black
)
// 使用 HorizontalDivider 替代 Box
HorizontalDivider(modifier = Modifier
.fillMaxWidth()
.alpha(0.1f))
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.background(Color(0xFF9BA1B2), RoundedCornerShape(4.dp))
.padding(horizontal = 6.dp, vertical = 2.dp)
) {
Text(
text = "月结".takeIf { uiState.value.orderInfo?.settleType == 1 }
?: "现金",
color = Color.White,
fontSize = 12.sp
)
}
// 订单信息
Column(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)) {
Text(text = uiState.value.orderInfo?.serviceTypeName ?: "",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black)
Spacer(modifier = Modifier.height(8.dp))
// 订单标签
Row(verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Box(modifier = Modifier
.background(Color(0xFF9BA1B2), RoundedCornerShape(4.dp))
.padding(horizontal = 6.dp, vertical = 2.dp)) {
Text(text = "月结".takeIf { uiState.value.orderInfo?.settleType == 1 }
?: "现金", color = Color.White, fontSize = 12.sp)
}
Text(
text = uiState.value.orderInfo?.addressProperty ?: "",
color = Color(0xFFFD8205),
fontSize = 12.sp,
fontWeight = FontWeight.Medium
)
}
}
Text(text = uiState.value.orderInfo?.orderSource ?: "",
color = Color.Black,
fontSize = 12.sp)
Spacer(modifier = Modifier.height(12.dp))
Text(text = uiState.value.orderInfo?.addressProperty ?: "",
color = Color(0xFFFD8205),
fontSize = 12.sp,
fontWeight = FontWeight.Medium)
}
// 订单号
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { uiState.value.orderInfo?.taskCode?.copy(context) }
) {
Text(
text = "单号",
color = Color(0xFF999999),
fontSize = 13.sp
)
Spacer(modifier = Modifier.height(12.dp))
Spacer(modifier = Modifier.width(8.dp))
// 订单号
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable {
uiState.value.orderInfo?.taskCode?.copy(context)
}) {
Text(text = "单号", color = Color(0xFF999999), fontSize = 13.sp)
Text(
text = uiState.value.orderInfo?.taskCode ?: "",
color = Color(0xFF666666),
fontSize = 14.sp
)
Spacer(modifier = Modifier.width(8.dp))
Spacer(modifier = Modifier.width(8.dp))
Text(text = uiState.value.orderInfo?.taskCode ?: "",
color = Color(0xFF666666),
fontSize = 14.sp)
AsyncImage(
model = R.drawable.sv_copy,
contentDescription = "copy",
modifier = Modifier.size(16.dp)
)
}
Spacer(modifier = Modifier.width(8.dp))
Spacer(modifier = Modifier.height(12.dp))
AsyncImage(model = R.drawable.sv_copy,
contentDescription = "copy",
modifier = Modifier.size(16.dp))
}
// 地址信息
Column(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
// 救援地
Row(
verticalAlignment = Alignment.Top,
modifier = Modifier
.fillMaxWidth()
.clickable {
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 &&
order.lng != null && order.lng != 0.0
) {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(order.lat!!, order.lng!!),
16f
)
)
}
}
}
) {
AsyncImage(
model = R.drawable.sv_rescuing,
contentDescription = "rescue",
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Spacer(modifier = Modifier.width(8.dp))
// 地址信息
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { // 救援地
Row(verticalAlignment = Alignment.Top,
modifier = Modifier
.fillMaxWidth()
.clickable {
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0) {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngZoom(
LatLng(order.lat !!, order.lng !!),
16f))
}
}
}) {
AsyncImage(model = R.drawable.sv_rescuing,
contentDescription = "rescue",
modifier = Modifier.size(16.dp))
Text(
text = uiState.value.orderInfo?.address ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.weight(1f)
)
}
Spacer(modifier = Modifier.width(8.dp))
// 目的地
if (!uiState.value.orderInfo?.distAddress.isNullOrBlank()) {
Row(
verticalAlignment = Alignment.Top,
modifier = Modifier
.fillMaxWidth()
.clickable {
uiState.value.orderInfo?.let { order ->
if (order.distLat != null && order.distLat != 0.0 &&
order.distLng != null && order.distLng != 0.0
) {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(order.distLat!!, order.distLng!!),
16f
)
)
}
}
}
) {
AsyncImage(
model = R.drawable.sv_dist,
contentDescription = "destination",
modifier = Modifier.size(16.dp)
)
Text(text = uiState.value.orderInfo?.address ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.weight(1f))
}
Spacer(modifier = Modifier.width(8.dp))
// 目的地
if (! uiState.value.orderInfo?.distAddress.isNullOrBlank()) {
Row(verticalAlignment = Alignment.Top,
modifier = Modifier
.fillMaxWidth()
.clickable {
uiState.value.orderInfo?.let { order ->
if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0) {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngZoom(
LatLng(order.distLat !!, order.distLng !!),
16f))
}
}
}) {
AsyncImage(model = R.drawable.sv_dist,
contentDescription = "destination",
modifier = Modifier.size(16.dp))
Text(
text = uiState.value.orderInfo?.distAddress ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.weight(1f)
)
}
}
}
}
Spacer(modifier = Modifier.width(8.dp))
// 到达按钮 - 移除外层 Column简化布局
Button(
onClick = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = true)))
},
modifier = Modifier
.fillMaxWidth()
.height(44.dp)
.padding(horizontal = 16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = headBgColor
),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = "到达目的地",
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
Text(text = uiState.value.orderInfo?.distAddress ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.weight(1f))
}
}
}
}
Spacer(modifier = Modifier.height(16.dp))
}
},
sheetPeekHeight = 180.dp,
sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp),
sheetContainerColor = Color.White,
sheetDragHandle = null,
sheetSwipeEnabled = true
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
AndroidView(
modifier = Modifier
.fillMaxSize(),
factory = {
AMapLocationClient.updatePrivacyShow(context, true, true)
AMapLocationClient.updatePrivacyAgree(context, true)
mapView.apply {
map.apply {
isTrafficEnabled = false
isMyLocationEnabled = false
uiSettings.isMyLocationButtonEnabled = false
uiSettings.setLogoBottomMargin(-100)
uiSettings.isZoomControlsEnabled = false
// 到达按钮 - 移除外层 Column简化布局
Button(onClick = {
vm.dispatch(GoToDestinationVm.Action.UpdateState(uiState.value.copy(
isGoNextPageDialog = true)))
},
modifier = Modifier
.fillMaxWidth()
.height(44.dp)
.padding(horizontal = 16.dp),
colors = ButtonDefaults.buttonColors(containerColor = headBgColor),
shape = RoundedCornerShape(8.dp)) {
Text(text = "到达目的地",
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Medium)
}
// 添加标记点点击事件
setOnMarkerClickListener { marker ->
marker.showInfoWindow()
Handler(Looper.getMainLooper()).postDelayed({
marker.hideInfoWindow()
}, 800)
true
}
}
}
},
update = {
// 清除旧标记和路线
mapView.map.clear()
Spacer(modifier = Modifier.height(16.dp))
}
},
sheetPeekHeight = 180.dp,
sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp),
sheetContainerColor = Color.White,
sheetDragHandle = null,
sheetSwipeEnabled = true) { paddingValues ->
Box(modifier = Modifier
.fillMaxSize()
.padding(paddingValues)) {
AndroidView(modifier = Modifier.fillMaxSize(), factory = {
AMapLocationClient.updatePrivacyShow(context, true, true)
AMapLocationClient.updatePrivacyAgree(context, true)
mapView.apply {
map.apply {
isTrafficEnabled = false
isMyLocationEnabled = false
uiSettings.isMyLocationButtonEnabled = false
uiSettings.setLogoBottomMargin(- 100)
uiSettings.isZoomControlsEnabled = false
// 先绘制路线
uiState.value.routePoints?.let { points ->
mapView.map.addPolyline(
PolylineOptions()
.addAll(points)
.width(15f)
.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.icon_road_green_arrow))
.zIndex(1f)
)
}
// 添加标记点点击事件
setOnMarkerClickListener { marker ->
marker.showInfoWindow()
Handler(Looper.getMainLooper()).postDelayed({
marker.hideInfoWindow()
}, 800)
true
}
}
}
}, update = { // 清除旧标记和路线
mapView.map.clear()
// 再添加标记点,确保标记点在路线上层
// 添加当前位置标记
if (GlobalData.currentLocation != null) {
mapView.map.addMarker(
MarkerOptions()
.position(LatLng(
GlobalData.currentLocation?.latitude!!,
GlobalData.currentLocation?.longitude!!
))
.title("当前位置")
.icon(ImageUtil.vectorToBitmap(context,R.drawable.ic_current_location))
.anchor(0.5f, 0.5f)
.visible(true)
)
}
// 先绘制路线
uiState.value.routePoints?.let { points ->
mapView.map.addPolyline(PolylineOptions().addAll(points).width(15f)
.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.icon_road_green_arrow))
.zIndex(1f))
}
// 添加救援地标记
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 &&
order.lng != null && order.lng != 0.0
) {
mapView.map.addMarker(
MarkerOptions()
.position(LatLng(order.lat!!, order.lng!!))
.title("救援地点")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.anchor(0.5f, 0.5f)
)
}
// 添加标记点,确保标记点在路线上层
// 添加当前位置标记
if (GlobalData.currentLocation != null) {
mapView.map.addMarker(MarkerOptions().position(LatLng(GlobalData.currentLocation?.latitude !!,
GlobalData.currentLocation?.longitude !!)).title("当前位置")
.icon(ImageUtil.vectorToBitmap(context, R.drawable.ic_current_location))
.anchor(0.5f, 0.5f).visible(true))
}
// 添加目的地标记
if (order.distLat != null && order.distLat != 0.0 &&
order.distLng != null && order.distLng != 0.0
) {
mapView.map.addMarker(
MarkerOptions()
.position(LatLng(order.distLat!!, order.distLng!!))
.title("目的地")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_dist_map))
.anchor(0.5f, 0.5f)
)
}
}
// 添加救援地标记
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0) {
mapView.map.addMarker(MarkerOptions().position(LatLng(order.lat !!,
order.lng !!)).title("救援地点")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.anchor(0.5f, 0.5f))
}
// 调整地图显示范围
val bounds = LatLngBounds.Builder().apply {
// 添加当前位置
GlobalData.currentLocation?.let {
include(LatLng(it.latitude, it.longitude))
}
// 添加目的地标记
if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0) {
mapView.map.addMarker(MarkerOptions().position(LatLng(order.distLat !!,
order.distLng !!)).title("目的地")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_dist_map))
.anchor(0.5f, 0.5f))
}
}
// 添加救援地点和目的地
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 &&
order.lng != null && order.lng != 0.0
) {
include(LatLng(order.lat!!, order.lng!!))
}
// 调整地图显示范围
val bounds = LatLngBounds.Builder().apply { // 添加当前位置
GlobalData.currentLocation?.let {
include(LatLng(it.latitude, it.longitude))
}
if (order.distLat != null && order.distLat != 0.0 &&
order.distLng != null && order.distLng != 0.0
) {
include(LatLng(order.distLat!!, order.distLng!!))
}
}
}.build()
// 添加救援地点和目的地
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0) {
include(LatLng(order.lat !!, order.lng !!))
}
try {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngBounds(bounds, 100)
)
} catch (e: Exception) {
// 如果计算边界失败,则使用默认缩放级别
GlobalData.currentLocation?.let {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(it.latitude, it.longitude),
15f
)
)
}
}
}
)
}
}
if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0) {
include(LatLng(order.distLat !!, order.distLng !!))
}
}
}.build()
try {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100))
} catch (e : Exception) { // 如果计算边界失败,则使用默认缩放级别
GlobalData.currentLocation?.let {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(it.latitude,
it.longitude), 15f))
}
}
})
}
}
}

View File

@ -34,252 +34,239 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
class GoToDestinationVm : IServicingVm<GoToDestinationVm.Action, GoToDestinationVm.UiState>() {
private val _uiState = MutableStateFlow(UiState())
val uiState get() = _uiState
override fun updateState(uiState: UiState) {
_uiState.value = uiState
}
private val _uiState = MutableStateFlow(UiState())
val uiState get() = _uiState
override fun updateState(uiState : UiState) {
_uiState.value = uiState
}
override fun dispatch(action: Action) {
when (action) {
is Action.Init -> init()
is Action.UpdateTask -> updateTask()
is Action.UpdateState -> updateState(action.uiState)
is Action.UploadOffline -> updateOfflineTask()
is Action.StartTimer -> startTimer()
}
}
override fun dispatch(action : Action) {
when (action) {
is Action.Init -> init()
is Action.UpdateTask -> updateTask()
is Action.UpdateState -> updateState(action.uiState)
is Action.UploadOffline -> updateOfflineTask()
is Action.StartTimer -> startTimer()
}
}
private fun updateOfflineTask(taskRequest: UpdateTaskRequest? = null) {
LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(success = {
LoadingManager.hideLoading()
val temp = taskRequest ?: UpdateTaskRequest(
type = "ARRIVE_DEST",
taskId = GlobalData.currentOrder?.taskId,
userId = GlobalData.driverInfoBean?.userId,
vehicleId = GlobalData.driverInfoBean?.vehicleId,
flowType = GlobalData.currentOrder?.flowType,
currentState = GlobalData.currentOrder?.taskState,
offlineMode = 0,
operateTime = System.currentTimeMillis().toString(),
lat = it.latitude,
lng = it.longitude)
private fun updateOfflineTask(taskRequest : UpdateTaskRequest? = null) {
LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(success = {
LoadingManager.hideLoading()
val temp = taskRequest ?: UpdateTaskRequest(type = "ARRIVE_DEST",
taskId = GlobalData.currentOrder?.taskId,
userId = GlobalData.driverInfoBean?.userId,
vehicleId = GlobalData.driverInfoBean?.vehicleId,
flowType = GlobalData.currentOrder?.flowType,
currentState = GlobalData.currentOrder?.taskState,
offlineMode = 0,
operateTime = System.currentTimeMillis().toString(),
lat = it.latitude,
lng = it.longitude)
val offlineUpdateTaskBean = OfflineUpdateTaskBean(
type = temp.type,
taskId = temp.taskId,
flowType = temp.flowType,
userId = temp.userId,
vehicleId = temp.vehicleId,
currentState = temp.currentState,
offlineMode = 1,
operateTime = temp.operateTime,
updateTaskLat = temp.lat,
updateTaskLng = temp.lng,
taskCode = uiState.value.orderInfo?.taskCode,
userOrderId = uiState.value.orderInfo?.userOrderId,
offlineTitle = "救援车发车,正在赶往现场",
offlineType = 1)
insertOfflineTask(offlineUpdateTaskBean)
val offlineUpdateTaskBean = OfflineUpdateTaskBean(type = temp.type,
taskId = temp.taskId,
flowType = temp.flowType,
userId = temp.userId,
vehicleId = temp.vehicleId,
currentState = temp.currentState,
offlineMode = 1,
operateTime = temp.operateTime,
updateTaskLat = temp.lat,
updateTaskLng = temp.lng,
taskCode = uiState.value.orderInfo?.taskCode,
userOrderId = uiState.value.orderInfo?.userOrderId,
offlineTitle = "救援车发车,正在赶往现场",
offlineType = 1)
insertOfflineTask(offlineUpdateTaskBean)
updateOrder(getCurrentOrder()?.copy(taskState = getCurrentOrder()?.getNextStatus()))
updateState(uiState.value.copy(goNextPage = UpdateTaskBean(nextState = getCurrentOrder()?.taskState), orderInfo = getCurrentOrder()))
}, failed = {
LoadingManager.hideLoading()
ToastUtils.showShort(it)
})
}
updateOrder(getCurrentOrder()?.copy(taskState = getCurrentOrder()?.getNextStatus()))
updateState(uiState.value.copy(goNextPage = UpdateTaskBean(nextState = getCurrentOrder()?.taskState),
orderInfo = getCurrentOrder()))
}, failed = {
LoadingManager.hideLoading()
ToastUtils.showShort(it)
})
}
private fun updateTask() {
LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(success = {
LoadingManager.hideLoading()
val taskRequest = UpdateTaskRequest(
type = "ARRIVE_DEST",
taskId = GlobalData.currentOrder?.taskId,
userId = GlobalData.driverInfoBean?.userId,
vehicleId = GlobalData.driverInfoBean?.vehicleId,
flowType = GlobalData.currentOrder?.flowType,
currentState = GlobalData.currentOrder?.taskState,
offlineMode = 0,
operateTime = System.currentTimeMillis().toString(),
lat = it.latitude,
lng = it.longitude)
doUploadTask(request = taskRequest)
}, failed = {
LoadingManager.hideLoading()
ToastUtils.showShort(it)
})
}
private fun updateTask() {
LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(success = {
LoadingManager.hideLoading()
val taskRequest = UpdateTaskRequest(type = "ARRIVE_DEST",
taskId = GlobalData.currentOrder?.taskId,
userId = GlobalData.driverInfoBean?.userId,
vehicleId = GlobalData.driverInfoBean?.vehicleId,
flowType = GlobalData.currentOrder?.flowType,
currentState = GlobalData.currentOrder?.taskState,
offlineMode = 0,
operateTime = System.currentTimeMillis().toString(),
lat = it.latitude,
lng = it.longitude)
doUploadTask(request = taskRequest)
}, failed = {
LoadingManager.hideLoading()
ToastUtils.showShort(it)
})
}
private fun doUploadTask(request: UpdateTaskRequest) {
if (!getCurrentOrderOfflineTask().isNullOrEmpty()) {
updateOfflineTask(taskRequest = request)
return
}
LoadingManager.showLoading()
CommonMethod.updateTask(request, success = {
LoadingManager.hideLoading()
updateOrder(getCurrentOrder()?.copy(taskState = it?.nextState))
updateState(uiState.value.copy(goNextPage = it, orderInfo = getCurrentOrder()))
}, failed = { msg, code ->
LoadingManager.hideLoading()
ToastUtils.showShort(msg)
if (code == Const.NetWorkException) {
updateState(uiState.value.copy(showOfflineDialog = true))
}
LogUtil.print("$tag doUploadTask", "状态更新失败==${request.toJson()} msg==$msg")
})
}
private fun doUploadTask(request : UpdateTaskRequest) {
if (! getCurrentOrderOfflineTask().isNullOrEmpty()) {
updateOfflineTask(taskRequest = request)
return
}
LoadingManager.showLoading()
CommonMethod.updateTask(request, success = {
LoadingManager.hideLoading()
updateOrder(getCurrentOrder()?.copy(taskState = it?.nextState))
updateState(uiState.value.copy(goNextPage = it, orderInfo = getCurrentOrder()))
}, failed = { msg, code ->
LoadingManager.hideLoading()
ToastUtils.showShort(msg)
if (code == Const.NetWorkException) {
updateState(uiState.value.copy(showOfflineDialog = true))
}
LogUtil.print("$tag doUploadTask", "状态更新失败==${request.toJson()} msg==$msg")
})
}
private fun init() {
updateState(uiState = uiState.value.copy(orderInfo = getCurrentOrder()))
buildMarkers(getCurrentOrder())
searchDrivingRoute(getCurrentOrder())
// dispatch(Action.StartTimer)
}
private fun init() {
updateState(uiState = uiState.value.copy(orderInfo = getCurrentOrder()))
buildMarkers(getCurrentOrder())
searchDrivingRoute(getCurrentOrder()) // dispatch(Action.StartTimer)
}
private fun searchDrivingRoute(orderInfo: OrderInfo?) {
// 如果没有当前位置,则不进行路径规划
if (GlobalData.currentLocation == null) return
private fun searchDrivingRoute(orderInfo : OrderInfo?) { // 如果没有当前位置,则不进行路径规划
if (GlobalData.currentLocation == null) return
val currentPoint = LatLonPoint(
GlobalData.currentLocation?.latitude ?: 0.0,
GlobalData.currentLocation?.longitude ?: 0.0
)
val currentPoint = LatLonPoint(GlobalData.currentLocation?.latitude ?: 0.0,
GlobalData.currentLocation?.longitude ?: 0.0)
// 获取目的地坐标
val destPoint = if (orderInfo?.distLat != null && orderInfo.distLat != 0.0 &&
orderInfo.distLng != null && orderInfo.distLng != 0.0
) {
LatLonPoint(orderInfo.distLat!!, orderInfo.distLng!!)
} else null
// 获取目的地坐标
val destPoint =
if (orderInfo?.distLat != null && orderInfo.distLat != 0.0 && orderInfo.distLng != null && orderInfo.distLng != 0.0) {
LatLonPoint(orderInfo.distLat !!, orderInfo.distLng !!)
} else null
// 如果没有目的地,则不进行规划
if (destPoint == null) return
// 如果没有目的地,则不进行规划
if (destPoint == null) return
// 使用 Application Context 创建路由搜索对象
val routeSearch = RouteSearch(GlobalData.application)
routeSearch.setRouteSearchListener(object : RouteSearch.OnRouteSearchListener {
override fun onDriveRouteSearched(result: DriveRouteResult?, errorCode: Int) {
if (errorCode == 1000 && result != null && result.paths.isNotEmpty()) {
val path = result.paths[0]
val points = path.steps.flatMap { step ->
step.polyline.map { LatLng(it.latitude, it.longitude) }
}
val durationInSeconds = path.duration
val arrivalTime = SimpleDateFormat("HH:mm:ss",
Locale.getDefault()).format(System.currentTimeMillis() + durationInSeconds * 1000)
// 使用 Application Context 创建路由搜索对象
val routeSearch = RouteSearch(GlobalData.application)
routeSearch.setRouteSearchListener(object : RouteSearch.OnRouteSearchListener {
override fun onDriveRouteSearched(result : DriveRouteResult?, errorCode : Int) {
if (errorCode == 1000 && result != null && result.paths.isNotEmpty()) {
val path = result.paths[0]
val points = path.steps.flatMap { step ->
step.polyline.map { LatLng(it.latitude, it.longitude) }
}
val duration = path.duration
val arrivalTime = System.currentTimeMillis() + duration * 1000
val dateFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
val estimatedTime = dateFormat.format(Date(arrivalTime))
updateState(uiState.value.copy(routePoints = points,
estimatedArrivalTime = estimatedTime,
remainingDistance = path.distance))
} else {
LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode")
}
}
updateState(uiState.value.copy(routePoints = points, estimatedArrivalTime =arrivalTime, remainingDistance = path.distance ))
} else {
LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode")
}
}
override fun onBusRouteSearched(p0 : BusRouteResult?, p1 : Int) {}
override fun onWalkRouteSearched(p0 : WalkRouteResult?, p1 : Int) {}
override fun onRideRouteSearched(p0 : RideRouteResult?, p1 : Int) {}
})
override fun onBusRouteSearched(p0: BusRouteResult?, p1: Int) {}
override fun onWalkRouteSearched(p0: WalkRouteResult?, p1: Int) {}
override fun onRideRouteSearched(p0: RideRouteResult?, p1: Int) {}
})
// 规划当前位置到目的地的路线
val query = RouteSearch.FromAndTo(currentPoint, destPoint)
val driveQuery =
RouteSearch.DriveRouteQuery(query, RouteSearch.DrivingDefault, null, null, "")
routeSearch.calculateDriveRouteAsyn(driveQuery)
}
// 规划当前位置到目的地的路线
val query = RouteSearch.FromAndTo(currentPoint, destPoint)
val driveQuery = RouteSearch.DriveRouteQuery(query, RouteSearch.DrivingDefault, null, null, "")
routeSearch.calculateDriveRouteAsyn(driveQuery)
}
private fun calculateRemainingDistance() : Pair<Float, String> {
val currentLocation = GlobalData.currentLocation ?: return Pair(0f, "")
val orderInfo = _uiState.value.orderInfo ?: return Pair(0f, "")
private fun calculateRemainingDistance(): Pair<Float, String> {
val currentLocation = GlobalData.currentLocation ?: return Pair(0f, "")
val orderInfo = _uiState.value.orderInfo ?: return Pair(0f, "")
// 计算到目的地的距离
val distance = if (orderInfo.distLat != null && orderInfo.distLng != null) {
AMapUtils.calculateLineDistance(LatLng(currentLocation.latitude,
currentLocation.longitude), LatLng(orderInfo.distLat !!, orderInfo.distLng !!))
} else 0f
// 计算到目的地的距离
val distance = if (orderInfo.distLat != null && orderInfo.distLng != null) {
AMapUtils.calculateLineDistance(
LatLng(currentLocation.latitude, currentLocation.longitude),
LatLng(orderInfo.distLat!!, orderInfo.distLng!!)
)
} else 0f
if (distance <= 0f) {
return Pair(0f, "")
}
if (distance <= 0f) {
return Pair(0f, "")
}
// 计算预计到达时间假设平均速度40km/h
val timeInSeconds = ((distance / 1000.0 * 60.0 / 40.0) * 60).toInt()
val calendar = Calendar.getInstance()
calendar.add(Calendar.SECOND, timeInSeconds)
val arrivalTime = SimpleDateFormat("HH:mm", Locale.getDefault()).format(calendar.time)
// 计算预计到达时间假设平均速度40km/h
val timeInSeconds = ((distance / 1000.0 * 60.0 / 40.0) * 60).toInt()
val calendar = Calendar.getInstance()
calendar.add(Calendar.SECOND, timeInSeconds)
val arrivalTime = SimpleDateFormat("HH:mm", Locale.getDefault()).format(calendar.time)
return Pair(distance, arrivalTime)
}
return Pair(distance, arrivalTime)
}
private var timerJob : Job? = null
private var timerJob: Job? = null
private fun startTimer() {
timerJob?.cancel()
timerJob = viewModelScope.launch {
while (isActive) {
val (distance, arrivalTime) = calculateRemainingDistance()
_uiState.update {
it.copy(remainingDistance = distance, estimatedArrivalTime = arrivalTime)
}
delay(1000)
}
}
}
private fun startTimer() {
timerJob?.cancel()
timerJob = viewModelScope.launch {
while (isActive) {
val (distance, arrivalTime) = calculateRemainingDistance()
_uiState.update {
it.copy(
remainingDistance = distance,
estimatedArrivalTime = arrivalTime
)
}
delay(1000)
}
}
}
override fun onCleared() {
super.onCleared()
timerJob?.cancel()
}
override fun onCleared() {
super.onCleared()
timerJob?.cancel()
}
private fun buildMarkers(orderInfo : OrderInfo?) {
val markers = arrayListOf<MarkerOptions>()
if (orderInfo?.lat != null && orderInfo.lat != 0.0 && orderInfo.lng != null && orderInfo.lng != 0.0) { // 获取矢量图资源文件
val startMarkers =
MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.position(LatLng(orderInfo.lat !!, orderInfo.lng !!)).infoWindowEnable(false)
.visible(true)
markers.add(startMarkers)
}
private fun buildMarkers(orderInfo: OrderInfo?) {
val markers = arrayListOf<MarkerOptions>()
if (orderInfo?.lat != null && orderInfo.lat != 0.0 && orderInfo.lng != null && orderInfo.lng != 0.0) {
// 获取矢量图资源文件
val startMarkers = MarkerOptions()
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.position(LatLng(orderInfo.lat!!, orderInfo.lng!!))
.infoWindowEnable(false)
.visible(true)
markers.add(startMarkers)
}
if (orderInfo?.distLat != null && orderInfo.distLat != 0.0 && orderInfo.distLng != null && orderInfo.distLng != 0.0) {
val startMarkers =
MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_dist_map))
.position(LatLng(orderInfo.distLat !!, orderInfo.distLng !!)).visible(true)
markers.add(startMarkers)
}
updateState(uiState.value.copy(markers = markers))
}
if (orderInfo?.distLat != null && orderInfo.distLat != 0.0 && orderInfo.distLng != null && orderInfo.distLng != 0.0) {
val startMarkers = MarkerOptions()
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_dist_map))
.position(LatLng(orderInfo.distLat!!, orderInfo.distLng!!))
.visible(true)
markers.add(startMarkers)
}
updateState(uiState.value.copy(markers = markers))
}
sealed class Action {
data object Init : Action()
data object UpdateTask : Action()
data class UpdateState(val uiState : UiState) : Action()
data object UploadOffline : Action()
data object StartTimer : Action()
}
sealed class Action {
data object Init : Action()
data object UpdateTask : Action()
data class UpdateState(val uiState: UiState) : Action()
data object UploadOffline : Action()
data object StartTimer : Action()
}
data class UiState(
val orderInfo: OrderInfo? = null,
val showCallPhoneDialog: Boolean? = false,
val markers: ArrayList<MarkerOptions>? = null,
val goNextPage: UpdateTaskBean? = null,
val isGoNextPageDialog: Boolean? = null,
val showOfflineDialog: Boolean? = null,
val routePoints: List<LatLng>? = null,
val remainingDistance: Float = 0f,
val estimatedArrivalTime: String = ""
)
data class UiState(val orderInfo : OrderInfo? = null,
val showCallPhoneDialog : Boolean? = false,
val markers : ArrayList<MarkerOptions>? = null,
val goNextPage : UpdateTaskBean? = null,
val isGoNextPageDialog : Boolean? = null,
val showOfflineDialog : Boolean? = null,
val routePoints : List<LatLng>? = null,
val remainingDistance : Float = 0f,
val estimatedArrivalTime : String = "")
}

View File

@ -38,6 +38,7 @@ import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>() {
@ -155,7 +156,13 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
val points = path.steps.flatMap { step ->
step.polyline.map { LatLng(it.latitude, it.longitude) }
}
updateState(uiState.value.copy(routePoints = points))
val duration = path.duration
val arrivalTime = System.currentTimeMillis() + duration * 1000
val dateFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
val estimatedTime = dateFormat.format(Date(arrivalTime))
updateState(uiState.value.copy(routePoints = points,
estimatedArrivalTime = estimatedTime,
remainingDistance = path.distance))
} else {
LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode")
}
@ -176,16 +183,16 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
private var timerJob : Job? = null
private fun startTimer() {
timerJob?.cancel()
timerJob = viewModelScope.launch {
while (isActive) { // 计算从当前位置到目标点的距离和到达时间
val (distance, arrivalTime) = calculateRemainingDistance()
_uiState.update {
it.copy(remainingDistance = distance, estimatedArrivalTime = arrivalTime)
}
delay(1000) // 每秒更新一次
}
}
// timerJob?.cancel()
// timerJob = viewModelScope.launch {
// while (isActive) { // 计算从当前位置到目标点的距离和到达时间
// val (distance, arrivalTime) = calculateRemainingDistance()
// _uiState.update {
// it.copy(remainingDistance = distance, estimatedArrivalTime = arrivalTime)
// }
// delay(1000) // 每秒更新一次
// }
// }
}
private fun calculateRemainingDistance() : Pair<Float, String> {

View File

@ -5,7 +5,6 @@ import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
@ -51,5 +50,5 @@ fun SignatureView(modifier: Modifier = Modifier, success: (String?) -> Unit, ser
intent.putExtra("lineLength", 10) //每行显示字数(超出屏幕支持横向滚动)
signatureLauncher.launch(intent)
},
contentScale = ContentScale.FillBounds)
contentScale = ContentScale.Inside)
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Dealer" parent="Theme.AppCompat.Light.NoActionBar" />
<style name="Theme.Zd_sdk_demo" parent="Theme.Dealer" />
<style name="Theme.Zd_sdk_demo" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.Dealer" parent="Theme.AppCompat.Light.NoActionBar" />
</resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">sino-assist.com</domain>
</domain-config>
</network-security-config>