feat(network): 优化网络异常处理和环境切换

- 新增网络异常统一处理逻辑
- 实现环境切换功能
- 更新 API接口
- 重构部分代码以提高可维护性
This commit is contained in:
songzhiling
2025-05-21 15:20:52 +08:00
parent 502e1cd604
commit f29cac2d73
39 changed files with 2450 additions and 1026 deletions

View File

@ -27,7 +27,7 @@ class MainActivity : ComponentActivity() {
.fillMaxSize() .fillMaxSize()
.clickable { .clickable {
val uri = val uri =
"zd.assist://app?taskCode=ZD250512100030&driverName=宋志领&driverPhone=17630035658&rescueVehicle=沪88888".toUri() "zd.assist://app?taskCode=ZD240823100724&driverName=宋志领&driverPhone=17630035658&rescueVehicle=沪88888".toUri()
val intent = Intent(Intent.ACTION_VIEW, uri) val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent) startActivity(intent)
} }

View File

@ -10,6 +10,7 @@ fastjson = "1.2.69"
glide = "4.16.0" glide = "4.16.0"
gson = "2.11.0" gson = "2.11.0"
jcore = "3.3.2" jcore = "3.3.2"
faceDetection = "16.1.7"
jpush = "4.8.1" jpush = "4.8.1"
location = "5.6.1" location = "5.6.1"
loggingInterceptor = "4.11.0" loggingInterceptor = "4.11.0"
@ -100,6 +101,7 @@ utilcodex = { module = "com.blankj:utilcodex", version.ref = "utilcodex" }
xdmap = { module = "com.amap.api:3dmap", version.ref = "xdmap" } xdmap = { module = "com.amap.api:3dmap", version.ref = "xdmap" }
xz = { module = "org.tukaani:xz", version.ref = "xz" } xz = { module = "org.tukaani:xz", version.ref = "xz" }
androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" } androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" }
face-detection = { module = "com.google.mlkit:face-detection", version.ref = "faceDetection" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }

View File

@ -73,7 +73,7 @@ publishing {
release(MavenPublication) { release(MavenPublication) {
groupId = 'io.github.szl9' groupId = 'io.github.szl9'
artifactId = 'zd_servicing' artifactId = 'zd_servicing'
version = "1.0.1.9.9.68" version = "1.0.1.9.9.100"
pom { pom {
packaging = "aar" packaging = "aar"
@ -219,5 +219,7 @@ dependencies {
api libs.org.eclipse.paho.client.mqttv3 api libs.org.eclipse.paho.client.mqttv3
api libs.org.eclipse.paho.android.service api libs.org.eclipse.paho.android.service
api libs.face.detection
} }

View File

@ -77,10 +77,19 @@
</queries> </queries>
<application <application
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:targetApi="24"> tools:targetApi="24">
<activity
android:name="com.za.base.NetworkRouteSelectionActivity"
android:exported="true"
android:theme="@style/Theme.Dealer" />
<activity
android:name="com.za.ui.camera.ServicePeopleRealActivity"
android:exported="true"
android:theme="@style/Theme.Dealer" />
<activity <activity
android:name="com.za.ui.servicing.inservice_people_confirm.ServicePeopleConfirmActivity" android:name="com.za.ui.servicing.inservice_people_confirm.ServicePeopleConfirmActivity"
android:exported="true" android:exported="true"

View File

@ -1,98 +1,152 @@
package com.za.base package com.za.base
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.net.RetrofitHelper
object AppConfig { object AppConfig {
var isRelease = false var isRelease = false
// API 相关地址 // API 相关地址
lateinit var BASE_URL: String // API 主地址 lateinit var BASE_URL : String // API 主地址
lateinit var IMG_BASE_URL: String // 图片服务器地址 lateinit var IMG_BASE_URL : String // 图片服务器地址
lateinit var Resource_URL: String // 资源服务器地址 lateinit var Resource_URL : String // 资源服务器地址
// H5 相关地址
lateinit var TRAIN_URL: String // 培训文档地址
lateinit var DOCMENT_URL: String // 中道资料地址
/** // H5 相关地址
* 正式环境配置 lateinit var trainUrl : String // 培训文档地址
*/ lateinit var documentUrl : String // 中道资料地址
fun release() { lateinit var newDriverTrainUrl : String // 新司机培训地址
isRelease = true
// API 配置
BASE_URL = "https://api.sinoassist.com"
IMG_BASE_URL = "https://api.sinoassist.com"
Resource_URL = "https://www.sinoassist.com/res"
// H5 配置 fun init(isRelease : Boolean? = false) {
TRAIN_URL = "https://www.sinoassist.com/h5/supplier/dispatch/diverTrainDocment" val envType = GlobalData.networkEnv
DOCMENT_URL = "https://www.sinoassist.com/h5/supplier/dispatch/docmentList" if (isRelease == true) {
} when (envType) {
Const.NetEnv.Main -> release()
Const.NetEnv.Review -> review()
}
} else {
when (envType) {
Const.NetEnv.CRM1 -> crm1()
Const.NetEnv.CRM2 -> crm2()
Const.NetEnv.UAT -> uat()
}
}
}
/** fun changeEnv(envType : Int) {
* 审核环境配置 GlobalData.networkEnv = envType
*/ when (envType) {
fun review() { Const.NetEnv.Main -> release()
isRelease = true Const.NetEnv.Review -> review()
Const.NetEnv.CRM1 -> crm1()
Const.NetEnv.CRM2 -> crm2()
Const.NetEnv.UAT -> uat()
}
RetrofitHelper.reset()
}
// API 配置
BASE_URL = "http://interface.review.sino-assist.com"
IMG_BASE_URL = "http://interface.review.sino-assist.com"
Resource_URL = "https://www.sinoassist.com/res"
}
/** fun release() {
* CRM1 环境配置 isRelease = true // API 配置
*/ GlobalData.networkEnv = Const.NetEnv.Main
fun crm1() { BASE_URL = "https://api.sinoassist.com"
isRelease = false IMG_BASE_URL = "https://api.sinoassist.com"
Resource_URL = "https://www.sinoassist.com/res"
// API 配置 // H5 配置
BASE_URL = "https://api1.sino-assist.com" trainUrl = "https://www.sinoassist.com/h5/supplier/dispatch/diverTrainDocment"
IMG_BASE_URL = "https://api1.sino-assist.com" documentUrl = "https://www.sinoassist.com/h5/supplier/dispatch/docmentList"
Resource_URL = "https://crm1.sino-assist.com/res" newDriverTrainUrl = "https://www.sinoassist.com/h5/supplier/dispatch/driverTrainingList";
}
// H5 配置
TRAIN_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/diverTrainDocment"
DOCMENT_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/docmentList"
}
/** /**
* CRM2 环境配置 * 审核环境配置
*/ */
fun crm2() { private fun review() {
isRelease = false isRelease = true // API 配置
GlobalData.networkEnv = Const.NetEnv.Review
// API 配置 // API 配置
BASE_URL = "https://api2.sino-assist.com" BASE_URL = "http://interface.review.sino-assist.com"
IMG_BASE_URL = "https://api2.sino-assist.com" IMG_BASE_URL = "http://interface.review.sino-assist.com"
Resource_URL = "https://crm2.sino-assist.com/res" Resource_URL = "https://www.sinoassist.com/res"
} documentUrl = "http://interface.review.sino-assist.com/h5/supplier/dispatch/docmentList"
trainUrl = "http://interface.review.sino-assist.com/h5/supplier/dispatch/diverTrainDocment"
newDriverTrainUrl =
"http://interface.review.sino-assist.com/h5/supplier/dispatch/driverTrainingList"
}
/** /**
* 获取培训文档完整地址 * CRM1 环境配置
* @param driverId 司机ID */
* @param keyword 关键字 fun crm1() {
* @return 完整的培训文档URL isRelease = false
*/ GlobalData.networkEnv = Const.NetEnv.CRM1
fun getTrainUrl(keyWord: String = ""): String {
if (keyWord.isEmpty()) {
return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
}
return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord"
}
/** // API 配置
* 获取中道资料完整地址 BASE_URL = "https://api1.sino-assist.com"
* @param driverId 司机ID IMG_BASE_URL = "https://api1.sino-assist.com"
* @param keyword 关键字 Resource_URL = "https://crm1.sino-assist.com/res"
* @return 完整的中道资料URL
*/ documentUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/docmentList";
fun getDocmentUrl(keyWord: String = ""): String { trainUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/diverTrainDocment";
if (keyWord.isEmpty()) { newDriverTrainUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/driverTrainingList";
return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}" }
}
return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord" /**
} * CRM2 环境配置
} */
fun crm2() {
isRelease = false
GlobalData.networkEnv = Const.NetEnv.CRM2
// API 配置
BASE_URL = "https://api2.sino-assist.com"
IMG_BASE_URL = "https://api2.sino-assist.com"
Resource_URL = "https://crm2.sino-assist.com/res"
}
fun uat() {
isRelease = false
GlobalData.networkEnv = Const.NetEnv.UAT
BASE_URL = "https://api-uat.sino-assist.com" //crm2
IMG_BASE_URL = "https://api-uat.sino-assist.com"
Resource_URL = "https://uat.sino-assist.com/res"
documentUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/docmentList"
trainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/diverTrainDocment"
newDriverTrainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/driverTrainingList"
documentUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/docmentList";
trainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/diverTrainDocment";
newDriverTrainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/driverTrainingList";
}
/**
* 获取培训文档完整地址
* @param driverId 司机ID
* @param keyword 关键字
* @return 完整的培训文档URL
*/
fun getTrainUrl(keyWord : String = "") : String {
if (keyWord.isEmpty()) {
return trainUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
}
return trainUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord"
}
/**
* 获取中道资料完整地址
* @param driverId 司机ID
* @param keyword 关键字
* @return 完整的中道资料URL
*/
fun getDocmentUrl(keyWord : String = "") : String {
if (keyWord.isEmpty()) {
return documentUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
}
return documentUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord"
}
}

View File

@ -42,4 +42,12 @@ object Const {
const val ORDER_DETAIL = 2 //案件详情 const val ORDER_DETAIL = 2 //案件详情
const val ORDER_GIVE_UP = 3 //订单放弃 const val ORDER_GIVE_UP = 3 //订单放弃
} }
object NetEnv {
const val Main = 0 //正线
const val Review = 1 //正线
const val CRM1 = 2 //测试环境
const val CRM2 = 3 //测试环境
const val UAT = 4 //测试环境
}
} }

View File

@ -0,0 +1,442 @@
package com.za.base
import android.Manifest
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.telephony.PhoneStateListener
import android.telephony.SignalStrength
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.app.ActivityCompat
import com.blankj.utilcode.util.AppUtils
import com.za.base.theme.bgColor
import com.za.base.view.HeadView
import com.za.bean.BaseResponse
import com.za.bean.UpdateVersionBean
import com.za.bean.UpdateVersionRequest
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.ext.finish
import com.za.net.BaseObserver
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.POST
import java.util.concurrent.TimeUnit
data class Route(val name : String,
val url : String,
val id : String = name,
val type : Int) // 添加id字段用于唯一标识
// 定义所有可用线路
val availableRoutes = if (AppConfig.isRelease) {
listOf(
Route("主线路", "https://api.sinoassist.com", "main", type = Const.NetEnv.Main),
Route("备用线路",
"http://interface.review.sino-assist.com",
"review",
type = Const.NetEnv.Review),
)
} else {
listOf(Route("CRM1", "https://api1.sino-assist.com", "crm1", type = Const.NetEnv.CRM1),
Route("CRM2", "https://api2.sino-assist.com", "crm2", type = Const.NetEnv.CRM2),
Route("UAT", "https://api-uat.sino-assist.com", "uat", type = Const.NetEnv.UAT))
}
// 定义检测状态枚举
enum class DetectionStatus {
UNKNOWN, // 未检测
CHECKING, // 检测中
NORMAL, // 正常
ABNORMAL // 异常
}
class NetworkRouteSelectionActivity : BaseActivity() {
@Composable
override fun ContentView() {
NetworkRouteScreen(telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager,
connectivityManager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager)
}
}
@Composable
fun NetworkInfo(telephonyManager : TelephonyManager, connectivityManager : ConnectivityManager) {
val context = LocalContext.current
var networkType by remember { mutableStateOf("") }
var signalStrength by remember { mutableIntStateOf(- 1) }
var operatorName by remember { mutableStateOf("") }
val scope = rememberCoroutineScope()
// 更新网络信息
LaunchedEffect(Unit) {
if (ActivityCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
) { // 获取运营商名称
operatorName = telephonyManager.networkOperatorName
// 使用新的 API 获取网络类型
val networkCapabilities =
connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
networkType = when {
networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true -> "WiFi"
networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
when (telephonyManager.dataNetworkType) {
TelephonyManager.NETWORK_TYPE_LTE -> "4G"
TelephonyManager.NETWORK_TYPE_NR -> "5G"
TelephonyManager.NETWORK_TYPE_UMTS -> "3G"
TelephonyManager.NETWORK_TYPE_EDGE -> "2G"
else -> "未知"
}
} else {
"未知"
}
}
else -> "无网络"
}
// 使用新的 TelephonyCallback API 监听信号强度变化
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val callback =
object : TelephonyCallback(), TelephonyCallback.SignalStrengthsListener {
override fun onSignalStrengthsChanged(signalStrengths : SignalStrength) {
scope.launch(Dispatchers.Main) {
signalStrength = signalStrengths.level
LogUtil.print("NetworkInfo", "signalStrength: $signalStrength")
}
}
}
telephonyManager.registerTelephonyCallback(context.mainExecutor, callback)
} else {
@Suppress("DEPRECATION") telephonyManager.listen(object : PhoneStateListener() {
override fun onSignalStrengthsChanged(signalStrengths : SignalStrength) {
super.onSignalStrengthsChanged(signalStrengths)
scope.launch(Dispatchers.Main) {
signalStrength = signalStrengths.level
LogUtil.print("NetworkInfo", "signalStrength: $signalStrength")
}
}
}, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS)
}
}
}
Card(modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.elevatedCardElevation(4.dp)) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "当前手机网络",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF2E7D32))
Spacer(modifier = Modifier.height(8.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Column {
Text("运营商:$operatorName", fontSize = 16.sp)
Text("网络类型:$networkType", fontSize = 16.sp)
Text(text = "信号强度:${
when (signalStrength) {
0 -> "无信号"
1 -> ""
2 -> "一般"
3 -> "良好"
4 -> "极好"
else -> "未知"
}
}", fontSize = 16.sp)
}
}
}
}
}
@Composable
fun RouteItem(
route : Route,
isSelected : Boolean,
onSelect : () -> Unit,
) {
var detectionStatus by remember { mutableStateOf(DetectionStatus.UNKNOWN) }
var errorMessage by remember { mutableStateOf<String?>(null) }
var isDetecting by remember { mutableStateOf(false) }
var detectionTime by remember { mutableStateOf(0L) }
// 检测函数
fun detectRoute() {
if (isDetecting) return
isDetecting = true
detectionStatus = DetectionStatus.CHECKING
val startTime = System.currentTimeMillis()
try {
val request = UpdateVersionRequest(appVersion = AppUtils.getAppVersionName())
NetWorkRetrofit.getDefaultService(route.url).checkConnection(request)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<UpdateVersionBean>() {
override fun doFailure(code : Int, msg : String?) {
detectionTime = System.currentTimeMillis() - startTime
if (code == 999) {
detectionStatus = DetectionStatus.ABNORMAL
errorMessage = msg
} else {
detectionStatus = DetectionStatus.NORMAL
}
isDetecting = false
}
override fun doSuccess(it : UpdateVersionBean?) {
detectionTime = System.currentTimeMillis() - startTime
detectionStatus = DetectionStatus.NORMAL
isDetecting = false
}
})
} catch (e : Exception) {
detectionTime = System.currentTimeMillis() - startTime
detectionStatus = DetectionStatus.ABNORMAL
errorMessage = e.message ?: "连接失败"
isDetecting = false
}
}
// 初始检测
LaunchedEffect(Unit) {
detectRoute()
}
Column {
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable { onSelect() }
.padding(vertical = 5.dp)
.border(width = if (isSelected) 1.dp else 0.dp,
color = if (isSelected) MaterialTheme.colorScheme.primary else Color.Transparent,
shape = RoundedCornerShape(4.dp))
.padding(8.dp)) {
RadioButton(selected = isSelected, onClick = { onSelect() })
Text(text = route.name,
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal,
modifier = Modifier.padding(start = 8.dp))
Spacer(Modifier.weight(1f))
if (isDetecting) {
CircularProgressIndicator(modifier = Modifier.size(16.dp), strokeWidth = 2.dp)
Spacer(modifier = Modifier.width(8.dp))
}
val statusText = when (detectionStatus) {
DetectionStatus.NORMAL -> "正常 (${detectionTime}ms)"
DetectionStatus.ABNORMAL -> "异常 (${detectionTime}ms)"
DetectionStatus.CHECKING -> "检测中..."
DetectionStatus.UNKNOWN -> ""
}
val statusColor = when (detectionStatus) {
DetectionStatus.NORMAL -> Color(0xFF2E7D32)
DetectionStatus.ABNORMAL -> Color(0xFFC62828)
DetectionStatus.CHECKING -> Color(0xFF1565C0)
DetectionStatus.UNKNOWN -> Color.Gray
}
Text(text = statusText,
color = statusColor,
fontSize = 14.sp,
modifier = Modifier.padding(horizontal = 8.dp))
Box(modifier = Modifier
.clickable(enabled = ! isDetecting) { detectRoute() }
.background(color = MaterialTheme.colorScheme.primary,
shape = RoundedCornerShape(5.dp))
.padding(vertical = 5.dp, horizontal = 10.dp)) {
Text("检测", fontSize = 12.sp, color = Color.White)
}
}
// 显示错误信息(如果有)
if (detectionStatus == DetectionStatus.ABNORMAL && errorMessage != null) {
Text(text = "错误: $errorMessage",
color = Color(0xFFC62828),
fontSize = 12.sp,
modifier = Modifier.padding(start = 40.dp, bottom = 8.dp))
}
}
}
@Composable
fun NetworkRouteScreen(telephonyManager : TelephonyManager,
connectivityManager : ConnectivityManager) {
// 原有的线路选择部分
val context = LocalContext.current
var selectedRoute by remember { mutableStateOf(availableRoutes.find { GlobalData.networkEnv == it.type }) }
var showApplyDialog by remember { mutableStateOf(false) }
// 应用当前选中的线路
fun applySelectedRoute() {
selectedRoute?.let {
showApplyDialog = false
GlobalData.networkEnv = it.type
AppConfig.changeEnv(it.type)
}
}
// 显示确认对话框
if (showApplyDialog) {
AlertDialog(onDismissRequest = { showApplyDialog = false },
title = { Text("确认应用线路") },
text = { Text("确定要将当前线路切换为 ${selectedRoute?.name} 吗?") },
confirmButton = {
Button(onClick = { applySelectedRoute() }) {
Text("确定")
}
},
dismissButton = {
OutlinedButton(onClick = { showApplyDialog = false }) {
Text("取消")
}
})
}
Scaffold(topBar = { HeadView(title = "线路选择", onBack = { context.finish() }) }) {
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(state = rememberScrollState())
.padding(it)
.background(color = bgColor)
.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally) { // 标题卡片
NetworkInfo(telephonyManager, connectivityManager)
// 线路选择区域
Card(modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.elevatedCardElevation(4.dp)) {
Column(modifier = Modifier.padding(16.dp)) {
Text("选择线路",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(bottom = 8.dp))
Divider()
availableRoutes.forEachIndexed { index, route ->
RouteItem(route = route,
isSelected = route == selectedRoute,
onSelect = { selectedRoute = route })
if (index < availableRoutes.size - 1) {
Divider()
}
}
}
}
// 应用按钮
Button(onClick = { showApplyDialog = true },
enabled = selectedRoute != null,
modifier = Modifier.fillMaxWidth()) {
Icon(Icons.Default.Check, contentDescription = null)
Spacer(modifier = Modifier.width(4.dp))
Text("应用选中线路")
}
}
}
}
// 定义API接口
private interface RouteCheckApi {
@POST("/driverApp/base/appVersion")
fun checkConnection(@Body versionRequest : UpdateVersionRequest) : Observable<BaseResponse<UpdateVersionBean>>
}
// 在类的外部定义Retrofit管理器
private object NetWorkRetrofit {
private val client =
OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS)
.build()
private val retrofitMap = mutableMapOf<String, Retrofit>()
private fun getRetrofit(baseUrl : String) : Retrofit {
return retrofitMap.getOrPut(baseUrl) {
Retrofit.Builder().baseUrl(baseUrl).client(client)
.addConverterFactory(GsonConverterFactory.create()) // Add this line
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()).build()
}
}
fun getDefaultService(baseUrl : String) : RouteCheckApi {
return getRetrofit(baseUrl).create(RouteCheckApi::class.java)
}
fun clearCache() {
retrofitMap.clear()
}
}

View File

@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
@ -31,21 +30,14 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.za.base.NetworkRouteSelectionActivity
import com.za.base.theme.headBgColor import com.za.base.theme.headBgColor
import com.za.base.theme.white95
import com.za.bean.request.ReadTrainingCountRequest
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.ext.navigationActivity
import com.za.net.BaseObserver
import com.za.net.RetrofitHelper
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
val warnBean = mutableStateOf<IWarnBean?>(null) val warnBean = mutableStateOf<IWarnBean?>(null)
@ -60,59 +52,19 @@ fun AppTipsView() {
when (warnBean) { when (warnBean) {
is NetWarnBean -> { is NetWarnBean -> {
// NetTipView(warnBean as NetWarnBean) NetworkWeakView(warnBean as NetWarnBean)
}
is ReadTrainingCountBean -> {
// TrainingDocView(warnBean as ReadTrainingCountBean)
} }
else -> {} else -> {}
} }
} }
@Composable
fun NetTipView(tipsBean : NetWarnBean) {
Row(modifier = Modifier
.fillMaxWidth()
.height(20.dp)
.background(color = Color.Yellow)
.padding(top = 20.dp, start = 20.dp, end = 20.dp, bottom = 5.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center) {
Text(tipsBean.message, color = white95)
}
}
@Composable @Composable
fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) { fun NetworkWeakView(netWarnBean : NetWarnBean) {
val showTrainingDialog = remember { mutableStateOf(false) } val context = LocalContext.current
if (showTrainingDialog.value) { AnimatedVisibility(context !is NetworkRouteSelectionActivity,
CommonDialog(title = "培训提醒",
message = buildAnnotatedString {
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, fontSize = 16.sp)) {
append("重要提示:\n")
}
withStyle(style = SpanStyle(fontSize = 14.sp)) {
append("您有未完成的培训任务,为确保工作顺利开展,请尽快完成培训。\n\n")
}
withStyle(style = SpanStyle(color = Color(0xFFE65100),
fontSize = 15.sp,
fontWeight = FontWeight.Medium)) {
append("剩余培训任务:${readTrainingCountBean.mustReadTrainingCount ?: 0}")
}
}.toString(),
confirmText = "立即前往",
cancelText = "稍后提醒",
cancelEnable = true,
confirm = {
showTrainingDialog.value = false
},
dismiss = { showTrainingDialog.value = false })
}
AnimatedVisibility(visible = true,
modifier = Modifier.background(color = headBgColor), modifier = Modifier.background(color = headBgColor),
enter = fadeIn(animationSpec = tween(300)) + expandVertically(animationSpec = tween(300), enter = fadeIn(animationSpec = tween(300)) + expandVertically(animationSpec = tween(300),
expandFrom = Alignment.Top), expandFrom = Alignment.Top),
@ -138,11 +90,12 @@ fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) {
modifier = Modifier.size(24.dp)) modifier = Modifier.size(24.dp))
} }
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text(text = "培训提醒", Text(text = "提醒",
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold), style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
color = Color(0xFF424242), color = Color(0xFF424242),
fontSize = 14.sp) fontSize = 14.sp)
Text(text = "您有 ${readTrainingCountBean.mustReadTrainingCount ?: 0} 个培训任务待完成",
Text(text = "当前线路异常,是否前往切换线路",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF757575), color = Color(0xFF757575),
fontSize = 12.sp) fontSize = 12.sp)
@ -150,18 +103,20 @@ fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) {
} }
Box(modifier = Modifier Box(modifier = Modifier
.clickable {} .clickable {
context.navigationActivity(NetworkRouteSelectionActivity::class.java)
}
.background(color = Color(0xFFFF9800), shape = RoundedCornerShape(5.dp)) .background(color = Color(0xFFFF9800), shape = RoundedCornerShape(5.dp))
.padding(horizontal = 10.dp, vertical = 5.dp), .padding(horizontal = 10.dp, vertical = 5.dp),
contentAlignment = Alignment.Center) { contentAlignment = Alignment.Center) {
Text("去完成", color = Color.White, style = MaterialTheme.typography.labelLarge) Text("去完成", color = Color.White, style = MaterialTheme.typography.labelLarge)
} }
} }
} }
} }
} }
private fun fetchAppTipsData() { private fun fetchAppTipsData() {
if (GlobalData.currentOrder != null) { if (GlobalData.currentOrder != null) {
return return
@ -169,20 +124,6 @@ private fun fetchAppTipsData() {
if (GlobalData.token == null) { if (GlobalData.token == null) {
return return
} }
val request = ReadTrainingCountRequest(driverId = GlobalData.driverInfoBean?.userId?.toInt(),
userId = GlobalData.driverInfoBean?.userId,
supplierId = GlobalData.driverInfoBean?.supplierId.toString())
RetrofitHelper.getDefaultService().getReadTrainingCount(request).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<ReadTrainingCountBean>() {
override fun doSuccess(it : ReadTrainingCountBean?) {
warnBean.value = it
}
override fun doFailure(code : Int, msg : String?) {
LogUtil.print("fetchAppTipsData ", "doFailure==$msg")
}
})
} }
abstract class IWarnBean abstract class IWarnBean

View File

@ -33,188 +33,220 @@ import com.za.common.util.MapUtil
import com.za.servicing.R import com.za.servicing.R
@Composable @Composable
fun CommonDialog( fun CommonDialog(title : String? = null,
title: String? = null, confirmText : String = "确定",
confirmText: String = "确定", confirm : () -> Unit,
confirm: () -> Unit, content : @Composable () -> Unit = {},
content: @Composable () -> Unit = {}, message : String? = null,
message: String? = null, cancelText : String? = "取消",
cancelText: String? = "取消", cancelEnable : Boolean = true,
cancelEnable: Boolean = true, cancel : () -> Unit = {},
cancel: () -> Unit = {}, dismiss : () -> Unit) {
dismiss: () -> Unit Dialog(onDismissRequest = { dismiss() },
) { properties = DialogProperties(dismissOnBackPress = cancelEnable,
Dialog(onDismissRequest = { dismiss() }, dismissOnClickOutside = cancelEnable)) {
properties = DialogProperties( Box(modifier = Modifier.background(color = Color.White,
dismissOnBackPress = cancelEnable, shape = RoundedCornerShape(13.dp))) {
dismissOnClickOutside = cancelEnable)) { Spacer(modifier = Modifier
Box(modifier = Modifier .fillMaxWidth()
.background(color = Color.White, shape = RoundedCornerShape(13.dp))) { .height(105.dp)
Spacer(modifier = Modifier .background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF),
.fillMaxWidth() Color(0x00DAE8FF))),
.height(105.dp) shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp)))
.background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF), Column(modifier = Modifier
Color(0x00DAE8FF))), .fillMaxWidth()
shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp))) .padding(horizontal = 30.dp),
Column(modifier = Modifier horizontalAlignment = Alignment.CenterHorizontally,
.fillMaxWidth() verticalArrangement = Arrangement.Center) {
.padding(horizontal = 30.dp), Spacer(modifier = Modifier.height(35.dp))
horizontalAlignment = Alignment.CenterHorizontally, Text(text = "$title",
verticalArrangement = Arrangement.Center) { fontSize = 16.sp,
Spacer(modifier = Modifier.height(35.dp)) fontWeight = FontWeight.Medium,
Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054)) color = Color(0xFF2A4054))
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
if (message == null) { if (message == null) {
content() content()
} else { } else {
Text(text = message, fontSize = 14.sp, color = Color(0xFF536475)) Text(text = message, fontSize = 14.sp, color = Color(0xFF536475))
} }
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
Box(modifier = Modifier Box(modifier = Modifier
.width(202.dp) .width(202.dp)
.clickable { confirm() } .clickable { confirm() }
.background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp)) .background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp))
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) { .padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
Text(text = confirmText, color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium) Text(text = confirmText,
} color = Color.White,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
if (!cancelText.isNullOrBlank() && cancelEnable) { if (! cancelText.isNullOrBlank() && cancelEnable) {
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { cancel() }, contentAlignment = Alignment.Center) { .clickable { cancel() },
Text(text = cancelText, fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90) contentAlignment = Alignment.Center) {
} Text(text = cancelText,
} fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = black90)
}
}
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
} }
} }
} }
} }
@Composable @Composable
fun ReTakePhotoDialog( fun ReTakePhotoDialog(title : String? = null,
title: String? = null, confirm : () -> Unit,
confirm: () -> Unit, againSubmit : () -> Unit,
againSubmit: () -> Unit, path : String? = null,
path: String? = null, showAgain : Boolean = false,
showAgain: Boolean = false, cancel : () -> Unit = {},
cancel: () -> Unit = {}, dismiss : () -> Unit) {
dismiss: () -> Unit Dialog(onDismissRequest = { dismiss() },
) { properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) {
Dialog(onDismissRequest = { dismiss() }, Box(modifier = Modifier.background(color = Color.White,
properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) { shape = RoundedCornerShape(13.dp))) {
Box(modifier = Modifier Spacer(modifier = Modifier
.background(color = Color.White, shape = RoundedCornerShape(13.dp))) { .fillMaxWidth()
Spacer(modifier = Modifier .height(105.dp)
.fillMaxWidth() .background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF),
.height(105.dp) Color(0x00DAE8FF))),
.background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF), shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp)))
Color(0x00DAE8FF))), Column(modifier = Modifier
shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp))) .fillMaxWidth()
Column(modifier = Modifier .padding(horizontal = 30.dp),
.fillMaxWidth() horizontalAlignment = Alignment.CenterHorizontally,
.padding(horizontal = 30.dp), verticalArrangement = Arrangement.Center) {
horizontalAlignment = Alignment.CenterHorizontally, Spacer(modifier = Modifier.height(35.dp))
verticalArrangement = Arrangement.Center) { Text(text = "$title",
Spacer(modifier = Modifier.height(35.dp)) fontSize = 16.sp,
Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054)) fontWeight = FontWeight.Medium,
Spacer(modifier = Modifier.height(16.dp)) color = Color(0xFF2A4054))
Box(modifier = Modifier Spacer(modifier = Modifier.height(16.dp))
.fillMaxWidth() Box(modifier = Modifier
.height(300.dp), contentAlignment = Alignment.Center) { .fillMaxWidth()
AsyncImage(model = path, .height(300.dp),
contentDescription = "", contentAlignment = Alignment.Center) {
modifier = Modifier.fillMaxSize(), AsyncImage(model = path,
contentScale = ContentScale.FillWidth) contentDescription = "",
} modifier = Modifier.fillMaxSize(),
Spacer(modifier = Modifier.height(24.dp)) contentScale = ContentScale.FillWidth)
}
Spacer(modifier = Modifier.height(24.dp))
Box(modifier = Modifier Box(modifier = Modifier
.width(202.dp) .width(202.dp)
.clickable { confirm() } .clickable { confirm() }
.background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp)) .background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp))
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) { .padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
Text(text = "重拍", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium) Text(text = "重拍",
} color = Color.White,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
}
if (showAgain) { if (showAgain) {
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
Box(modifier = Modifier Box(modifier = Modifier
.width(202.dp) .width(202.dp)
.clickable { againSubmit() } .clickable { againSubmit() }
.background(color = Color.Red, shape = RoundedCornerShape(23.dp)) .background(color = Color.Red, shape = RoundedCornerShape(23.dp))
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) { .padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
Text(text = "再次上传", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium) Text(text = "再次上传",
} color = Color.White,
} fontSize = 15.sp,
fontWeight = FontWeight.Medium)
}
}
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { cancel() }, contentAlignment = Alignment.Center) { .clickable { cancel() },
Text(text = "取消", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90) contentAlignment = Alignment.Center) {
} Text(text = "取消",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = black90)
}
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
} }
} }
} }
} }
@Composable @Composable
fun ChoiceMapDialog(dismiss: () -> Unit, fun ChoiceMapDialog(dismiss : () -> Unit, lat : Double?, lng : Double?, address : String?) {
lat: Double?, val context = LocalContext.current
lng: Double?, Dialog(onDismissRequest = { dismiss() }) {
address: String?) { Row(modifier = Modifier
val context = LocalContext.current .fillMaxWidth()
Dialog(onDismissRequest = { dismiss() }) { .height(180.dp)
Row(modifier = Modifier .background(color = Color.White, shape = RoundedCornerShape(8.dp))
.fillMaxWidth() .padding(10.dp),
.height(180.dp) verticalAlignment = Alignment.CenterVertically,
.background(color = Color.White, shape = RoundedCornerShape(8.dp)) horizontalArrangement = Arrangement.SpaceAround) {
.padding(10.dp), verticalAlignment = Alignment.CenterVertically, if (MapUtil.isGdMapInstalled(context)) {
horizontalArrangement = Arrangement.SpaceAround) { Column(modifier = Modifier.clickable {
if (MapUtil.isGdMapInstalled(context)) { MapUtil.startNavigationGd(context, lat = lat, lng = lng, address = address)
Column(modifier = Modifier.clickable { dismiss()
MapUtil.startNavigationGd(context, lat = lat, lng = lng, address = address) }) {
dismiss() AsyncImage(model = R.drawable.sv_amap_icon,
}) { contentDescription = "",
AsyncImage(model = R.drawable.sv_amap_icon, contentDescription = "", modifier = Modifier.size(60.dp)) modifier = Modifier.size(60.dp))
Spacer(modifier = Modifier.height(5.dp)) Spacer(modifier = Modifier.height(5.dp))
Text(text = "高德地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) Text(text = "高德地图",
} color = Color.Black,
} fontWeight = FontWeight.Medium,
fontSize = 14.sp)
}
}
if (MapUtil.isBaiduMapInstalled(context)) { if (MapUtil.isBaiduMapInstalled(context)) {
Column(modifier = Modifier.clickable { Column(modifier = Modifier.clickable {
MapUtil.startNavigationBd(context, lat = lat, lng = lng, address = address) MapUtil.startNavigationBd(context, lat = lat, lng = lng, address = address)
dismiss() dismiss()
}) { }) {
AsyncImage(model = R.drawable.sv_baidu_icon, contentDescription = "", modifier = Modifier.size(60.dp)) AsyncImage(model = R.drawable.sv_baidu_icon,
Spacer(modifier = Modifier.height(5.dp)) contentDescription = "",
Text(text = "百度地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) modifier = Modifier.size(60.dp))
} Spacer(modifier = Modifier.height(5.dp))
} Text(text = "百度地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
}
}
if (MapUtil.isTencentInstalled(context)) { if (MapUtil.isTencentInstalled(context)) {
Column(modifier = Modifier.clickable { Column(modifier = Modifier.clickable {
MapUtil.startNavigationTencent(context, lat = lat, lng = lng, address = address) MapUtil.startNavigationTencent(context, lat = lat, lng = lng, address = address)
dismiss() dismiss()
}) { }) {
AsyncImage(model = R.drawable.sv_tencent_icon, contentDescription = "", modifier = Modifier.size(60.dp)) AsyncImage(model = R.drawable.sv_tencent_icon,
Spacer(modifier = Modifier.height(5.dp)) contentDescription = "",
Text(text = "腾讯地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) modifier = Modifier.size(60.dp))
} Spacer(modifier = Modifier.height(5.dp))
} Text(text = "腾讯地图",
} color = Color.Black,
} fontWeight = FontWeight.Medium,
} fontSize = 14.sp)
}
}
}
}
}

View File

@ -23,7 +23,6 @@ import com.za.servicing.R
@Composable @Composable
fun HeadView(title : String, fun HeadView(title : String,
onBack : () -> Unit = {}, onBack : () -> Unit = {},
warnType : WarnType = WarnType.NULL,
isCanBack : Boolean = true, isCanBack : Boolean = true,
action : @Composable () -> Unit = {}) { action : @Composable () -> Unit = {}) {
Column { Column {
@ -43,9 +42,7 @@ fun HeadView(title : String,
}, },
actions = { action() }) actions = { action() })
if (warnType != WarnType.NULL) { AppTipsView()
AppTipsView()
}
} }
} }

View File

@ -16,31 +16,31 @@ import com.za.servicing.R
object LoadingManager { object LoadingManager {
val showLoading = mutableStateOf(false) val showLoading = mutableStateOf(false)
fun showLoading() { fun showLoading() {
showLoading.value = true showLoading.value = true
} }
fun hideLoading() { fun hideLoading() {
showLoading.value = false showLoading.value = false
} }
@Composable @Composable
fun LoadingView() { fun LoadingView() {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
AsyncImage(model = ImageRequest.Builder(LocalContext.current) AsyncImage(model = ImageRequest.Builder(LocalContext.current)
.data(R.drawable.gif_loading) .data(R.drawable.gif_loading).decoderFactory(GifDecoder.Factory()).build(),
.decoderFactory(GifDecoder.Factory()) contentDescription = "加载中",
.build(), contentDescription = "加载中", modifier = Modifier modifier = Modifier
.size(70.dp) .size(70.dp)
.align(Alignment.Center)) .align(Alignment.Center))
} }
} }
} }
open class LoadingState { open class LoadingState {
data object Loading : LoadingState() data object Loading : LoadingState()
data object LoadingFailed : LoadingState() data class LoadingFailed(val msg : String? = "加载失败") : LoadingState()
data object LoadingSuccess : LoadingState() data object LoadingSuccess : LoadingState()
} }

View File

@ -25,3 +25,10 @@ data class ReportHistoryBean(val reportType : String? = null,
val reportTemplate : String? = null, val reportTemplate : String? = null,
val createTime : Long? = null, val createTime : Long? = null,
val state : Int? = null) val state : Int? = null)
data class TaskNotesBean(val taskNotes : String? = null, //救援要求
val feeStandard : String? = null, //收费标准
val customerNotes : String? = null, //客户提醒
val otherNotes : String? = null, //特殊提醒
val modelVinNo : String? = null, //车型
val contract : String? = null) //车型

View File

@ -4,23 +4,23 @@ import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "ele_work_order") @Entity(tableName = "ele_work_order")
data class EleWorkOrderBean( data class EleWorkOrderBean(@PrimaryKey(autoGenerate = false) val orderId : Int,
@PrimaryKey(autoGenerate = false) val orderId: Int, val userOrderId : Int? = null,
val userOrderId: Int? = null, val date : String? = null, //日期 2023-10-20
val date: String? = null, //日期 2023-10-20 val serviceContent : String? = null, //服务须知内容
val serviceContent: String? = null, //服务须知内容 val orderWorkStatus : Int? = null, //电子工单状态 1车辆损伤照片 2 客户签名页面 3 服务完成 接车人签名和服务人员签名状态
val orderWorkStatus: Int? = null, //电子工单状态 1车辆损伤照片 2 客户签名页面 3 服务完成 接车人签名和服务人员签名状态 val hasBad : Boolean? = null, //车辆是否有损伤
val hasBad: Boolean? = null, //车辆是否有损伤 val carNO : String? = null, //车牌号
val carNO: String? = null,//车 val carVin : String? = null, //车
val carVin: String? = null,//车架号 val orderType : String? = null, //救援类型
val orderType: String? = null, //救援类型 val isSuccess : Int? = null, //服务是否成功 1 成功 其他失败
val isSuccess: Int? = null, //服务是否成功 1 成功 其他失败 val localCustomSignPath : String? = null,
val localCustomSignPath: String? = null, val localAcceptCarSignPath : String? = null, //接车人本地路径
val localAcceptCarSignPath: String? = null,//接车人本地路径 val localServicePeopleSignPath : String? = null, //服务人员签名
val localServicePeopleSignPath: String? = null, //服务人员签名 val serverCustomSignPath : String? = null,
val serverCustomSignPath: String? = null, val serverAcceptCarSignPath : String? = null, //远程地址
val serverAcceptCarSignPath: String? = null, //远程地址 val serverServicePeopleSignPath : String? = null,
val serverServicePeopleSignPath: String? = null, val hasCreatedEleWorkOrderPhoto : Boolean? = null, //是否已经生成电子工单照片
val hasCreatedEleWorkOrderPhoto: Boolean? = null, //是否已经生成电子工单照片 val changeBattery : Boolean? = null, //是否更换电瓶
val changeBattery: Boolean? = null //是否更换电瓶 var driverChoiceNoNeedReceiveMoney : Boolean? = null) //师傅是否选择了无需收款
)

View File

@ -7,231 +7,230 @@ import androidx.room.PrimaryKey
@Entity(tableName = "order_info") @Entity(tableName = "order_info")
data class OrderInfo( data class OrderInfo(
@PrimaryKey(autoGenerate = false) var taskId: Int? = null, //订单号 "taskId":5313005 @PrimaryKey(autoGenerate = false) var taskId : Int? = null, //订单号 "taskId":5313005
var userOrderId: Int? = null, //订单id var userOrderId : Int? = null, //订单id
var taskCode: String? = null, //订单编码 "taskCode":"ZD20190308009965" var taskCode : String? = null, //订单编码 "taskCode":"ZD20190308009965"
var customerName: String? = null, //客户姓名 "customerName":"越继安" var customerName : String? = null, //客户姓名 "customerName":"越继安"
var customerPhone: String? = null,//客户电话 "customerPhone":"18078815268" var customerPhone : String? = null, //客户电话 "customerPhone":"18078815268"
var carBrand: String? = null,//车辆品牌 "carBrand":"" var carBrand : String? = null, //车辆品牌 "carBrand":""
var modelVinNo: String? = null, var modelVinNo : String? = null,
var carModel: String? = null, //车辆型号 "carModel":"秦" var carModel : String? = null, //车辆型号 "carModel":"秦"
var carNo: String? = null, //客户车车牌号 "carNo":"粤AF53918" var carNo : String? = null, //客户车车牌号 "carNo":"粤AF53918"
var carVin: String? = null,//客户车Vin码 "carVin":"LGXC76C30J0132942" var carVin : String? = null, //客户车Vin码 "carVin":"LGXC76C30J0132942"
var taskState: String? = null, //订单状态 "taskState":"GOTO" var taskState : String? = null, //订单状态 "taskState":"GOTO"
var nextState: String? = null,//下一步状态 var nextState : String? = null, //下一步状态
var electronOrderState: Int? = null, //电子工单状态1检查损伤 2生成车辆检查表签字 3作业完成签字 var electronOrderState : Int? = null, //电子工单状态1检查损伤 2生成车辆检查表签字 3作业完成签字
var address: String? = null, //任务地址 "address":"广东省广州市白云区107国道石井凰岗路342号(白云黄石、同德围地区近庆丰兴隆公园)美景大酒店" var address : String? = null, //任务地址 "address":"广东省广州市白云区107国道石井凰岗路342号(白云黄石、同德围地区近庆丰兴隆公园)美景大酒店"
var addressProperty: String? = null, //任务地址类型 "addressProperty":"地面" var addressProperty : String? = null, //任务地址类型 "addressProperty":"地面"
var addressRemark: String? = null, //事发地地址补充 var addressRemark : String? = null, //事发地地址补充
var distAddress: String? = null, //目的地地址 "distAddress":"广东省广州市白云区雅岗南大道" var distAddress : String? = null, //目的地地址 "distAddress":"广东省广州市白云区雅岗南大道"
var distAddressRemark: String? = null,//目的地地址补充 var distAddressRemark : String? = null, //目的地地址补充
var expectArriveTime: String? = null, //预计到达时间 "expectArriveTime":"2019-03-08 05:11:07" var expectArriveTime : String? = null, //预计到达时间 "expectArriveTime":"2019-03-08 05:11:07"
var serviceTypeName: String? = null, //服务类型 "serviceTypeName":"故障--平板拖车" var serviceTypeName : String? = null, //服务类型 "serviceTypeName":"故障--平板拖车"
var orderSource: String? = null, // "orderSource":"中道救援-比亚迪道路救援项目" var orderSource : String? = null, // "orderSource":"中道救援-比亚迪道路救援项目"
var flowType: Int? = null, //流程类型 0 综合服务 1小修 2拖车 3取送件 4在线技术解决 5组合服务 6拖车简化流程 7武汉交管服务 var flowType : Int? = null, //流程类型 0 综合服务 1小修 2拖车 3取送件 4在线技术解决 5组合服务 6拖车简化流程 7武汉交管服务
var settleType: Int? = null,//结算类型 1 月结 2 现金 var settleType : Int? = null, //结算类型 1 月结 2 现金
var settleTypeStr: String? = null, //结算类型 var settleTypeStr : String? = null, //结算类型
var supplierId: Int? = null, var supplierId : Int? = null,
var startTime: String? = null,//发车时间 var startTime : String? = null, //发车时间
var operationTime: String? = null, //作业完成时间 var operationTime : String? = null, //作业完成时间
var dispatchTime: String? = null,//派单时间 "dispatchTime":"2019-03-08 04:26:07" var dispatchTime : String? = null, //派单时间 "dispatchTime":"2019-03-08 04:26:07"
var verifyType: Int? = null,//验证类型 "verifyType": 0 跳过验证 1和2 车牌和车架号验证 5 新车验证 var verifyType : Int? = null, //验证类型 "verifyType": 0 跳过验证 1和2 车牌和车架号验证 5 新车验证
var verifyValue: String? = null,//验证内容 "verifyValue":"粤AF53918" var verifyValue : String? = null, //验证内容 "verifyValue":"粤AF53918"
var holdon: Boolean? = null,//是否被挂起 "holdon":false var holdon : Boolean? = null, //是否被挂起 "holdon":false
var isCurrent: Boolean? = null, //是否为正在做的任务 var isCurrent : Boolean? = null, //是否为正在做的任务
var flow: String? = null, //流程状态 "flow":"START,GOTO,VERIFY,EXAMINE,OPERATION,SENDTO,SETTLEMENT,FINISH" var flow : String? = null, //流程状态 "flow":"START,GOTO,VERIFY,EXAMINE,OPERATION,SENDTO,SETTLEMENT,FINISH"
var externalCode: String? = null, // var externalCode : String? = null, //
var plateNumber: String? = null, var plateNumber : String? = null,
var distaddressProperty: String? = null, var distaddressProperty : String? = null,
var vehiclePointRemark: String? = null, var vehiclePointRemark : String? = null,
var destinationRemark: String? = null, var destinationRemark : String? = null,
var lat: Double? = null, var lat : Double? = null,
var lng: Double? = null, var lng : Double? = null,
var distLat: Double? = null, var distLat : Double? = null,
var distLng: Double? = null, var distLng : Double? = null,
var hotline: String? = null, var hotline : String? = null,
var createTime: String? = null, var createTime : String? = null,
var acceptTime: String? = null, var acceptTime : String? = null,
var arriveTime: String? = null, var arriveTime : String? = null,
var arriveDestTime: String? = null, var arriveDestTime : String? = null,
var needECDevice: Boolean? = null, var needECDevice : Boolean? = null,
var needDestAddress: Boolean? = null, var needDestAddress : Boolean? = null,
var linkToDocs: Boolean? = null, var linkToDocs : Boolean? = null,
var linkToDaDianH5: Boolean? = null, var linkToDaDianH5 : Boolean? = null,
var carFactory: Boolean? = null, var carFactory : Boolean? = null,
var needWaterMarker: Boolean? = null, var needWaterMarker : Boolean? = null,
var needShowPhoneBrand: Boolean? = null, var needShowPhoneBrand : Boolean? = null,
var taskNotes: String? = null, //救援要求 var taskNotes : String? = null, //救援要求
var feeStandard: String? = null, //收费标准 var feeStandard : String? = null, //收费标准
var customerNotes: String? = null, //客户提醒 var customerNotes : String? = null, //客户提醒
var otherNotes: String? = null, //特殊提醒 var otherNotes : String? = null, //特殊提醒
var isNewCar: Boolean? = null, //是否新车 var isNewCar : Boolean? = null, //是否新车
var ECDeviceString: String? = null,//搭电设备照片提示 var ECDeviceString : String? = null, //搭电设备照片提示
var customerReportImgs: String? = null, //客户上报照片,多张照片用;分隔 var customerReportImgs : String? = null, //客户上报照片,多张照片用;分隔
var arriveRemind: String? = null, var arriveRemind : String? = null,
var arriveRemindLink: String? = null, var arriveRemindLink : String? = null,
var policyNo: String? = null, // 平安拖车责任险 保单号 var policyNo : String? = null, // 平安拖车责任险 保单号
var advanceTime: Long? = null, var advanceTime : Long? = null,
var importantTip: String? = null, var importantTip : String? = null,
var hasReplaceBatteryCapable: Int? = null, //是否有更换电瓶的能力 1 搭电可以更换电瓶 2搭电不可以更换电瓶 其他的不展示 var hasReplaceBatteryCapable : Int? = null, //是否有更换电瓶的能力 1 搭电可以更换电瓶 2搭电不可以更换电瓶 其他的不展示
var taskSuccessStatus: Int? = null, //作业是否完成 0 完成 1未完成 拖车默认完成 var taskSuccessStatus : Int? = null, //作业是否完成 0 完成 1未完成 拖车默认完成
var tyreNumber: Int? = null, //辅助费用 var tyreNumber : Int? = null, //辅助费用
) : Parcelable { ) : Parcelable {
constructor(parcel: Parcel) : this( constructor(parcel : Parcel) : this(parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readString(),
parcel.readString(), parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readString(),
parcel.readString(), parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readValue(Double::class.java.classLoader) as? Double,
parcel.readValue(Double::class.java.classLoader) as? Double, parcel.readValue(Double::class.java.classLoader) as? Double,
parcel.readValue(Double::class.java.classLoader) as? Double, parcel.readValue(Double::class.java.classLoader) as? Double,
parcel.readValue(Double::class.java.classLoader) as? Double, parcel.readValue(Double::class.java.classLoader) as? Double,
parcel.readValue(Double::class.java.classLoader) as? Double, parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readString(),
parcel.readString(), parcel.readValue(Long::class.java.classLoader) as? Long,
parcel.readValue(Long::class.java.classLoader) as? Long, parcel.readString(),
parcel.readString(), parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int, parcel.readValue(Int::class.java.classLoader) as? Int) {
parcel.readValue(Int::class.java.classLoader) as? Int) { }
}
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel : Parcel, flags : Int) {
parcel.writeValue(taskId) parcel.writeValue(taskId)
parcel.writeValue(userOrderId) parcel.writeValue(userOrderId)
parcel.writeString(taskCode) parcel.writeString(taskCode)
parcel.writeString(customerName) parcel.writeString(customerName)
parcel.writeString(customerPhone) parcel.writeString(customerPhone)
parcel.writeString(carBrand) parcel.writeString(carBrand)
parcel.writeString(modelVinNo) parcel.writeString(modelVinNo)
parcel.writeString(carModel) parcel.writeString(carModel)
parcel.writeString(carNo) parcel.writeString(carNo)
parcel.writeString(carVin) parcel.writeString(carVin)
parcel.writeString(taskState) parcel.writeString(taskState)
parcel.writeString(nextState) parcel.writeString(nextState)
parcel.writeValue(electronOrderState) parcel.writeValue(electronOrderState)
parcel.writeString(address) parcel.writeString(address)
parcel.writeString(addressProperty) parcel.writeString(addressProperty)
parcel.writeString(addressRemark) parcel.writeString(addressRemark)
parcel.writeString(distAddress) parcel.writeString(distAddress)
parcel.writeString(distAddressRemark) parcel.writeString(distAddressRemark)
parcel.writeString(expectArriveTime) parcel.writeString(expectArriveTime)
parcel.writeString(serviceTypeName) parcel.writeString(serviceTypeName)
parcel.writeString(orderSource) parcel.writeString(orderSource)
parcel.writeValue(flowType) parcel.writeValue(flowType)
parcel.writeValue(settleType) parcel.writeValue(settleType)
parcel.writeString(settleTypeStr) parcel.writeString(settleTypeStr)
parcel.writeValue(supplierId) parcel.writeValue(supplierId)
parcel.writeString(startTime) parcel.writeString(startTime)
parcel.writeString(operationTime) parcel.writeString(operationTime)
parcel.writeString(dispatchTime) parcel.writeString(dispatchTime)
parcel.writeValue(verifyType) parcel.writeValue(verifyType)
parcel.writeString(verifyValue) parcel.writeString(verifyValue)
parcel.writeValue(holdon) parcel.writeValue(holdon)
parcel.writeValue(isCurrent) parcel.writeValue(isCurrent)
parcel.writeString(flow) parcel.writeString(flow)
parcel.writeString(externalCode) parcel.writeString(externalCode)
parcel.writeString(plateNumber) parcel.writeString(plateNumber)
parcel.writeString(distaddressProperty) parcel.writeString(distaddressProperty)
parcel.writeString(vehiclePointRemark) parcel.writeString(vehiclePointRemark)
parcel.writeString(destinationRemark) parcel.writeString(destinationRemark)
parcel.writeValue(lat) parcel.writeValue(lat)
parcel.writeValue(lng) parcel.writeValue(lng)
parcel.writeValue(distLat) parcel.writeValue(distLat)
parcel.writeValue(distLng) parcel.writeValue(distLng)
parcel.writeString(hotline) parcel.writeString(hotline)
parcel.writeString(createTime) parcel.writeString(createTime)
parcel.writeString(acceptTime) parcel.writeString(acceptTime)
parcel.writeString(arriveTime) parcel.writeString(arriveTime)
parcel.writeString(arriveDestTime) parcel.writeString(arriveDestTime)
parcel.writeValue(needECDevice) parcel.writeValue(needECDevice)
parcel.writeValue(needDestAddress) parcel.writeValue(needDestAddress)
parcel.writeValue(linkToDocs) parcel.writeValue(linkToDocs)
parcel.writeValue(linkToDaDianH5) parcel.writeValue(linkToDaDianH5)
parcel.writeValue(carFactory) parcel.writeValue(carFactory)
parcel.writeValue(needWaterMarker) parcel.writeValue(needWaterMarker)
parcel.writeValue(needShowPhoneBrand) parcel.writeValue(needShowPhoneBrand)
parcel.writeString(taskNotes) parcel.writeString(taskNotes)
parcel.writeString(feeStandard) parcel.writeString(feeStandard)
parcel.writeString(customerNotes) parcel.writeString(customerNotes)
parcel.writeString(otherNotes) parcel.writeString(otherNotes)
parcel.writeValue(isNewCar) parcel.writeValue(isNewCar)
parcel.writeString(ECDeviceString) parcel.writeString(ECDeviceString)
parcel.writeString(customerReportImgs) parcel.writeString(customerReportImgs)
parcel.writeString(arriveRemind) parcel.writeString(arriveRemind)
parcel.writeString(arriveRemindLink) parcel.writeString(arriveRemindLink)
parcel.writeString(policyNo) parcel.writeString(policyNo)
parcel.writeValue(advanceTime) parcel.writeValue(advanceTime)
parcel.writeString(importantTip) parcel.writeString(importantTip)
parcel.writeValue(hasReplaceBatteryCapable) parcel.writeValue(hasReplaceBatteryCapable)
parcel.writeValue(taskSuccessStatus) parcel.writeValue(taskSuccessStatus)
parcel.writeValue(tyreNumber) parcel.writeValue(tyreNumber)
} }
override fun describeContents(): Int { override fun describeContents() : Int {
return 0 return 0
} }
companion object CREATOR : Parcelable.Creator<OrderInfo> { companion object CREATOR : Parcelable.Creator<OrderInfo> {
override fun createFromParcel(parcel: Parcel): OrderInfo { override fun createFromParcel(parcel : Parcel) : OrderInfo {
return OrderInfo(parcel) return OrderInfo(parcel)
} }
override fun newArray(size: Int): Array<OrderInfo?> { override fun newArray(size : Int) : Array<OrderInfo?> {
return arrayOfNulls(size) return arrayOfNulls(size)
} }
} }
} }

View File

@ -1,56 +1,55 @@
package com.za.bean.request package com.za.bean.request
data class OrderListRequest( data class OrderListRequest(
val vehicleId: Int? = null, val vehicleId : Int? = null,
val deviceId: String? = null, val deviceId : String? = null,
) )
/** /**
* 照片模版请求数据 * 照片模版请求数据
*/ */
data class PhotoTemplateRequest(val userOrderId: Int? = null) data class PhotoTemplateRequest(val userOrderId : Int? = null)
//照片识别 //照片识别
data class OrderPhotoOcrRecognizeRequest( data class OrderPhotoOcrRecognizeRequest(
val userOrderId: Int? = null, val userOrderId : Int? = null,
val recognizeType: Int? = null, val recognizeType : Int? = null,
val imgUrl: String? = null, val imgUrl : String? = null,
) )
data class TaskFinishRequest( data class TaskFinishRequest(val userOrderId : Int? = null,
val userOrderId: Int? = null, val lat : Double? = null,
val lat: Double? = null, val lng : Double? = null,
val lng: Double? = null, val operateTime : Long? = null)
val operateTime: Long? = null)
data class GiveUpTaskRequest( data class GiveUpTaskRequest(
val taskId: Int? = null, //任务id val taskId : Int? = null, //任务id
val userId: Int? = null, //用户id val userId : Int? = null, //用户id
val vehicleId: Int? = null, //车辆id val vehicleId : Int? = null, //车辆id
val lat: Double? = null, val lat : Double? = null,
val lng: Double? = null, val lng : Double? = null,
val address: String? = null, val address : String? = null,
val templatePhotoInfoList: List<String?>? = null, //照片数据 val templatePhotoInfoList : List<String?>? = null, //照片数据
val pushGiveUpFlag: Int? = null, //1 是 0否 val pushGiveUpFlag : Int? = null, //1 是 0否
) )
data class UpdatePhotoRequest( data class UpdatePhotoRequest(val taskId : Int? = null,
val taskId: Int? = null, val taskState : String? = null,
val taskState: String? = null, val picturePosition : Int? = null, //图片位置
val picturePosition: Int? = null, //图片位置 val picturePath : String? = null, //图片路径
val picturePath: String? = null,//图片路径 val imgInfo : String? = null)
val imgInfo: String? = null
)
data class SwitchTaskRequest(val currentTaskId: Int? = null, data class SwitchTaskRequest(val currentTaskId : Int? = null,
val nextTaskId: Int? = null, val nextTaskId : Int? = null,
val userId: Int? = null, val userId : Int? = null,
val vehicleId: Int? = null) val vehicleId : Int? = null)
data class HistoryTasksRequest(val userId: Int? = null, val vehicleId: Int? = null) data class HistoryTasksRequest(val userId : Int? = null, val vehicleId : Int? = null)
data class HistoryPhotoTemplateRequest(val taskId: Int? = null) data class HistoryPhotoTemplateRequest(val taskId : Int? = null)
data class HistoryDetailRequest(val taskId: Int? = null) data class HistoryDetailRequest(val taskId : Int? = null)
data class TaskNotesRequest(val taskId : Int? = null)

View File

@ -4,6 +4,8 @@ import android.app.Application
import com.amap.api.location.AMapLocation import com.amap.api.location.AMapLocation
import com.blankj.utilcode.util.AppUtils import com.blankj.utilcode.util.AppUtils
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.za.base.AppConfig
import com.za.base.Const
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.room.RoomHelper import com.za.room.RoomHelper
@ -112,6 +114,18 @@ object GlobalData : GlobalLocalData() {
field = value field = value
} }
var networkEnv : Int
get() {
return if (AppConfig.isRelease) {
mmkv.decodeInt("isReviewEnv", Const.NetEnv.Main)
} else {
mmkv.decodeInt("isReviewEnv", Const.NetEnv.CRM1)
}
}
set(value) {
mmkv.encode("isReviewEnv", value)
}
fun clearUserCache() { fun clearUserCache() {
token = null token = null
aesKey = null aesKey = null
@ -119,6 +133,16 @@ object GlobalData : GlobalLocalData() {
driverInfoBean = null driverInfoBean = null
loginTime = null loginTime = null
isLoginRecognition = null isLoginRecognition = null
if (AppConfig.isRelease) {
networkEnv = if (AppConfig.isRelease) {
Const.NetEnv.Main
} else {
Const.NetEnv.CRM1
}
}
} }
fun clearAllOrderCache() { fun clearAllOrderCache() {

View File

@ -18,13 +18,9 @@ object ZDManager {
} }
private fun thirdSdkInit(isRelease : Boolean = false) { private fun thirdSdkInit(isRelease : Boolean = false) {
if (isRelease) {
AppConfig.release()
} else {
AppConfig.crm1()
}
GlobalData.application = application // 在 Application 中初始化 MMKV所有进程共享同一存储路径 GlobalData.application = application // 在 Application 中初始化 MMKV所有进程共享同一存储路径
MMKV.initialize(application) MMKV.initialize(application)
AppConfig.init(isRelease)
Bugly.init(application, "6972a6b56d", true) Bugly.init(application, "6972a6b56d", true)
LogUtil.init(application) LogUtil.init(application)
RoomHelper.init(application) RoomHelper.init(application)

View File

@ -0,0 +1,5 @@
package com.za.common.util
class PermissionUtil {
}

View File

@ -23,6 +23,7 @@ import com.za.bean.ReportHistoryRequest
import com.za.bean.ReportInfoRequest import com.za.bean.ReportInfoRequest
import com.za.bean.ReportItem import com.za.bean.ReportItem
import com.za.bean.SettleInfoRequest import com.za.bean.SettleInfoRequest
import com.za.bean.TaskNotesBean
import com.za.bean.TaskSettlementAndTraceBean import com.za.bean.TaskSettlementAndTraceBean
import com.za.bean.UpdateVersionBean import com.za.bean.UpdateVersionBean
import com.za.bean.UpdateVersionRequest import com.za.bean.UpdateVersionRequest
@ -61,6 +62,7 @@ import com.za.bean.request.SaveEleOrderRequest
import com.za.bean.request.SwitchTaskRequest import com.za.bean.request.SwitchTaskRequest
import com.za.bean.request.TaskFinishRequest import com.za.bean.request.TaskFinishRequest
import com.za.bean.request.TaskFinishResponse import com.za.bean.request.TaskFinishResponse
import com.za.bean.request.TaskNotesRequest
import com.za.bean.request.TodayMaintainRequest import com.za.bean.request.TodayMaintainRequest
import com.za.bean.request.TodayMaintainUploadRequest import com.za.bean.request.TodayMaintainUploadRequest
import com.za.bean.request.TodayMaintainbean import com.za.bean.request.TodayMaintainbean
@ -284,4 +286,7 @@ interface ApiService {
@POST("driverApp/supplier/iaiCompareFace") @POST("driverApp/supplier/iaiCompareFace")
fun iaiCompareFace(@Body info : DriverFaceCompareRequest) : Observable<BaseResponse<IaiCompareFaceBean>> fun iaiCompareFace(@Body info : DriverFaceCompareRequest) : Observable<BaseResponse<IaiCompareFaceBean>>
@POST("driverApp/task/getTaskNotes")
fun getTaskNotes(@Body request : TaskNotesRequest) : Observable<BaseResponse<TaskNotesBean>>
} }

View File

@ -6,6 +6,8 @@ import com.blankj.utilcode.util.ThreadUtils
import com.blankj.utilcode.util.ToastUtils import com.blankj.utilcode.util.ToastUtils
import com.google.gson.JsonParseException import com.google.gson.JsonParseException
import com.za.base.Const import com.za.base.Const
import com.za.base.view.NetWarnBean
import com.za.base.view.warnBean
import com.za.bean.BaseResponse import com.za.bean.BaseResponse
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
@ -32,6 +34,11 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
override fun onNext(tBaseResponse : BaseResponse<T>) { override fun onNext(tBaseResponse : BaseResponse<T>) {
if (tBaseResponse.isOk) { if (tBaseResponse.isOk) {
doSuccess(tBaseResponse.result) doSuccess(tBaseResponse.result)
ThreadUtils.runOnUiThread {
if (warnBean.value != null && warnBean.value is NetWarnBean) {
warnBean.value = null
}
}
} else { } else {
when (tBaseResponse.code) { when (tBaseResponse.code) {
3, 401 -> handlerTokenExpired() 3, 401 -> handlerTokenExpired()
@ -67,15 +74,18 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
is ConnectException -> { is ConnectException -> {
doFailure(Const.NetWorkException, "与服务器断开连接") doFailure(Const.NetWorkException, "与服务器断开连接")
handlerNetError()
} }
is UnknownHostException -> { is UnknownHostException -> {
doFailure(Const.NetWorkException, "与服务器断开连接") doFailure(Const.NetWorkException, "与服务器断开连接")
handlerNetError()
} }
is SSLHandshakeException -> { is SSLHandshakeException -> {
doFailure(1, "证书验证失败") doFailure(1, "证书验证失败")
LogUtil.print("SSLHandshakeException", e) LogUtil.print("SSLHandshakeException", e)
handlerNetError()
} }
is TimeoutException -> { is TimeoutException -> {
@ -86,6 +96,7 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
is SocketTimeoutException -> { is SocketTimeoutException -> {
doFailure(Const.NetWorkException, "网络连接超时2") doFailure(Const.NetWorkException, "网络连接超时2")
LogUtil.print("SocketTimeoutException2", e) LogUtil.print("SocketTimeoutException2", e)
handlerNetError()
} }
else -> { else -> {
@ -102,6 +113,15 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
abstract fun doFailure(code : Int, msg : String?) abstract fun doFailure(code : Int, msg : String?)
private fun handlerNetError() {
ThreadUtils.runOnUiThread {
if (warnBean.value == null) {
warnBean.value = NetWarnBean("当前网络异常")
}
}
}
private fun handlerTokenExpired() { private fun handlerTokenExpired() {
ThreadUtils.runOnUiThread { ThreadUtils.runOnUiThread {
try { try {

View File

@ -1,6 +1,5 @@
package com.za.net package com.za.net
import android.util.Log
import com.za.base.AppConfig import com.za.base.AppConfig
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -38,6 +37,11 @@ object RetrofitHelper {
}.setLevel(HttpLoggingInterceptor.Level.BODY) }.setLevel(HttpLoggingInterceptor.Level.BODY)
fun reset() {
apiService = null
retrofit = null
}
fun getDefaultService() : ApiService { fun getDefaultService() : ApiService {
return if (apiService == null) { return if (apiService == null) {
apiService = getDefaultRetrofit().create(ApiService::class.java) apiService = getDefaultRetrofit().create(ApiService::class.java)

View File

@ -11,7 +11,7 @@ import com.za.room.db.GlobalRoom
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
object RoomHelper { object RoomHelper {
const val VERSION: Int = 40 const val VERSION: Int = 41
private lateinit var mContext: Context private lateinit var mContext: Context
var db: GlobalRoom? = null var db: GlobalRoom? = null

View File

@ -3,6 +3,7 @@ package com.za.service.mqtt
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.service.ServiceManager import com.za.service.ServiceManager
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -59,7 +60,7 @@ object MyMqttClient {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
val message = String(mqttMessage.payload) val message = String(mqttMessage.payload)
LogUtil.print("MyMqttClient ", "Message arrived: $message") LogUtil.print("MyMqttClient ", "Message arrived: $message")
ServiceManager.handlerPushMsg(message) // Pass the message to ServiceManager for processing ServiceManager.handlerPushMsg(message)
} }
} }
@ -96,10 +97,11 @@ object MyMqttClient {
//检测mqtt连接状态 //检测mqtt连接状态
fun publishMessage() { fun publishMessage() {
if (mqttClient.isConnected) { if (mqttClient.isConnected) {
LogUtil.print("MyMqttClient ", "publishMessage success") LogUtil.print("MyMqttClient ", "mqttClient.hasConnected")
return return
} }
connect() LogUtil.print("MyMqttClient ", "mqttClient 断开重新初始化")
ServiceManager.initialize(GlobalData.application)
} }
private fun subscribeTopic() { private fun subscribeTopic() {

View File

@ -7,7 +7,6 @@ import android.content.res.Configuration;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.os.BaseBundle;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;

View File

@ -84,7 +84,6 @@ public class HandWriteEditView extends AppCompatEditText {
/** /**
* 设置行高 * 设置行高
*
*/ */
public void setLineHeight(float lineHeight) { public void setLineHeight(float lineHeight) {
this.lineHeight = lineHeight; this.lineHeight = lineHeight;
@ -102,7 +101,7 @@ public class HandWriteEditView extends AppCompatEditText {
return null; return null;
} }
SpannableString mSpan = new SpannableString("1"); SpannableString mSpan = new SpannableString("1");
mSpan.setSpan(new ImageSpan(getContext(), srcBitmap), mSpan.length() - 1, mSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mSpan.setSpan(new ImageSpan(getContext(), srcBitmap, ImageSpan.ALIGN_BASELINE), mSpan.length() - 1, mSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Editable editable = getText(); Editable editable = getText();
//获取光标所在位置 //获取光标所在位置

View File

@ -0,0 +1,461 @@
package com.za.ui.camera
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.Matrix
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.OptIn
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.camera.view.LifecycleCameraController
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.ImageUtils
import com.blankj.utilcode.util.ToastUtils
import com.bumptech.glide.Glide
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.Face
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.za.bean.request.DriverFaceCompareBean
import com.za.bean.request.DriverFaceCompareRequest
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.speech.SpeechManager
import com.za.net.BaseObserver
import com.za.net.CommonMethod
import com.za.net.RetrofitHelper
import com.za.servicing.R
import com.za.signature.view.CircleImageView
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import java.io.File
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.math.abs
class ServicePeopleRealActivity : AppCompatActivity() {
private lateinit var lifecycleCameraController : LifecycleCameraController
private lateinit var cameraExecutor : ExecutorService
private var avatarBitmap : Bitmap? = null
private var isActivityActive = true
private var currentStep = 0
private val steps = listOf("请保持面部居中", "请向左转头", "请向右转头", "请保持面部居中")
private var isStepCompleted = false
private lateinit var viewFinder : PreviewView
private lateinit var stepText : TextView
private lateinit var confirmationLayout : FrameLayout
private lateinit var previewImage : CircleImageView
private lateinit var retryButton : Button
private lateinit var confirmButton : Button
private val faceDetector = FaceDetection.getClient(FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL).build())
private var loadingDialog : AlertDialog? = null
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_service_people_real) // 初始化控制器
lifecycleCameraController = LifecycleCameraController(this).apply {
cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
setEnabledUseCases(LifecycleCameraController.IMAGE_CAPTURE or LifecycleCameraController.IMAGE_ANALYSIS)
}
initializeViews()
setupClickListeners()
if (allPermissionsGranted()) {
startCamera()
} else {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
cameraExecutor = Executors.newSingleThreadExecutor()
updateStepUI()
}
private fun initializeViews() {
viewFinder = findViewById(R.id.viewFinder)
viewFinder.controller = lifecycleCameraController // 将控制器与生命周期绑定
lifecycleCameraController.bindToLifecycle(this)
stepText = findViewById(R.id.stepText)
confirmationLayout = findViewById(R.id.confirmationLayout)
previewImage = findViewById(R.id.previewImage)
retryButton = findViewById(R.id.retryButton)
confirmButton = findViewById(R.id.confirmButton)
}
private fun setupClickListeners() {
retryButton.setOnClickListener {
resetDetection()
}
confirmButton.setOnClickListener {
returnPhotoResult()
}
}
private fun captureImage() {
lifecycleCameraController.takePicture(ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageCapturedCallback() {
@OptIn(ExperimentalGetImage::class)
override fun onCaptureSuccess(image : ImageProxy) {
val mediaImage = image.image
LogUtil.print("拍照成功", image.imageInfo.toString())
if (mediaImage != null) {
val inputImage =
InputImage.fromMediaImage(mediaImage, image.imageInfo.rotationDegrees)
faceDetector.process(inputImage).addOnSuccessListener { faces ->
when {
faces.isEmpty() -> {
runOnUiThread {
Toast.makeText(this@ServicePeopleRealActivity,
"未检测到人脸",
Toast.LENGTH_SHORT).show()
}
}
faces.size > 1 -> {
runOnUiThread {
Toast.makeText(this@ServicePeopleRealActivity,
"检测到多个人脸,请确保画面中只有一个人脸",
Toast.LENGTH_SHORT).show()
}
}
else -> {
LogUtil.print("人脸检测成功", faces[0].toString())
avatarBitmap = handlerBitmap(image.toBitmap(), image.imageInfo.rotationDegrees)
LogUtil.print("人脸认证通过", faces[0].toString())
runOnUiThread {
showConfirmation()
}
}
}
}.addOnFailureListener { e ->
LogUtil.print("人脸检测失败", e)
if (isActivityActive) {
Toast.makeText(this@ServicePeopleRealActivity,
"人脸检测失败",
Toast.LENGTH_SHORT).show()
}
}.addOnCompleteListener {
image.close()
}
} else {
image.close()
}
}
override fun onError(exc : ImageCaptureException) {
LogUtil.print("拍照失败", exc)
if (isActivityActive) {
Toast.makeText(this@ServicePeopleRealActivity,
"拍照失败",
Toast.LENGTH_SHORT).show()
}
}
})
}
private fun isValidFace(face : Face, imageWidth : Int, imageHeight : Int) : Boolean {
val faceBounds = face.boundingBox
val faceCenterX = faceBounds.centerX()
val faceCenterY = faceBounds.centerY()
val imageCenterX = imageWidth / 2
val imageCenterY = imageHeight / 2
// 计算人脸框占图片的比例
val faceWidthRatio = faceBounds.width().toFloat() / imageWidth
val faceHeightRatio = faceBounds.height().toFloat() / imageHeight
// 检查人脸是否居中允许30%的偏移)
val centerThreshold = imageWidth * 0.8f
val isCentered =
abs(faceCenterX - imageCenterX) < centerThreshold && abs(faceCenterY - imageCenterY) < centerThreshold
// 检查人脸大小是否合适建议占据图片30%-70%的区域)
val isProperSize = faceWidthRatio in 0.6f .. 0.7f && faceHeightRatio in 0.6f .. 0.7f
// 检查是否是正面人脸且睁眼
val isFrontalFace = abs(face.headEulerAngleY) < 15 && // 左右角度
abs(face.headEulerAngleX) < 15 && // 上下角度
abs(face.headEulerAngleZ) < 15 && // 倾斜角度
face.rightEyeOpenProbability != null && face.leftEyeOpenProbability != null && face.rightEyeOpenProbability !! > 0.8 && face.leftEyeOpenProbability !! > 0.8
return isCentered && isProperSize && isFrontalFace
}
//校正图片,解决图片可能翻转的问题
private val rotationMatrix = Matrix()
private fun handlerBitmap(bitmap : Bitmap, degrees : Int?) : Bitmap {
if (degrees == null || degrees == 0) return bitmap
rotationMatrix.reset()
rotationMatrix.postRotate(degrees.toFloat())
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, rotationMatrix, true)
}
private fun resetDetection() {
currentStep = 0
isStepCompleted = false
avatarBitmap = null
confirmationLayout.visibility = View.GONE
stepText.visibility = View.VISIBLE
updateStepUI()
}
private fun startCamera() {
lifecycleCameraController.setImageAnalysisAnalyzer(ContextCompat.getMainExecutor(this@ServicePeopleRealActivity),
FaceAnalyzer { handleFaceDetection(it) })
}
private fun handleFaceDetection(eulerY : Float) {
if (! isActivityActive) return
when (currentStep) {
0 -> {
if (abs(eulerY) < 15) {
if (! isStepCompleted) {
isStepCompleted = true
proceedToNextStep()
}
}
}
1 -> {
if (eulerY > 15) {
if (! isStepCompleted) {
isStepCompleted = true
proceedToNextStep()
}
}
}
2 -> {
if (eulerY < - 15) {
if (! isStepCompleted) {
isStepCompleted = true
proceedToNextStep()
}
}
}
3 -> {
if (abs(eulerY) < 15) {
if (! isStepCompleted) {
isStepCompleted = true
runOnUiThread {
captureImage()
}
}
}
}
}
}
private fun showConfirmation() {
avatarBitmap?.let { bitmap ->
Glide.with(previewImage).load(bitmap).into(previewImage)
confirmationLayout.visibility = View.VISIBLE
stepText.visibility = View.GONE
} ?: run {
AlertDialog.Builder(this).setTitle("提示")
.setMessage("未检测到有效的人脸照片,是否重新开始检测?")
.setPositiveButton("重新检测") { dialog, _ ->
dialog.dismiss()
resetDetectionProcess()
}.setNegativeButton("取消") { dialog, _ ->
dialog.dismiss()
finish()
}.setCancelable(false).show()
}
}
private fun resetDetectionProcess() {
currentStep = 0
isStepCompleted = false
avatarBitmap = null
confirmationLayout.visibility = View.GONE
stepText.visibility = View.VISIBLE
stepText.text = steps[currentStep]
startCamera()
}
private fun proceedToNextStep() {
currentStep ++
isStepCompleted = false
if (currentStep < steps.size) {
updateStepUI()
}
}
private fun updateStepUI() {
stepText.visibility = View.VISIBLE
stepText.text = steps[currentStep]
SpeechManager.releaseMediaPlayer()
when (currentStep) {
0 -> SpeechManager.playFaceCenter()
1 -> SpeechManager.playFaceLeft()
2 -> SpeechManager.playFaceRight()
3 -> SpeechManager.playFaceCenter()
}
}
private fun returnPhotoResult() {
avatarBitmap?.let { bitmap -> // 保存图片到临时文件
val file = ImageUtils.save2Album(bitmap, CompressFormat.JPEG, 100)
if (file?.exists() == true) { // 创建返回结果
val resultIntent = Intent()
resultIntent.putExtra("path", file.absolutePath)
setResult(RESULT_OK, resultIntent)
finish()
} else {
Toast.makeText(this, "照片保存失败,请重试", Toast.LENGTH_SHORT).show()
}
} ?: run {
Toast.makeText(this, "未获取到有效的人脸照片,请重试", Toast.LENGTH_SHORT).show()
}
}
private fun dismissLoadingDialog() {
loadingDialog?.dismiss()
}
private fun showUploadFailedDialog() {
AlertDialog.Builder(this).setTitle("上传失败").setMessage("人脸照片上传失败,请重新识别")
.setPositiveButton("重新识别") { dialog, _ ->
dialog.dismiss()
resetDetection()
}.setNegativeButton("取消") { dialog, _ ->
dialog.dismiss()
finish()
}.setCancelable(false).show()
}
@SuppressLint("CheckResult")
private fun doUploadImg(file : File) {
CommonMethod.uploadImage(file, success = {
if (it.isNullOrBlank()) {
ToastUtils.showLong("上传失败")
return@uploadImage
}
doUpload(it)
}, failed = {
dismissLoadingDialog()
showUploadFailedDialog()
})
}
private fun doUpload(url : String) {
val request = DriverFaceCompareRequest(vehicleId = GlobalData.driverInfoBean?.vehicleId,
driverId = GlobalData.driverInfoBean?.userId,
photoUrl = url)
RetrofitHelper.getDefaultService().driverFaceCompare(request).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<DriverFaceCompareBean>() {
override fun doSuccess(it : DriverFaceCompareBean?) {
ToastUtils.showLong("头像上传成功!")
finish()
}
override fun doFailure(code : Int, msg : String?) {
LogUtil.print("doUpload addRealAvatarActivity failed",
"code==$msg request==$request")
showUploadFailedDialog()
}
})
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(requestCode : Int,
permissions : Array<String>,
grantResults : IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
startCamera()
} else {
Toast.makeText(this, "需要相机权限", Toast.LENGTH_SHORT).show()
finish()
}
}
}
override fun onResume() {
super.onResume()
isActivityActive = true
}
override fun onPause() {
super.onPause()
isActivityActive = false
}
override fun onDestroy() {
super.onDestroy()
isActivityActive = false
cameraExecutor.shutdown()
}
private class FaceAnalyzer(private val listener : (Float) -> Unit) : ImageAnalysis.Analyzer {
private val detector = FaceDetection.getClient(FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_NONE)
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_NONE).build())
@ExperimentalGetImage
override fun analyze(imageProxy : ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image =
InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
detector.process(image).addOnSuccessListener { faces ->
if (faces.isNotEmpty()) {
listener(faces[0].headEulerAngleY)
}
}.addOnCompleteListener {
imageProxy.close()
}
} else {
imageProxy.close()
}
}
}
companion object {
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
}
}

View File

@ -7,12 +7,10 @@ import android.view.ViewGroup
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -33,7 +31,6 @@ import com.tencent.smtt.sdk.WebView.setWebContentsDebuggingEnabled
import com.tencent.smtt.sdk.WebViewClient import com.tencent.smtt.sdk.WebViewClient
import com.za.base.AppConfig import com.za.base.AppConfig
import com.za.base.BaseActivity import com.za.base.BaseActivity
import com.za.base.theme.headPadding
import com.za.base.view.HeadView import com.za.base.view.HeadView
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
@ -168,11 +165,6 @@ private fun CommonH5Screen(url : String,
Scaffold(topBar = { Scaffold(topBar = {
if (title.isNotBlank()) { if (title.isNotBlank()) {
HeadView(title = title, onBack = { handleBackPress(context, webView) }) HeadView(title = title, onBack = { handleBackPress(context, webView) })
} else {
Spacer(modifier = Modifier
.fillMaxWidth()
.statusBarsPadding()
.padding(vertical = headPadding))
} }
}) { paddingValues -> }) { paddingValues ->
Box(modifier = Modifier Box(modifier = Modifier

View File

@ -42,7 +42,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -76,6 +75,7 @@ import com.za.ext.callPhone
import com.za.ext.copy import com.za.ext.copy
import com.za.ext.finish import com.za.ext.finish
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.servicing.in_servicing_setting.OrderTaskNotesDialog
class NewOrderActivity : BaseActivity() { class NewOrderActivity : BaseActivity() {
@ -198,6 +198,28 @@ private fun AcceptOrderScreen(jpushBean : JpushBean?, vm : NewOrderVm = viewMode
}) })
} }
if (uiState.value.acceptOrderDialog == true) {
CommonDialog(title = "请确认是否接受该任务!",
confirmText = "确认接单",
cancelText = "再想想",
cancelEnable = false,
confirm = {
vm.updateState(uiState.value.copy(acceptOrderDialog = false))
vm.dispatch(NewOrderVm.Action.ShowTaskNotes)
},
cancel = {},
dismiss = {})
}
if (uiState.value.showTaskNotesDialog == true) {
OrderTaskNotesDialog(uiState.value.taskNotesBean,
uiState.value.jpushBean?.hasReplaceBatteryCapable == 2,
dismiss = {},
confirm = {
vm.dispatch(NewOrderVm.Action.AcceptOrder)
})
}
BottomSheetScaffold(scaffoldState = scaffoldState, BottomSheetScaffold(scaffoldState = scaffoldState,
topBar = { HeadViewNotBack(title = "新订单") }, topBar = { HeadViewNotBack(title = "新订单") },
sheetContent = { sheetContent = {
@ -377,7 +399,7 @@ private fun AcceptOrderScreen(jpushBean : JpushBean?, vm : NewOrderVm = viewMode
} }
// 接单按钮 // 接单按钮
Button(onClick = { vm.dispatch(NewOrderVm.Action.AcceptOrder) }, Button(onClick = { vm.dispatch(NewOrderVm.Action.ShowTaskNotes) },
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.height(44.dp), .height(44.dp),

View File

@ -15,8 +15,10 @@ import com.blankj.utilcode.util.ToastUtils
import com.za.base.BaseVm import com.za.base.BaseVm
import com.za.base.view.LoadingManager import com.za.base.view.LoadingManager
import com.za.bean.JpushBean import com.za.bean.JpushBean
import com.za.bean.TaskNotesBean
import com.za.bean.request.AcceptOrderRequest import com.za.bean.request.AcceptOrderRequest
import com.za.bean.request.RefuseOrderRequest import com.za.bean.request.RefuseOrderRequest
import com.za.bean.request.TaskNotesRequest
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.common.util.DeviceUtil import com.za.common.util.DeviceUtil
@ -37,237 +39,272 @@ import java.util.Date
import java.util.Locale import java.util.Locale
class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() { class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
private val _uiState = MutableStateFlow(UiState()) private val _uiState = MutableStateFlow(UiState())
val uiState get() = _uiState val uiState get() = _uiState
private var timerJob: Job? = null private var timerJob : Job? = null
override fun dispatch(action: Action) { override fun dispatch(action : Action) {
when (action) { when (action) {
is Action.Init -> init(action.jpushBean) is Action.Init -> init(action.jpushBean)
is Action.AcceptOrder -> acceptOrder() is Action.AcceptOrder -> acceptOrder()
is Action.RefuseOrder -> refuseOrder() is Action.RefuseOrder -> refuseOrder()
is Action.StartTimer -> startTimer() is Action.StartTimer -> startTimer()
is Action.UpdateTimer -> updateTimer(action.remainingTime) is Action.ShowTaskNotes -> showTaskNotes()
else -> {} is Action.UpdateTimer -> updateTimer(action.remainingTime)
} else -> {}
} }
}
override fun updateState(uiState: UiState) { override fun updateState(uiState : UiState) {
_uiState.value = uiState _uiState.value = uiState
} }
private fun init(jpushBean: JpushBean?) { private fun init(jpushBean : JpushBean?) {
updateState(uiState.value.copy(jpushBean = jpushBean)) updateState(uiState.value.copy(jpushBean = jpushBean))
buildMarkers(jpushBean) buildMarkers(jpushBean)
searchDrivingRoute(jpushBean) searchDrivingRoute(jpushBean)
startTimer() startTimer()
} getTaskNote()
}
private fun buildMarkers(jpushBean: JpushBean?) { private fun getTaskNote() {
val markers = arrayListOf<MarkerOptions>() val taskNotesRequest = TaskNotesRequest(taskId = uiState.value.jpushBean?.taskId)
if (jpushBean?.lat != null && jpushBean.lat != 0.0 && jpushBean.lng != null && jpushBean.lng != 0.0) { RetrofitHelper.getDefaultService().getTaskNotes(taskNotesRequest)
val startMarkers = MarkerOptions() .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map)) .subscribe(object : BaseObserver<TaskNotesBean>() {
.position(LatLng(jpushBean.lat, jpushBean.lng)) override fun doSuccess(it : TaskNotesBean?) {
.title(jpushBean.address) updateState(uiState.value.copy(taskNotesBean = it))
.snippet("救援地点") }
.visible(true)
markers.add(startMarkers)
}
if (jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean.distLng != null && jpushBean.distLng != 0.0) { override fun doFailure(code : Int, msg : String?) {
val startMarkers = MarkerOptions() LogUtil.print("获取任务备注失败", "request=$taskNotesRequest msg=$msg")
.icon(ImageUtil.vectorToBitmap(ActivityUtils.getTopActivity(), R.drawable.sv_map_dist)) }
.position(LatLng(jpushBean.distLat, jpushBean.distLng)) })
.title(jpushBean.distAddress) }
.snippet("目的地")
.visible(true)
markers.add(startMarkers)
}
updateState(uiState.value.copy(markers = markers))
}
private fun acceptOrder() { private fun buildMarkers(jpushBean : JpushBean?) {
LoadingManager.showLoading() val markers = arrayListOf<MarkerOptions>()
ZdLocationManager.getSingleLocation(success = { if (jpushBean?.lat != null && jpushBean.lat != 0.0 && jpushBean.lng != null && jpushBean.lng != 0.0) {
val orderInfo = uiState.value.jpushBean val startMarkers =
val acceptOrderRequest = AcceptOrderRequest() MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
acceptOrderRequest.taskId = orderInfo?.taskId .position(LatLng(jpushBean.lat, jpushBean.lng)).title(jpushBean.address)
acceptOrderRequest.vehicleId = GlobalData.driverInfoBean?.vehicleId .snippet("救援地点").visible(true)
acceptOrderRequest.userId = GlobalData.driverInfoBean?.userId markers.add(startMarkers)
acceptOrderRequest.taskCode = orderInfo?.taskCode }
acceptOrderRequest.deviceId = DeviceUtil.getAndroidId(ActivityUtils.getTopActivity())
acceptOrderRequest.lat = it.latitude
acceptOrderRequest.lng = it.longitude
RetrofitHelper.getDefaultService().acceptOrder(acceptOrderRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() {
override fun doSuccess(it: String?) {
LoadingManager.hideLoading()
LogUtil.print("接单成功", "request=$acceptOrderRequest")
updateState(uiState.value.copy(showCallPhoneDialog = orderInfo?.isNeedCallCustomPhone()))
}
override fun doFailure(code: Int, msg: String?) { if (jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean.distLng != null && jpushBean.distLng != 0.0) {
LoadingManager.hideLoading() val startMarkers =
LogUtil.print("接单失败", "request=$acceptOrderRequest msg=$msg") MarkerOptions().icon(ImageUtil.vectorToBitmap(ActivityUtils.getTopActivity(),
} R.drawable.sv_map_dist)).position(LatLng(jpushBean.distLat, jpushBean.distLng))
}) .title(jpushBean.distAddress).snippet("目的地").visible(true)
}, failed = { markers.add(startMarkers)
LoadingManager.hideLoading() }
LogUtil.print("接单时获取定位失败", it) updateState(uiState.value.copy(markers = markers))
}) }
}
private fun refuseOrder() { private fun showTaskNotes() {
LoadingManager.showLoading() if (uiState.value.taskNotesBean != null && isNeedShowNotes()) {
RetrofitHelper.getDefaultService().refuseOrder(RefuseOrderRequest(taskId = uiState.value.jpushBean?.taskId, updateState(uiState.value.copy(showTaskNotesDialog = true))
vehicleId = GlobalData.driverInfoBean?.vehicleId, return
taskCode = uiState.value.jpushBean?.taskCode, }
userId = GlobalData.driverInfoBean?.userId))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() {
override fun doSuccess(it: String?) {
LoadingManager.hideLoading()
updateState(uiState.value.copy(refuseSuccess = true))
LogUtil.print("refuseOrder", "订单拒绝成功")
}
override fun doFailure(code: Int, msg: String?) { acceptOrder()
LoadingManager.hideLoading() }
ToastUtils.showShort(msg)
LogUtil.print("refuseOrder", "failed=$msg")
}
})
}
private fun startTimer() { private fun acceptOrder() {
timerJob?.cancel() LoadingManager.showLoading()
timerJob = viewModelScope.launch { ZdLocationManager.getSingleLocation(success = {
try { val orderInfo = uiState.value.jpushBean
var timeLeft = 120 val acceptOrderRequest = AcceptOrderRequest()
while (timeLeft > 0 && isActive) { acceptOrderRequest.taskId = orderInfo?.taskId
delay(1000) acceptOrderRequest.vehicleId = GlobalData.driverInfoBean?.vehicleId
timeLeft-- acceptOrderRequest.userId = GlobalData.driverInfoBean?.userId
updateState(uiState.value.copy(remainingTime = timeLeft)) acceptOrderRequest.taskCode = orderInfo?.taskCode
} acceptOrderRequest.deviceId = DeviceUtil.getAndroidId(ActivityUtils.getTopActivity())
if (timeLeft == 0 && isActive) { acceptOrderRequest.lat = it.latitude
// 倒计时结束,显示订单超时 acceptOrderRequest.lng = it.longitude
updateState(uiState.value.copy( RetrofitHelper.getDefaultService().acceptOrder(acceptOrderRequest)
isTimeout = true, .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
showTimeoutDialog = true .subscribe(object : BaseObserver<String>() {
)) override fun doSuccess(it : String?) {
} LoadingManager.hideLoading()
} catch (e: Exception) { LogUtil.print("接单成功", "request=$acceptOrderRequest")
LogUtil.print("startTimer", "倒计时异常: ${e.message}") updateState(uiState.value.copy(showCallPhoneDialog = orderInfo?.isNeedCallCustomPhone()))
} }
}
}
private fun updateTimer(remainingTime: Int) { override fun doFailure(code : Int, msg : String?) {
updateState(uiState.value.copy(remainingTime = remainingTime)) LoadingManager.hideLoading()
} LogUtil.print("接单失败", "request=$acceptOrderRequest msg=$msg")
}
})
}, failed = {
LoadingManager.hideLoading()
LogUtil.print("接单时获取定位失败", it)
})
}
private fun searchDrivingRoute(jpushBean: JpushBean?) { private fun refuseOrder() {
if (GlobalData.currentLocation == null) { LoadingManager.showLoading()
ToastUtils.showShort("获取当前位置失败") RetrofitHelper.getDefaultService()
return .refuseOrder(RefuseOrderRequest(taskId = uiState.value.jpushBean?.taskId,
} vehicleId = GlobalData.driverInfoBean?.vehicleId,
taskCode = uiState.value.jpushBean?.taskCode,
userId = GlobalData.driverInfoBean?.userId)).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : String?) {
LoadingManager.hideLoading()
updateState(uiState.value.copy(refuseSuccess = true))
LogUtil.print("refuseOrder", "订单拒绝成功")
}
updateState(uiState.value.copy(isLoading = true)) override fun doFailure(code : Int, msg : String?) {
LoadingManager.hideLoading()
ToastUtils.showShort(msg)
LogUtil.print("refuseOrder", "failed=$msg")
}
})
}
val startPoint = LatLonPoint( private fun startTimer() {
GlobalData.currentLocation?.latitude ?: 0.0, timerJob?.cancel()
GlobalData.currentLocation?.longitude ?: 0.0 timerJob = viewModelScope.launch {
) try {
var timeLeft = 120
while (timeLeft > 0 && isActive) {
delay(1000)
timeLeft --
updateState(uiState.value.copy(remainingTime = timeLeft))
}
if (timeLeft == 0 && isActive) { // 倒计时结束,显示订单超时
updateState(uiState.value.copy(isTimeout = true, showTimeoutDialog = true))
}
} catch (e : Exception) {
LogUtil.print("startTimer", "倒计时异常: ${e.message}")
}
}
}
val endPoint = when { private fun isNeedShowNotes() : Boolean {
jpushBean?.distLat != null && jpushBean.distLat != 0.0 && if (! uiState.value.taskNotesBean?.taskNotes.isNullOrBlank()) {
jpushBean.distLng != null && jpushBean.distLng != 0.0 -> { return true
LatLonPoint(jpushBean.distLat, jpushBean.distLng) }
}
jpushBean?.lat != null && jpushBean.lat != 0.0 &&
jpushBean.lng != null && jpushBean.lng != 0.0 -> {
LatLonPoint(jpushBean.lat, jpushBean.lng)
}
else -> null
}
if (endPoint == null) return if (! uiState.value.taskNotesBean?.customerNotes.isNullOrBlank()) {
return true
}
val fromAndTo = RouteSearch.FromAndTo(startPoint, endPoint) if (! uiState.value.taskNotesBean?.feeStandard.isNullOrBlank()) {
val query = RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DrivingDefault, null, null, "") return true
}
RouteSearch(GlobalData.application).apply { if (! uiState.value.taskNotesBean?.otherNotes.isNullOrBlank()) {
setRouteSearchListener(object : RouteSearch.OnRouteSearchListener { return true
override fun onDriveRouteSearched(result: DriveRouteResult?, errorCode: Int) { }
updateState(uiState.value.copy(isLoading = false))
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,
remainingDistance = path.distance.toDouble(),
estimatedArrivalTime = estimatedTime
))
} else {
ToastUtils.showShort("路线规划失败,请重试")
LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode")
}
}
override fun onBusRouteSearched(p0: BusRouteResult?, p1: Int) {} return false
override fun onWalkRouteSearched(p0: WalkRouteResult?, p1: Int) {} }
override fun onRideRouteSearched(p0: RideRouteResult?, p1: Int) {}
})
calculateDriveRouteAsyn(query)
}
}
override fun onCleared() { private fun updateTimer(remainingTime : Int) {
super.onCleared() updateState(uiState.value.copy(remainingTime = remainingTime))
try { }
timerJob?.cancel()
timerJob = null
} catch (e: Exception) {
LogUtil.print("onCleared", "取消倒计时异常: ${e.message}")
}
}
sealed class Action { private fun searchDrivingRoute(jpushBean : JpushBean?) {
data class Init(val jpushBean: JpushBean?) : Action() if (GlobalData.currentLocation == null) {
data object AcceptOrder : Action() ToastUtils.showShort("获取当前位置失败")
data class UpdateState(val uiState: UiState) : Action() return
data object RefuseOrder : Action() }
data object StartTimer : Action()
data class UpdateTimer(val remainingTime: Int) : Action()
}
data class UiState( updateState(uiState.value.copy(isLoading = true))
val jpushBean: JpushBean? = null,
val showCallPhoneDialog: Boolean? = false, val startPoint = LatLonPoint(GlobalData.currentLocation?.latitude ?: 0.0,
val markers: ArrayList<MarkerOptions>? = null, GlobalData.currentLocation?.longitude ?: 0.0)
val refuseSuccess: Boolean? = false,
val remainingTime: Int = 50, val endPoint = when {
val routePoints: List<LatLng>? = null, jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean.distLng != null && jpushBean.distLng != 0.0 -> {
val remainingDistance: Double = 0.0, LatLonPoint(jpushBean.distLat, jpushBean.distLng)
val estimatedArrivalTime: String = "", }
val isLoading: Boolean = false,
val isTimeout: Boolean = false, jpushBean?.lat != null && jpushBean.lat != 0.0 && jpushBean.lng != null && jpushBean.lng != 0.0 -> {
val showTimeoutDialog: Boolean = false LatLonPoint(jpushBean.lat, jpushBean.lng)
) }
else -> null
}
if (endPoint == null) return
val fromAndTo = RouteSearch.FromAndTo(startPoint, endPoint)
val query =
RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DrivingDefault, null, null, "")
RouteSearch(GlobalData.application).apply {
setRouteSearchListener(object : RouteSearch.OnRouteSearchListener {
override fun onDriveRouteSearched(result : DriveRouteResult?, errorCode : Int) {
updateState(uiState.value.copy(isLoading = false))
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,
remainingDistance = path.distance.toDouble(),
estimatedArrivalTime = estimatedTime))
} else {
ToastUtils.showShort("路线规划失败,请重试")
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) {}
})
calculateDriveRouteAsyn(query)
}
}
override fun onCleared() {
super.onCleared()
try {
timerJob?.cancel()
timerJob = null
} catch (e : Exception) {
LogUtil.print("onCleared", "取消倒计时异常: ${e.message}")
}
}
sealed class Action {
data class Init(val jpushBean : JpushBean?) : Action()
data object AcceptOrder : Action()
data class UpdateState(val uiState : UiState) : Action()
data object RefuseOrder : Action()
data object ShowTaskNotes : Action()
data object StartTimer : Action()
data class UpdateTimer(val remainingTime : Int) : Action()
}
data class UiState(val jpushBean : JpushBean? = null,
val showCallPhoneDialog : Boolean? = false,
val markers : ArrayList<MarkerOptions>? = null,
val refuseSuccess : Boolean? = false,
val taskNotesBean : TaskNotesBean? = null,
val showTaskNotesDialog : Boolean? = false,
val acceptOrderDialog : Boolean? = null,
val remainingTime : Int = 50,
val routePoints : List<LatLng>? = null,
val remainingDistance : Double = 0.0,
val estimatedArrivalTime : String = "",
val isLoading : Boolean = false,
val isTimeout : Boolean = false,
val showTimeoutDialog : Boolean = false)
} }

View File

@ -39,14 +39,12 @@ import com.za.base.theme.bgColor
import com.za.base.theme.black5 import com.za.base.theme.black5
import com.za.base.theme.headBgColor import com.za.base.theme.headBgColor
import com.za.base.view.ChoiceMapDialog import com.za.base.view.ChoiceMapDialog
import com.za.base.view.CommonButton
import com.za.base.view.CommonDialog import com.za.base.view.CommonDialog
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.ext.callPhone import com.za.ext.callPhone
import com.za.ext.convertToFlowName import com.za.ext.convertToFlowName
import com.za.ext.copy import com.za.ext.copy
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
@Composable @Composable
fun OrderDetailItemScreen(orderInfo : OrderInfo?) { fun OrderDetailItemScreen(orderInfo : OrderInfo?) {
@ -57,6 +55,7 @@ fun OrderDetailItemScreen(orderInfo : OrderInfo?) {
.padding(5.dp)) { .padding(5.dp)) {
OrderDetailBaseInformationView(orderInfo = orderInfo) OrderDetailBaseInformationView(orderInfo = orderInfo)
OrderDetailServiceInformationView(orderInfo = orderInfo) OrderDetailServiceInformationView(orderInfo = orderInfo)
OrderDetailTime(orderInfo = orderInfo)
} }
} }
@ -187,18 +186,40 @@ private fun OrderDetailBaseInformationView(orderInfo : OrderInfo?) {
modifier = Modifier.size(15.dp)) modifier = Modifier.size(15.dp))
} }
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { if (! orderInfo?.carModel.isNullOrBlank()) {
Text(text = "车型", Spacer(modifier = Modifier.height(10.dp))
color = titleColor, Row(modifier = Modifier.fillMaxWidth(),
fontSize = titleSize, verticalAlignment = Alignment.CenterVertically) {
fontWeight = FontWeight.Medium, Text(text = "车型",
modifier = Modifier.width(75.dp)) color = titleColor,
Spacer(modifier = Modifier.width(5.dp)) fontSize = titleSize,
Text(text = "${orderInfo?.carModel}", fontWeight = FontWeight.Medium,
color = contentColor, modifier = Modifier.width(75.dp))
fontSize = titleSize, Spacer(modifier = Modifier.width(5.dp))
fontWeight = FontWeight.Medium) Text(text = "${orderInfo?.carModel}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
}
//拖车责任险
if (! orderInfo?.policyNo.isNullOrBlank()) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "拖车责任险",
color = titleColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium,
modifier = Modifier.width(75.dp))
Spacer(modifier = Modifier.width(5.dp))
Text(text = "${orderInfo?.policyNo}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
} }
} }
@ -211,7 +232,6 @@ private fun OrderDetailServiceInformationView(orderInfo : OrderInfo?) {
val titleSize = 12.sp val titleSize = 12.sp
val titleColor = Color(0xFF7A7A7A) val titleColor = Color(0xFF7A7A7A)
val contentColor = Color.Black val contentColor = Color.Black
val context = LocalContext.current
// 1 事发地 2 目的地 // 1 事发地 2 目的地
var showChoiceMapDialog by remember { mutableStateOf<Int?>(null) } var showChoiceMapDialog by remember { mutableStateOf<Int?>(null) }
@ -355,11 +375,77 @@ private fun OrderDetailServiceInformationView(orderInfo : OrderInfo?) {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
CommonButton(text = "客户放弃") { }
OrderGiveUpActivity.goOrderGiveUpActivity(context,
orderInfo = orderInfo,
userOrderId = orderInfo?.userOrderId,
giveUpType = 0)
}
@Composable
fun OrderDetailTime(orderInfo : OrderInfo?) {
val titleSize = 12.sp
val titleColor = Color(0xFF7A7A7A)
val contentColor = Color.Black
Column(modifier = Modifier
.fillMaxWidth()
.background(color = Color.White, shape = RoundedCornerShape(4.dp))
.padding(10.dp),
verticalArrangement = Arrangement.Top) {
Box(contentAlignment = Alignment.CenterStart) {
Text(text = "订单时间",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 16.sp)
}
HorizontalDivider(color = black5, modifier = Modifier.padding(vertical = 10.dp))
if (! orderInfo?.acceptTime.isNullOrBlank()) {
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "接单时间",
color = titleColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium,
modifier = Modifier.width(75.dp))
Spacer(modifier = Modifier.width(5.dp))
Text(text = "${orderInfo?.acceptTime}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
}
if (! orderInfo?.arriveTime.isNullOrBlank()) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "到达事发地时间",
color = titleColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium,
modifier = Modifier.width(75.dp))
Spacer(modifier = Modifier.width(5.dp))
Text(text = "${orderInfo?.arriveTime}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
}
if (! orderInfo?.arriveDestTime.isNullOrBlank()) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "到达目的地时间",
color = titleColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium,
modifier = Modifier.width(75.dp))
Spacer(modifier = Modifier.width(5.dp))
Text(text = "${orderInfo?.arriveDestTime}",
color = contentColor,
fontSize = titleSize,
fontWeight = FontWeight.Medium)
}
}
Spacer(modifier = Modifier.height(10.dp))
}
} }

View File

@ -28,9 +28,11 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.za.base.theme.headBgColor import com.za.base.theme.headBgColor
import com.za.base.theme.white80 import com.za.base.theme.white80
import com.za.base.view.CommonButton
import com.za.base.view.HeadView import com.za.base.view.HeadView
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.ext.finish import com.za.ext.finish
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
@ -41,6 +43,13 @@ fun OrderDetailScreen(orderInfo : OrderInfo?) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
Scaffold(topBar = { Scaffold(topBar = {
HeadView(title = "订单信息", onBack = { context.finish() }) HeadView(title = "订单信息", onBack = { context.finish() })
}, bottomBar = {
CommonButton(text = "客户放弃") {
OrderGiveUpActivity.goOrderGiveUpActivity(context,
orderInfo = orderInfo,
userOrderId = orderInfo?.userOrderId,
giveUpType = 0)
}
}) { }) {
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxSize() .fillMaxSize()

View File

@ -1,5 +1,8 @@
package com.za.ui.servicing.in_servicing_setting package com.za.ui.servicing.in_servicing_setting
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -9,9 +12,14 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -21,9 +29,13 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.za.base.theme.black5 import com.za.base.theme.black5
import com.za.base.theme.black65 import com.za.base.theme.black65
import com.za.base.view.CommonButton
import com.za.bean.TaskNotesBean
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.servicing.R import com.za.servicing.R
@ -49,10 +61,10 @@ fun OrderRequirementsScreen(orderInfo : OrderInfo?) {
} }
@Composable @Composable
private fun OrderRequirementsItemView(title : String?, content : String?) { fun OrderRequirementsItemView(title : String?, content : String?) {
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 12.dp)) { .padding(vertical = 5.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = R.drawable.sv_warn_red, AsyncImage(model = R.drawable.sv_warn_red,
contentDescription = null, contentDescription = null,
@ -76,10 +88,10 @@ private fun OrderRequirementsItemView(title : String?, content : String?) {
} }
@Composable @Composable
private fun CarModeView(orderInfo : OrderInfo?) { fun CarModeView(orderInfo : OrderInfo?) {
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 12.dp)) { .padding(vertical = 5.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = R.drawable.sv_warn_red, AsyncImage(model = R.drawable.sv_warn_red,
contentDescription = null, contentDescription = null,
@ -100,4 +112,90 @@ private fun CarModeView(orderInfo : OrderInfo?) {
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
HorizontalDivider(color = black5, modifier = Modifier.fillMaxWidth()) HorizontalDivider(color = black5, modifier = Modifier.fillMaxWidth())
} }
} }
@Composable
fun OrderTaskNotesDialog(taskNotesBean : TaskNotesBean?,
isShowChangeBattery : Boolean? = false,
dismiss : () -> Unit,
confirm : () -> Unit) {
Dialog(onDismissRequest = { dismiss() },
properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false)) {
Box(contentAlignment = Alignment.BottomCenter,
modifier = Modifier.background(color = Color.White,
shape = RoundedCornerShape(13.dp))) {
Column(modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(state = rememberScrollState())
.padding(horizontal = 10.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top) {
Spacer(modifier = Modifier.height(20.dp))
Row(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp)) {
Text("合同名称",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
Spacer(Modifier.weight(1f))
Text("平安保险",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Bold)
}
HorizontalDivider(color = black5, modifier = Modifier.padding(vertical = 10.dp))
if (! taskNotesBean?.otherNotes.isNullOrBlank()) {
OrderRequirementsItemView(title = "特殊提醒",
content = taskNotesBean?.otherNotes)
}
if (! taskNotesBean?.feeStandard.isNullOrBlank()) {
OrderRequirementsItemView(title = "收费标准",
content = taskNotesBean?.feeStandard)
}
if (! taskNotesBean?.modelVinNo.isNullOrBlank()) {
OrderRequirementsItemView(title = "车型", content = taskNotesBean?.modelVinNo)
}
if (! taskNotesBean?.taskNotes.isNullOrBlank()) {
OrderRequirementsItemView(title = "救援要求",
content = taskNotesBean?.taskNotes)
}
if (! taskNotesBean?.customerNotes.isNullOrBlank()) {
OrderRequirementsItemView(title = "客户要求",
content = taskNotesBean?.customerNotes)
}
if (isShowChangeBattery == true) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.padding(vertical = 10.dp, horizontal = 10.dp),
verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = Icons.Default.Warning,
tint = Color.Red,
contentDescription = "",
modifier = Modifier.size(24.dp))
Text("此订单不允许销售电瓶",
color = Color.Red,
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 8.dp))
}
}
Spacer(modifier = Modifier.height(65.dp))
}
CommonButton(text = "我已阅读") { confirm() }
}
}
}

View File

@ -40,7 +40,7 @@ import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.ext.goNextPage import com.za.ext.goNextPage
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.camera.ZdCameraXActivity import com.za.ui.camera.ServicePeopleRealActivity
@Composable @Composable
fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(), fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
@ -86,8 +86,7 @@ fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
confirmText = "重新认证", confirmText = "重新认证",
confirm = { confirm = {
vm.updateState(uiState.value.copy(showCompareFailedDialog = null)) vm.updateState(uiState.value.copy(showCompareFailedDialog = null))
val intent = Intent(context, ZdCameraXActivity::class.java) val intent = Intent(context, ServicePeopleRealActivity::class.java)
intent.putExtra("isBack", false)
getResult.launch(intent) getResult.launch(intent)
}, },
cancelText = "关闭", cancelText = "关闭",
@ -166,8 +165,7 @@ fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
Spacer(modifier = Modifier.height(40.dp)) Spacer(modifier = Modifier.height(40.dp))
CommonButton(text = "开始核验", onClick = { CommonButton(text = "开始核验", onClick = {
val intent = Intent(context, ZdCameraXActivity::class.java) val intent = Intent(context, ServicePeopleRealActivity::class.java)
intent.putExtra("isBack", false)
getResult.launch(intent) getResult.launch(intent)
}) })
} }

View File

@ -37,7 +37,8 @@ class ConfirmH5SuccessVm : IServicingVm<ConfirmH5SuccessVm.Action, ConfirmH5Succ
is Action.UpdateState -> updateState(action.uiState) is Action.UpdateState -> updateState(action.uiState)
is Action.TaskFinish -> taskFinish() is Action.TaskFinish -> taskFinish()
is Action.ClearOfflineTask -> clearCurrentOrderOfflineTask() is Action.ClearOfflineTask -> clearCurrentOrderOfflineTask()
is Action.UpdateCurrentEleWorkOrder->updateCurrentEleWorkOrder(action.type, action.path) is Action.UpdateCurrentEleWorkOrder -> updateCurrentEleWorkOrder(action.type,
action.path)
} }
} }
@ -214,13 +215,14 @@ class ConfirmH5SuccessVm : IServicingVm<ConfirmH5SuccessVm.Action, ConfirmH5Succ
updateState(uiState.value.copy(goNextPage = true)) updateState(uiState.value.copy(goNextPage = true))
return return
} }
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed)) updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed(msg)))
LogUtil.print("任务失败", "code==$code msg==$msg") LogUtil.print("任务失败", "code==$code msg==$msg")
} }
}) })
}, failed = { }, failed = {
LoadingManager.hideLoading() LoadingManager.hideLoading()
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed)) ToastUtils.showLong(it)
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed(it)))
}) })
} }

View File

@ -12,65 +12,77 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.za.base.BaseActivity import com.za.base.BaseActivity
import com.za.base.view.CommonDialog import com.za.base.view.CommonDialog
import com.za.base.view.LoadingManager import com.za.base.view.LoadingManager
import com.za.room.RoomHelper
import com.za.ui.servicing.order_confirm.input_money.InputMoneyActivity import com.za.ui.servicing.order_confirm.input_money.InputMoneyActivity
import com.za.ui.servicing.order_confirm.real_order_confirm.RealOrderConfirmActivity import com.za.ui.servicing.order_confirm.real_order_confirm.RealOrderConfirmActivity
import com.za.ui.servicing.order_confirm.receive_money.ReceiveMoneyActivity import com.za.ui.servicing.order_confirm.receive_money.ReceiveMoneyActivity
class OrderConfirmActivity : BaseActivity() { class OrderConfirmActivity : BaseActivity() {
@Composable @Composable
override fun ContentView() { override fun ContentView() {
OrderConfirmInitScreen() OrderConfirmInitScreen()
} }
} }
@Composable @Composable
fun OrderConfirmInitScreen(vm: OrderConfirmInitVm = viewModel()) { fun OrderConfirmInitScreen(vm : OrderConfirmInitVm = viewModel()) {
val uiState = vm.uiState.collectAsStateWithLifecycle() val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(key1 = Unit) { LaunchedEffect(key1 = Unit) {
vm.dispatch(OrderConfirmInitVm.Action.Init) vm.dispatch(OrderConfirmInitVm.Action.Init)
} }
if (uiState.value.showNoNeedPayDialog == true) { if (uiState.value.showNoNeedPayDialog == true) {
CommonDialog(title = "是否需要收款?", confirmText = "去收款", cancelText = "无需收款", CommonDialog(title = "是否需要收款?",
confirm = { confirmText = "去收款",
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false)) cancelText = "无需收款",
InputMoneyActivity.goInputMoney(context, userOrderId = uiState.value.orderInfo?.userOrderId confirm = {
?: 0, taskId = uiState.value.orderInfo?.taskId ?: 0) vm.updateState(uiState.value.copy(showNoNeedPayDialog = false))
}, InputMoneyActivity.goInputMoney(context,
cancel = { userOrderId = uiState.value.orderInfo?.userOrderId ?: 0,
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false, orderConfirmState = OrderConfirmInitVm.OrderConfirmState.OrderConfirm)) taskId = uiState.value.orderInfo?.taskId ?: 0)
}, },
dismiss = { cancel = {
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false, orderConfirmState = OrderConfirmInitVm.OrderConfirmState.OrderConfirm)) uiState.value.eleWorkOrderBean?.copy(driverChoiceNoNeedReceiveMoney = true)?.let {
}) RoomHelper.db?.eleWorkOrderDao()?.update(it)
} }
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false,
orderConfirmState = OrderConfirmInitVm.OrderConfirmState.Init))
vm.dispatch(OrderConfirmInitVm.Action.Init)
},
dismiss = {
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false,
orderConfirmState = OrderConfirmInitVm.OrderConfirmState.Init))
vm.dispatch(OrderConfirmInitVm.Action.Init)
})
}
when (uiState.value.orderConfirmState) { when (uiState.value.orderConfirmState) {
is OrderConfirmInitVm.OrderConfirmState.ChangeBattery -> ChangeBatteryScreen() is OrderConfirmInitVm.OrderConfirmState.ChangeBattery -> ChangeBatteryScreen()
is OrderConfirmInitVm.OrderConfirmState.ConfirmEle -> ConfirmEleScreen() is OrderConfirmInitVm.OrderConfirmState.ConfirmEle -> ConfirmEleScreen()
is OrderConfirmInitVm.OrderConfirmState.ConfirmH5Success -> ConfirmH5SuccessScreen() is OrderConfirmInitVm.OrderConfirmState.ConfirmH5Success -> ConfirmH5SuccessScreen()
is OrderConfirmInitVm.OrderConfirmState.Init -> { is OrderConfirmInitVm.OrderConfirmState.Init -> {
Box { Box {
LoadingManager.showLoading() LoadingManager.showLoading()
} }
} }
is OrderConfirmInitVm.OrderConfirmState.Failed -> { is OrderConfirmInitVm.OrderConfirmState.Failed -> {
Box(modifier = Modifier.clickable { Box(modifier = Modifier.clickable {
vm.dispatch(OrderConfirmInitVm.Action.Init) vm.dispatch(OrderConfirmInitVm.Action.Init)
}) { }) {
Text("点击重试") Text("点击重试")
} }
} }
is OrderConfirmInitVm.OrderConfirmState.PaymentInfo -> { is OrderConfirmInitVm.OrderConfirmState.PaymentInfo -> {
ReceiveMoneyActivity.goReceiveMoney(context, userOrderId = uiState.value.orderInfo?.userOrderId ReceiveMoneyActivity.goReceiveMoney(context,
?: 0, taskId = uiState.value.orderInfo?.taskId ?: 0) userOrderId = uiState.value.orderInfo?.userOrderId ?: 0,
} taskId = uiState.value.orderInfo?.taskId ?: 0)
}
else -> RealOrderConfirmActivity.goRealOrderConfirm(context) else -> RealOrderConfirmActivity.goRealOrderConfirm(context)
} }
} }

View File

@ -17,110 +17,102 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
class OrderConfirmInitVm : IServicingVm<OrderConfirmInitVm.Action, OrderConfirmInitVm.UiState>() { class OrderConfirmInitVm : IServicingVm<OrderConfirmInitVm.Action, OrderConfirmInitVm.UiState>() {
private val _uiState = MutableStateFlow(UiState()) private val _uiState = MutableStateFlow(UiState())
val uiState get() = _uiState val uiState get() = _uiState
override fun updateState(uiState: UiState) { override fun updateState(uiState : UiState) {
_uiState.value = uiState _uiState.value = uiState
} }
override fun dispatch(action: Action) { override fun dispatch(action : Action) {
when (action) { when (action) {
is Action.Init -> init() is Action.Init -> init()
is Action.UpdateState -> updateState(action.uiState) is Action.UpdateState -> updateState(action.uiState)
} }
} }
private fun init() { private fun init() {
updateState(uiState.value.copy(orderInfo = getCurrentOrder())) updateState(uiState.value.copy(orderInfo = getCurrentOrder()))
queryPaymentInfo(uiState.value.orderInfo) queryPaymentInfo(uiState.value.orderInfo)
} }
private fun queryPaymentInfo(orderInfo: OrderInfo?) { private fun queryPaymentInfo(orderInfo : OrderInfo?) {
LoadingManager.showLoading() LoadingManager.showLoading()
val paymentInfoRequest = PaymentInfoRequest(orderInfo?.userOrderId, orderInfo?.taskId) val paymentInfoRequest = PaymentInfoRequest(orderInfo?.userOrderId, orderInfo?.taskId)
val eleWorkOrderBean = RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId val eleWorkOrderBean =
?: 0) RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId ?: 0)
RetrofitHelper.getDefaultService().paymentInfoQuery(paymentInfoRequest) RetrofitHelper.getDefaultService().paymentInfoQuery(paymentInfoRequest)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread()) .subscribe(object : BaseObserver<PaymentInfoBean>() {
.subscribe(object : BaseObserver<PaymentInfoBean>() { override fun doSuccess(it : PaymentInfoBean?) {
override fun doSuccess(it: PaymentInfoBean?) { LoadingManager.hideLoading()
LoadingManager.hideLoading() updateState(uiState.value.copy(paymentInfoBean = it,
updateState(uiState.value.copy(paymentInfoBean = it, orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean)) orderInfo = orderInfo,
if (it?.isPayment == true && it.tradeState != 2) { eleWorkOrderBean = eleWorkOrderBean))
if (it.askPayment == true) { if (eleWorkOrderBean != null && eleWorkOrderBean.driverChoiceNoNeedReceiveMoney == true) {
updateState(uiState.value.copy(showNoNeedPayDialog = true)) handlerOtherState(orderInfo, eleWorkOrderBean)
return return
} }
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.PaymentInfo)) if (it?.isPayment == true && it.tradeState != 2) {
return if (it.askPayment == true) {
} updateState(uiState.value.copy(showNoNeedPayDialog = true))
return
}
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.PaymentInfo))
return
}
if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 handlerOtherState(orderInfo, eleWorkOrderBean)
|| orderInfo?.electronOrderState == 2 }
) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle))
return
}
if (eleWorkOrderBean?.changeBattery == true) { override fun doFailure(code : Int, msg : String?) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery)) LoadingManager.hideLoading()
return LogUtil.print("eleworkOrder", eleWorkOrderBean.toJson() ?: "")
} updateState(uiState.value.copy(orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean))
if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) { handlerOtherState(orderInfo, eleWorkOrderBean)
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success)) LogUtil.print("queryPaymentInfo", "failed=$msg request=${paymentInfoRequest.toJson()}")
return }
} })
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm)) }
}
override fun doFailure(code: Int, msg: String?) { private fun handlerOtherState(orderInfo : OrderInfo?, eleWorkOrderBean : EleWorkOrderBean?) {
LoadingManager.hideLoading() if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 || orderInfo?.electronOrderState == 2) {
LogUtil.print("eleworkOrder", eleWorkOrderBean.toJson() ?: "") updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle))
updateState(uiState.value.copy(orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean)) return
if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 || orderInfo?.electronOrderState == 2) { }
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle))
return
}
if (eleWorkOrderBean?.changeBattery == true) { if (eleWorkOrderBean?.changeBattery == true) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery)) updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery))
return return
} }
if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success))
return
}
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm))
}
if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) { sealed class Action {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success)) data object Init : Action()
return data class UpdateState(val uiState : UiState) : Action()
} }
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm)) data class UiState(
LogUtil.print("queryPaymentInfo", "failed=$msg request=${paymentInfoRequest.toJson()}") val orderConfirmState : OrderConfirmState = OrderConfirmState.Init,
} val orderInfo : OrderInfo? = null,
}) val eleWorkOrderBean : EleWorkOrderBean? = null,
} val goNextPage : UpdateTaskBean? = null,
val isGoNextPageDialog : Boolean? = null,
val paymentInfoBean : PaymentInfoBean? = null,
val showNoNeedPayDialog : Boolean? = null,
)
sealed class Action { sealed class OrderConfirmState {
data object Init : Action() data object Init : OrderConfirmState()
data class UpdateState(val uiState: UiState) : Action() data object Failed : OrderConfirmState()
} data object PaymentInfo : OrderConfirmState()
data object ConfirmEle : OrderConfirmState()
data class UiState( data object ChangeBattery : OrderConfirmState()
val orderConfirmState: OrderConfirmState = OrderConfirmState.Init, data object ConfirmH5Success : OrderConfirmState()
val orderInfo: OrderInfo? = null, data object OrderConfirm : OrderConfirmState()
val eleWorkOrderBean: EleWorkOrderBean? = null, }
val goNextPage: UpdateTaskBean? = null,
val isGoNextPageDialog: Boolean? = null,
val paymentInfoBean: PaymentInfoBean? = null,
val showNoNeedPayDialog: Boolean? = null,
)
sealed class OrderConfirmState {
data object Init : OrderConfirmState()
data object Failed : OrderConfirmState()
data object PaymentInfo : OrderConfirmState()
data object ConfirmEle : OrderConfirmState()
data object ChangeBattery : OrderConfirmState()
data object ConfirmH5Success : OrderConfirmState()
data object OrderConfirm : OrderConfirmState()
}
} }

View File

@ -0,0 +1,49 @@
package com.za.ui.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Path
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.camera.view.PreviewView
import com.blankj.utilcode.util.ConvertUtils
class CirclePreviewContainer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val path = Path()
private var previewView: PreviewView? = null
init {
setWillNotDraw(false)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
val centerX = w / 2f
val centerY = h / 2f
val radius = ConvertUtils.dp2px(150f).toFloat().coerceAtMost(w / 2f).coerceAtMost(h / 2f)
path.reset()
path.addCircle(centerX, centerY, radius, Path.Direction.CW)
}
override fun dispatchDraw(canvas: Canvas) {
canvas.save()
canvas.clipPath(path)
super.dispatchDraw(canvas)
canvas.restore()
}
override fun onFinishInflate() {
super.onFinishInflate()
if (childCount > 0) {
val child = getChildAt(0)
if (child is PreviewView) {
previewView = child
}
}
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Dark semi-transparent background -->
<item>
<shape android:shape="rectangle">
<solid android:color="#00000000" />
</shape>
</item>
<!-- Circle with light border -->
<item android:gravity="center">
<shape android:shape="oval">
<size
android:width="300dp"
android:height="300dp" />
<stroke
android:width="2dp"
android:color="#FFFFFFFF" />
<solid android:color="#00000000" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.za.ui.view.CirclePreviewContainer
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.za.ui.view.CirclePreviewContainer>
<View
android:id="@+id/previewOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/preview_overlay_mask" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/stepText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="50dp"
android:background="#80000000"
android:padding="16dp"
android:textColor="@android:color/white"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="请保持面部居中" />
<FrameLayout
android:id="@+id/confirmationLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:padding="16dp">
<com.za.signature.view.CircleImageView
android:id="@+id/previewImage"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center"
android:scaleType="centerCrop" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:text="确认使用这张照片?"
android:textColor="@android:color/white"
android:textSize="18sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/retryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="重试" />
<Button
android:id="@+id/confirmButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确认" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>