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()
.clickable {
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)
startActivity(intent)
}

View File

@ -10,6 +10,7 @@ fastjson = "1.2.69"
glide = "4.16.0"
gson = "2.11.0"
jcore = "3.3.2"
faceDetection = "16.1.7"
jpush = "4.8.1"
location = "5.6.1"
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" }
xz = { module = "org.tukaani:xz", version.ref = "xz" }
androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" }
face-detection = { module = "com.google.mlkit:face-detection", version.ref = "faceDetection" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }

View File

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

View File

@ -77,10 +77,19 @@
</queries>
<application
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true"
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
android:name="com.za.ui.servicing.inservice_people_confirm.ServicePeopleConfirmActivity"
android:exported="true"

View File

@ -1,6 +1,7 @@
package com.za.base
import com.za.common.GlobalData
import com.za.net.RetrofitHelper
object AppConfig {
var isRelease = false
@ -10,36 +11,71 @@ object AppConfig {
lateinit var IMG_BASE_URL : String // 图片服务器地址
lateinit var Resource_URL : String // 资源服务器地址
// H5 相关地址
lateinit var TRAIN_URL: String // 培训文档地址
lateinit var DOCMENT_URL: String // 中道资料地址
lateinit var trainUrl : String // 培训文档地址
lateinit var documentUrl : String // 中道资料地址
lateinit var newDriverTrainUrl : String // 新司机培训地址
fun init(isRelease : Boolean? = false) {
val envType = GlobalData.networkEnv
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) {
Const.NetEnv.Main -> release()
Const.NetEnv.Review -> review()
Const.NetEnv.CRM1 -> crm1()
Const.NetEnv.CRM2 -> crm2()
Const.NetEnv.UAT -> uat()
}
RetrofitHelper.reset()
}
/**
* 正式环境配置
*/
fun release() {
isRelease = true
// API 配置
isRelease = true // API 配置
GlobalData.networkEnv = Const.NetEnv.Main
BASE_URL = "https://api.sinoassist.com"
IMG_BASE_URL = "https://api.sinoassist.com"
Resource_URL = "https://www.sinoassist.com/res"
// H5 配置
TRAIN_URL = "https://www.sinoassist.com/h5/supplier/dispatch/diverTrainDocment"
DOCMENT_URL = "https://www.sinoassist.com/h5/supplier/dispatch/docmentList"
trainUrl = "https://www.sinoassist.com/h5/supplier/dispatch/diverTrainDocment"
documentUrl = "https://www.sinoassist.com/h5/supplier/dispatch/docmentList"
newDriverTrainUrl = "https://www.sinoassist.com/h5/supplier/dispatch/driverTrainingList";
}
/**
* 审核环境配置
*/
fun review() {
isRelease = true
private fun review() {
isRelease = true // API 配置
GlobalData.networkEnv = Const.NetEnv.Review
// 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"
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"
}
/**
@ -47,15 +83,16 @@ object AppConfig {
*/
fun crm1() {
isRelease = false
GlobalData.networkEnv = Const.NetEnv.CRM1
// API 配置
BASE_URL = "https://api1.sino-assist.com"
IMG_BASE_URL = "https://api1.sino-assist.com"
Resource_URL = "https://crm1.sino-assist.com/res"
// H5 配置
TRAIN_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/diverTrainDocment"
DOCMENT_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/docmentList"
documentUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/docmentList";
trainUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/diverTrainDocment";
newDriverTrainUrl = "https://crm1.sino-assist.com/h5/supplier/dispatch/driverTrainingList";
}
/**
@ -63,6 +100,7 @@ object AppConfig {
*/
fun crm2() {
isRelease = false
GlobalData.networkEnv = Const.NetEnv.CRM2
// API 配置
BASE_URL = "https://api2.sino-assist.com"
@ -70,6 +108,22 @@ object AppConfig {
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
@ -78,9 +132,9 @@ object AppConfig {
*/
fun getTrainUrl(keyWord : String = "") : String {
if (keyWord.isEmpty()) {
return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
return trainUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
}
return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord"
return trainUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord"
}
/**
@ -91,8 +145,8 @@ object AppConfig {
*/
fun getDocmentUrl(keyWord : String = "") : String {
if (keyWord.isEmpty()) {
return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
return documentUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}"
}
return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord"
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_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.Row
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.shape.CircleShape
@ -31,21 +30,14 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.za.base.NetworkRouteSelectionActivity
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.log.LogUtil
import com.za.net.BaseObserver
import com.za.net.RetrofitHelper
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import com.za.ext.navigationActivity
val warnBean = mutableStateOf<IWarnBean?>(null)
@ -60,59 +52,19 @@ fun AppTipsView() {
when (warnBean) {
is NetWarnBean -> {
// NetTipView(warnBean as NetWarnBean)
}
is ReadTrainingCountBean -> {
// TrainingDocView(warnBean as ReadTrainingCountBean)
NetworkWeakView(warnBean as NetWarnBean)
}
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
fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) {
val showTrainingDialog = remember { mutableStateOf(false) }
fun NetworkWeakView(netWarnBean : NetWarnBean) {
val context = LocalContext.current
if (showTrainingDialog.value) {
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,
AnimatedVisibility(context !is NetworkRouteSelectionActivity,
modifier = Modifier.background(color = headBgColor),
enter = fadeIn(animationSpec = tween(300)) + expandVertically(animationSpec = tween(300),
expandFrom = Alignment.Top),
@ -138,11 +90,12 @@ fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) {
modifier = Modifier.size(24.dp))
}
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text(text = "培训提醒",
Text(text = "提醒",
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
color = Color(0xFF424242),
fontSize = 14.sp)
Text(text = "您有 ${readTrainingCountBean.mustReadTrainingCount ?: 0} 个培训任务待完成",
Text(text = "当前线路异常,是否前往切换线路",
style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF757575),
fontSize = 12.sp)
@ -150,17 +103,19 @@ fun TrainingDocView(readTrainingCountBean : ReadTrainingCountBean) {
}
Box(modifier = Modifier
.clickable {}
.clickable {
context.navigationActivity(NetworkRouteSelectionActivity::class.java)
}
.background(color = Color(0xFFFF9800), shape = RoundedCornerShape(5.dp))
.padding(horizontal = 10.dp, vertical = 5.dp),
contentAlignment = Alignment.Center) {
Text("去完成", color = Color.White, style = MaterialTheme.typography.labelLarge)
}
}
}
}
}
}
}
}
}
private fun fetchAppTipsData() {
if (GlobalData.currentOrder != null) {
@ -169,20 +124,6 @@ private fun fetchAppTipsData() {
if (GlobalData.token == null) {
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

View File

@ -33,8 +33,7 @@ import com.za.common.util.MapUtil
import com.za.servicing.R
@Composable
fun CommonDialog(
title: String? = null,
fun CommonDialog(title : String? = null,
confirmText : String = "确定",
confirm : () -> Unit,
content : @Composable () -> Unit = {},
@ -42,14 +41,12 @@ fun CommonDialog(
cancelText : String? = "取消",
cancelEnable : Boolean = true,
cancel : () -> Unit = {},
dismiss: () -> Unit
) {
dismiss : () -> Unit) {
Dialog(onDismissRequest = { dismiss() },
properties = DialogProperties(
dismissOnBackPress = cancelEnable,
properties = DialogProperties(dismissOnBackPress = cancelEnable,
dismissOnClickOutside = cancelEnable)) {
Box(modifier = Modifier
.background(color = Color.White, shape = RoundedCornerShape(13.dp))) {
Box(modifier = Modifier.background(color = Color.White,
shape = RoundedCornerShape(13.dp))) {
Spacer(modifier = Modifier
.fillMaxWidth()
.height(105.dp)
@ -62,7 +59,10 @@ fun CommonDialog(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) {
Spacer(modifier = Modifier.height(35.dp))
Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054))
Text(text = "$title",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF2A4054))
Spacer(modifier = Modifier.height(16.dp))
if (message == null) {
content()
@ -77,7 +77,10 @@ fun CommonDialog(
.clickable { confirm() }
.background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp))
.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))
@ -85,8 +88,12 @@ fun CommonDialog(
if (! cancelText.isNullOrBlank() && cancelEnable) {
Box(modifier = Modifier
.fillMaxWidth()
.clickable { cancel() }, contentAlignment = Alignment.Center) {
Text(text = cancelText, fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90)
.clickable { cancel() },
contentAlignment = Alignment.Center) {
Text(text = cancelText,
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = black90)
}
}
@ -98,19 +105,17 @@ fun CommonDialog(
@Composable
fun ReTakePhotoDialog(
title: String? = null,
fun ReTakePhotoDialog(title : String? = null,
confirm : () -> Unit,
againSubmit : () -> Unit,
path : String? = null,
showAgain : Boolean = false,
cancel : () -> Unit = {},
dismiss: () -> Unit
) {
dismiss : () -> Unit) {
Dialog(onDismissRequest = { dismiss() },
properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) {
Box(modifier = Modifier
.background(color = Color.White, shape = RoundedCornerShape(13.dp))) {
Box(modifier = Modifier.background(color = Color.White,
shape = RoundedCornerShape(13.dp))) {
Spacer(modifier = Modifier
.fillMaxWidth()
.height(105.dp)
@ -123,11 +128,15 @@ fun ReTakePhotoDialog(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center) {
Spacer(modifier = Modifier.height(35.dp))
Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054))
Text(text = "$title",
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = Color(0xFF2A4054))
Spacer(modifier = Modifier.height(16.dp))
Box(modifier = Modifier
.fillMaxWidth()
.height(300.dp), contentAlignment = Alignment.Center) {
.height(300.dp),
contentAlignment = Alignment.Center) {
AsyncImage(model = path,
contentDescription = "",
modifier = Modifier.fillMaxSize(),
@ -140,7 +149,10 @@ fun ReTakePhotoDialog(
.clickable { confirm() }
.background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp))
.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) {
@ -151,7 +163,10 @@ fun ReTakePhotoDialog(
.clickable { againSubmit() }
.background(color = Color.Red, shape = RoundedCornerShape(23.dp))
.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)
}
}
@ -159,8 +174,12 @@ fun ReTakePhotoDialog(
Box(modifier = Modifier
.fillMaxWidth()
.clickable { cancel() }, contentAlignment = Alignment.Center) {
Text(text = "取消", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90)
.clickable { cancel() },
contentAlignment = Alignment.Center) {
Text(text = "取消",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = black90)
}
Spacer(modifier = Modifier.height(10.dp))
@ -171,26 +190,29 @@ fun ReTakePhotoDialog(
@Composable
fun ChoiceMapDialog(dismiss: () -> Unit,
lat: Double?,
lng: Double?,
address: String?) {
fun ChoiceMapDialog(dismiss : () -> Unit, lat : Double?, lng : Double?, address : String?) {
val context = LocalContext.current
Dialog(onDismissRequest = { dismiss() }) {
Row(modifier = Modifier
.fillMaxWidth()
.height(180.dp)
.background(color = Color.White, shape = RoundedCornerShape(8.dp))
.padding(10.dp), verticalAlignment = Alignment.CenterVertically,
.padding(10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround) {
if (MapUtil.isGdMapInstalled(context)) {
Column(modifier = Modifier.clickable {
MapUtil.startNavigationGd(context, lat = lat, lng = lng, address = address)
dismiss()
}) {
AsyncImage(model = R.drawable.sv_amap_icon, contentDescription = "", modifier = Modifier.size(60.dp))
AsyncImage(model = R.drawable.sv_amap_icon,
contentDescription = "",
modifier = Modifier.size(60.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)
}
}
@ -199,9 +221,14 @@ fun ChoiceMapDialog(dismiss: () -> Unit,
MapUtil.startNavigationBd(context, lat = lat, lng = lng, address = address)
dismiss()
}) {
AsyncImage(model = R.drawable.sv_baidu_icon, contentDescription = "", modifier = Modifier.size(60.dp))
AsyncImage(model = R.drawable.sv_baidu_icon,
contentDescription = "",
modifier = Modifier.size(60.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)
}
}
@ -210,9 +237,14 @@ fun ChoiceMapDialog(dismiss: () -> Unit,
MapUtil.startNavigationTencent(context, lat = lat, lng = lng, address = address)
dismiss()
}) {
AsyncImage(model = R.drawable.sv_tencent_icon, contentDescription = "", modifier = Modifier.size(60.dp))
AsyncImage(model = R.drawable.sv_tencent_icon,
contentDescription = "",
modifier = Modifier.size(60.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)
}
}
}

View File

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

View File

@ -30,9 +30,9 @@ object LoadingManager {
fun LoadingView() {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
AsyncImage(model = ImageRequest.Builder(LocalContext.current)
.data(R.drawable.gif_loading)
.decoderFactory(GifDecoder.Factory())
.build(), contentDescription = "加载中", modifier = Modifier
.data(R.drawable.gif_loading).decoderFactory(GifDecoder.Factory()).build(),
contentDescription = "加载中",
modifier = Modifier
.size(70.dp)
.align(Alignment.Center))
}
@ -41,6 +41,6 @@ object LoadingManager {
open class LoadingState {
data object Loading : LoadingState()
data object LoadingFailed : LoadingState()
data class LoadingFailed(val msg : String? = "加载失败") : LoadingState()
data object LoadingSuccess : LoadingState()
}

View File

@ -25,3 +25,10 @@ data class ReportHistoryBean(val reportType : String? = null,
val reportTemplate : String? = null,
val createTime : Long? = 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,8 +4,7 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "ele_work_order")
data class EleWorkOrderBean(
@PrimaryKey(autoGenerate = false) val orderId: Int,
data class EleWorkOrderBean(@PrimaryKey(autoGenerate = false) val orderId : Int,
val userOrderId : Int? = null,
val date : String? = null, //日期 2023-10-20
val serviceContent : String? = null, //服务须知内容
@ -22,5 +21,6 @@ data class EleWorkOrderBean(
val serverAcceptCarSignPath : String? = null, //远程地址
val serverServicePeopleSignPath : String? = null,
val hasCreatedEleWorkOrderPhoto : Boolean? = null, //是否已经生成电子工单照片
val changeBattery: Boolean? = null //是否更换电瓶
)
val changeBattery : Boolean? = null, //是否更换电瓶
var driverChoiceNoNeedReceiveMoney : Boolean? = null) //师傅是否选择了无需收款

View File

@ -77,8 +77,7 @@ data class OrderInfo(
var taskSuccessStatus : Int? = null, //作业是否完成 0 完成 1未完成 拖车默认完成
var tyreNumber : Int? = null, //辅助费用
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readValue(Int::class.java.classLoader) as? Int,
constructor(parcel : Parcel) : this(parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readString(),
parcel.readString(),

View File

@ -18,8 +18,7 @@ data class OrderPhotoOcrRecognizeRequest(
val imgUrl : String? = null,
)
data class TaskFinishRequest(
val userOrderId: Int? = null,
data class TaskFinishRequest(val userOrderId : Int? = null,
val lat : Double? = null,
val lng : Double? = null,
val operateTime : Long? = null)
@ -35,13 +34,11 @@ data class GiveUpTaskRequest(
val pushGiveUpFlag : Int? = null, //1 是 0否
)
data class UpdatePhotoRequest(
val taskId: Int? = null,
data class UpdatePhotoRequest(val taskId : Int? = null,
val taskState : String? = null,
val picturePosition : Int? = null, //图片位置
val picturePath : String? = null, //图片路径
val imgInfo: String? = null
)
val imgInfo : String? = null)
data class SwitchTaskRequest(val currentTaskId : Int? = null,
val nextTaskId : Int? = null,
@ -54,3 +51,5 @@ data class HistoryPhotoTemplateRequest(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.blankj.utilcode.util.AppUtils
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.common.log.LogUtil
import com.za.room.RoomHelper
@ -112,6 +114,18 @@ object GlobalData : GlobalLocalData() {
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() {
token = null
aesKey = null
@ -119,6 +133,16 @@ object GlobalData : GlobalLocalData() {
driverInfoBean = null
loginTime = null
isLoginRecognition = null
if (AppConfig.isRelease) {
networkEnv = if (AppConfig.isRelease) {
Const.NetEnv.Main
} else {
Const.NetEnv.CRM1
}
}
}
fun clearAllOrderCache() {

View File

@ -18,13 +18,9 @@ object ZDManager {
}
private fun thirdSdkInit(isRelease : Boolean = false) {
if (isRelease) {
AppConfig.release()
} else {
AppConfig.crm1()
}
GlobalData.application = application // 在 Application 中初始化 MMKV所有进程共享同一存储路径
MMKV.initialize(application)
AppConfig.init(isRelease)
Bugly.init(application, "6972a6b56d", true)
LogUtil.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.ReportItem
import com.za.bean.SettleInfoRequest
import com.za.bean.TaskNotesBean
import com.za.bean.TaskSettlementAndTraceBean
import com.za.bean.UpdateVersionBean
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.TaskFinishRequest
import com.za.bean.request.TaskFinishResponse
import com.za.bean.request.TaskNotesRequest
import com.za.bean.request.TodayMaintainRequest
import com.za.bean.request.TodayMaintainUploadRequest
import com.za.bean.request.TodayMaintainbean
@ -284,4 +286,7 @@ interface ApiService {
@POST("driverApp/supplier/iaiCompareFace")
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.google.gson.JsonParseException
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.common.GlobalData
import com.za.common.log.LogUtil
@ -32,6 +34,11 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
override fun onNext(tBaseResponse : BaseResponse<T>) {
if (tBaseResponse.isOk) {
doSuccess(tBaseResponse.result)
ThreadUtils.runOnUiThread {
if (warnBean.value != null && warnBean.value is NetWarnBean) {
warnBean.value = null
}
}
} else {
when (tBaseResponse.code) {
3, 401 -> handlerTokenExpired()
@ -67,15 +74,18 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
is ConnectException -> {
doFailure(Const.NetWorkException, "与服务器断开连接")
handlerNetError()
}
is UnknownHostException -> {
doFailure(Const.NetWorkException, "与服务器断开连接")
handlerNetError()
}
is SSLHandshakeException -> {
doFailure(1, "证书验证失败")
LogUtil.print("SSLHandshakeException", e)
handlerNetError()
}
is TimeoutException -> {
@ -86,6 +96,7 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
is SocketTimeoutException -> {
doFailure(Const.NetWorkException, "网络连接超时2")
LogUtil.print("SocketTimeoutException2", e)
handlerNetError()
}
else -> {
@ -102,6 +113,15 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
abstract fun doFailure(code : Int, msg : String?)
private fun handlerNetError() {
ThreadUtils.runOnUiThread {
if (warnBean.value == null) {
warnBean.value = NetWarnBean("当前网络异常")
}
}
}
private fun handlerTokenExpired() {
ThreadUtils.runOnUiThread {
try {

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package com.za.service.mqtt
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.service.ServiceManager
import kotlinx.coroutines.CoroutineScope
@ -59,7 +60,7 @@ object MyMqttClient {
Handler(Looper.getMainLooper()).post {
val message = String(mqttMessage.payload)
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连接状态
fun publishMessage() {
if (mqttClient.isConnected) {
LogUtil.print("MyMqttClient ", "publishMessage success")
LogUtil.print("MyMqttClient ", "mqttClient.hasConnected")
return
}
connect()
LogUtil.print("MyMqttClient ", "mqttClient 断开重新初始化")
ServiceManager.initialize(GlobalData.application)
}
private fun subscribeTopic() {

View File

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

View File

@ -84,7 +84,6 @@ public class HandWriteEditView extends AppCompatEditText {
/**
* 设置行高
*
*/
public void setLineHeight(float lineHeight) {
this.lineHeight = lineHeight;
@ -102,7 +101,7 @@ public class HandWriteEditView extends AppCompatEditText {
return null;
}
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();
//获取光标所在位置

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

View File

@ -42,7 +42,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@ -76,6 +75,7 @@ import com.za.ext.callPhone
import com.za.ext.copy
import com.za.ext.finish
import com.za.servicing.R
import com.za.ui.servicing.in_servicing_setting.OrderTaskNotesDialog
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,
topBar = { HeadViewNotBack(title = "新订单") },
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
.weight(1f)
.height(44.dp),

View File

@ -15,8 +15,10 @@ import com.blankj.utilcode.util.ToastUtils
import com.za.base.BaseVm
import com.za.base.view.LoadingManager
import com.za.bean.JpushBean
import com.za.bean.TaskNotesBean
import com.za.bean.request.AcceptOrderRequest
import com.za.bean.request.RefuseOrderRequest
import com.za.bean.request.TaskNotesRequest
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.util.DeviceUtil
@ -48,6 +50,7 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
is Action.AcceptOrder -> acceptOrder()
is Action.RefuseOrder -> refuseOrder()
is Action.StartTimer -> startTimer()
is Action.ShowTaskNotes -> showTaskNotes()
is Action.UpdateTimer -> updateTimer(action.remainingTime)
else -> {}
}
@ -62,32 +65,53 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
buildMarkers(jpushBean)
searchDrivingRoute(jpushBean)
startTimer()
getTaskNote()
}
private fun getTaskNote() {
val taskNotesRequest = TaskNotesRequest(taskId = uiState.value.jpushBean?.taskId)
RetrofitHelper.getDefaultService().getTaskNotes(taskNotesRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<TaskNotesBean>() {
override fun doSuccess(it : TaskNotesBean?) {
updateState(uiState.value.copy(taskNotesBean = it))
}
override fun doFailure(code : Int, msg : String?) {
LogUtil.print("获取任务备注失败", "request=$taskNotesRequest msg=$msg")
}
})
}
private fun buildMarkers(jpushBean : JpushBean?) {
val markers = arrayListOf<MarkerOptions>()
if (jpushBean?.lat != null && jpushBean.lat != 0.0 && jpushBean.lng != null && jpushBean.lng != 0.0) {
val startMarkers = MarkerOptions()
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.position(LatLng(jpushBean.lat, jpushBean.lng))
.title(jpushBean.address)
.snippet("救援地点")
.visible(true)
val startMarkers =
MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.position(LatLng(jpushBean.lat, jpushBean.lng)).title(jpushBean.address)
.snippet("救援地点").visible(true)
markers.add(startMarkers)
}
if (jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean.distLng != null && jpushBean.distLng != 0.0) {
val startMarkers = MarkerOptions()
.icon(ImageUtil.vectorToBitmap(ActivityUtils.getTopActivity(), R.drawable.sv_map_dist))
.position(LatLng(jpushBean.distLat, jpushBean.distLng))
.title(jpushBean.distAddress)
.snippet("目的地")
.visible(true)
val startMarkers =
MarkerOptions().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 showTaskNotes() {
if (uiState.value.taskNotesBean != null && isNeedShowNotes()) {
updateState(uiState.value.copy(showTaskNotesDialog = true))
return
}
acceptOrder()
}
private fun acceptOrder() {
LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(success = {
@ -101,8 +125,7 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
acceptOrderRequest.lat = it.latitude
acceptOrderRequest.lng = it.longitude
RetrofitHelper.getDefaultService().acceptOrder(acceptOrderRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : String?) {
LoadingManager.hideLoading()
@ -123,13 +146,12 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
private fun refuseOrder() {
LoadingManager.showLoading()
RetrofitHelper.getDefaultService().refuseOrder(RefuseOrderRequest(taskId = uiState.value.jpushBean?.taskId,
RetrofitHelper.getDefaultService()
.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>() {
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))
@ -154,12 +176,8 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
timeLeft --
updateState(uiState.value.copy(remainingTime = timeLeft))
}
if (timeLeft == 0 && isActive) {
// 倒计时结束,显示订单超时
updateState(uiState.value.copy(
isTimeout = true,
showTimeoutDialog = true
))
if (timeLeft == 0 && isActive) { // 倒计时结束,显示订单超时
updateState(uiState.value.copy(isTimeout = true, showTimeoutDialog = true))
}
} catch (e : Exception) {
LogUtil.print("startTimer", "倒计时异常: ${e.message}")
@ -167,6 +185,26 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
}
}
private fun isNeedShowNotes() : Boolean {
if (! uiState.value.taskNotesBean?.taskNotes.isNullOrBlank()) {
return true
}
if (! uiState.value.taskNotesBean?.customerNotes.isNullOrBlank()) {
return true
}
if (! uiState.value.taskNotesBean?.feeStandard.isNullOrBlank()) {
return true
}
if (! uiState.value.taskNotesBean?.otherNotes.isNullOrBlank()) {
return true
}
return false
}
private fun updateTimer(remainingTime : Int) {
updateState(uiState.value.copy(remainingTime = remainingTime))
}
@ -179,27 +217,26 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
updateState(uiState.value.copy(isLoading = true))
val startPoint = LatLonPoint(
GlobalData.currentLocation?.latitude ?: 0.0,
GlobalData.currentLocation?.longitude ?: 0.0
)
val startPoint = LatLonPoint(GlobalData.currentLocation?.latitude ?: 0.0,
GlobalData.currentLocation?.longitude ?: 0.0)
val endPoint = when {
jpushBean?.distLat != null && jpushBean.distLat != 0.0 &&
jpushBean.distLng != null && jpushBean.distLng != 0.0 -> {
jpushBean?.distLat != null && jpushBean.distLat != 0.0 && jpushBean.distLng != null && jpushBean.distLng != 0.0 -> {
LatLonPoint(jpushBean.distLat, jpushBean.distLng)
}
jpushBean?.lat != null && jpushBean.lat != 0.0 &&
jpushBean.lng != null && jpushBean.lng != 0.0 -> {
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
val fromAndTo = RouteSearch.FromAndTo(startPoint, endPoint)
val query = RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DrivingDefault, null, null, "")
val query =
RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DrivingDefault, null, null, "")
RouteSearch(GlobalData.application).apply {
setRouteSearchListener(object : RouteSearch.OnRouteSearchListener {
@ -217,11 +254,9 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
val dateFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
val estimatedTime = dateFormat.format(Date(arrivalTime))
updateState(uiState.value.copy(
routePoints = points,
updateState(uiState.value.copy(routePoints = points,
remainingDistance = path.distance.toDouble(),
estimatedArrivalTime = estimatedTime
))
estimatedArrivalTime = estimatedTime))
} else {
ToastUtils.showShort("路线规划失败,请重试")
LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode")
@ -251,23 +286,25 @@ class NewOrderVm : BaseVm<NewOrderVm.Action, NewOrderVm.UiState>() {
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,
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
)
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.headBgColor
import com.za.base.view.ChoiceMapDialog
import com.za.base.view.CommonButton
import com.za.base.view.CommonDialog
import com.za.bean.db.order.OrderInfo
import com.za.ext.callPhone
import com.za.ext.convertToFlowName
import com.za.ext.copy
import com.za.servicing.R
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
@Composable
fun OrderDetailItemScreen(orderInfo : OrderInfo?) {
@ -57,6 +55,7 @@ fun OrderDetailItemScreen(orderInfo : OrderInfo?) {
.padding(5.dp)) {
OrderDetailBaseInformationView(orderInfo = orderInfo)
OrderDetailServiceInformationView(orderInfo = orderInfo)
OrderDetailTime(orderInfo = orderInfo)
}
}
@ -187,8 +186,11 @@ private fun OrderDetailBaseInformationView(orderInfo : OrderInfo?) {
modifier = Modifier.size(15.dp))
}
if (! orderInfo?.carModel.isNullOrBlank()) {
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "车型",
color = titleColor,
fontSize = titleSize,
@ -202,6 +204,25 @@ private fun OrderDetailBaseInformationView(orderInfo : OrderInfo?) {
}
}
//拖车责任险
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)
}
}
}
Spacer(modifier = Modifier.height(10.dp))
}
@ -211,7 +232,6 @@ private fun OrderDetailServiceInformationView(orderInfo : OrderInfo?) {
val titleSize = 12.sp
val titleColor = Color(0xFF7A7A7A)
val contentColor = Color.Black
val context = LocalContext.current
// 1 事发地 2 目的地
var showChoiceMapDialog by remember { mutableStateOf<Int?>(null) }
@ -355,11 +375,77 @@ private fun OrderDetailServiceInformationView(orderInfo : OrderInfo?) {
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 com.za.base.theme.headBgColor
import com.za.base.theme.white80
import com.za.base.view.CommonButton
import com.za.base.view.HeadView
import com.za.bean.db.order.OrderInfo
import com.za.ext.finish
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
import kotlinx.coroutines.launch
@Composable
@ -41,6 +43,13 @@ fun OrderDetailScreen(orderInfo : OrderInfo?) {
val scope = rememberCoroutineScope()
Scaffold(topBar = {
HeadView(title = "订单信息", onBack = { context.finish() })
}, bottomBar = {
CommonButton(text = "客户放弃") {
OrderGiveUpActivity.goOrderGiveUpActivity(context,
orderInfo = orderInfo,
userOrderId = orderInfo?.userOrderId,
giveUpType = 0)
}
}) {
Column(modifier = Modifier
.fillMaxSize()

View File

@ -1,5 +1,8 @@
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.Row
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.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
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.Warning
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
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.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import coil.compose.AsyncImage
import com.za.base.theme.black5
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.servicing.R
@ -49,10 +61,10 @@ fun OrderRequirementsScreen(orderInfo : OrderInfo?) {
}
@Composable
private fun OrderRequirementsItemView(title : String?, content : String?) {
fun OrderRequirementsItemView(title : String?, content : String?) {
Column(modifier = Modifier
.fillMaxWidth()
.padding(vertical = 12.dp)) {
.padding(vertical = 5.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = R.drawable.sv_warn_red,
contentDescription = null,
@ -76,10 +88,10 @@ private fun OrderRequirementsItemView(title : String?, content : String?) {
}
@Composable
private fun CarModeView(orderInfo : OrderInfo?) {
fun CarModeView(orderInfo : OrderInfo?) {
Column(modifier = Modifier
.fillMaxWidth()
.padding(vertical = 12.dp)) {
.padding(vertical = 5.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
AsyncImage(model = R.drawable.sv_warn_red,
contentDescription = null,
@ -101,3 +113,89 @@ private fun CarModeView(orderInfo : OrderInfo?) {
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.ext.goNextPage
import com.za.servicing.R
import com.za.ui.camera.ZdCameraXActivity
import com.za.ui.camera.ServicePeopleRealActivity
@Composable
fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
@ -86,8 +86,7 @@ fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
confirmText = "重新认证",
confirm = {
vm.updateState(uiState.value.copy(showCompareFailedDialog = null))
val intent = Intent(context, ZdCameraXActivity::class.java)
intent.putExtra("isBack", false)
val intent = Intent(context, ServicePeopleRealActivity::class.java)
getResult.launch(intent)
},
cancelText = "关闭",
@ -166,8 +165,7 @@ fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
Spacer(modifier = Modifier.height(40.dp))
CommonButton(text = "开始核验", onClick = {
val intent = Intent(context, ZdCameraXActivity::class.java)
intent.putExtra("isBack", false)
val intent = Intent(context, ServicePeopleRealActivity::class.java)
getResult.launch(intent)
})
}

View File

@ -37,7 +37,8 @@ class ConfirmH5SuccessVm : IServicingVm<ConfirmH5SuccessVm.Action, ConfirmH5Succ
is Action.UpdateState -> updateState(action.uiState)
is Action.TaskFinish -> taskFinish()
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))
return
}
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed))
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed(msg)))
LogUtil.print("任务失败", "code==$code msg==$msg")
}
})
}, failed = {
LoadingManager.hideLoading()
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed))
ToastUtils.showLong(it)
updateState(uiState.value.copy(loadingState = LoadingState.LoadingFailed(it)))
})
}

View File

@ -12,6 +12,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.za.base.BaseActivity
import com.za.base.view.CommonDialog
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.real_order_confirm.RealOrderConfirmActivity
import com.za.ui.servicing.order_confirm.receive_money.ReceiveMoneyActivity
@ -33,17 +34,27 @@ fun OrderConfirmInitScreen(vm: OrderConfirmInitVm = viewModel()) {
}
if (uiState.value.showNoNeedPayDialog == true) {
CommonDialog(title = "是否需要收款?", confirmText = "去收款", cancelText = "无需收款",
CommonDialog(title = "是否需要收款?",
confirmText = "去收款",
cancelText = "无需收款",
confirm = {
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false))
InputMoneyActivity.goInputMoney(context, userOrderId = uiState.value.orderInfo?.userOrderId
?: 0, taskId = uiState.value.orderInfo?.taskId ?: 0)
InputMoneyActivity.goInputMoney(context,
userOrderId = uiState.value.orderInfo?.userOrderId ?: 0,
taskId = uiState.value.orderInfo?.taskId ?: 0)
},
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.OrderConfirm))
vm.updateState(uiState.value.copy(showNoNeedPayDialog = false,
orderConfirmState = OrderConfirmInitVm.OrderConfirmState.Init))
vm.dispatch(OrderConfirmInitVm.Action.Init)
})
}
@ -66,8 +77,9 @@ fun OrderConfirmInitScreen(vm: OrderConfirmInitVm = viewModel()) {
}
is OrderConfirmInitVm.OrderConfirmState.PaymentInfo -> {
ReceiveMoneyActivity.goReceiveMoney(context, userOrderId = uiState.value.orderInfo?.userOrderId
?: 0, taskId = uiState.value.orderInfo?.taskId ?: 0)
ReceiveMoneyActivity.goReceiveMoney(context,
userOrderId = uiState.value.orderInfo?.userOrderId ?: 0,
taskId = uiState.value.orderInfo?.taskId ?: 0)
}
else -> RealOrderConfirmActivity.goRealOrderConfirm(context)

View File

@ -38,15 +38,20 @@ class OrderConfirmInitVm : IServicingVm<OrderConfirmInitVm.Action, OrderConfirmI
private fun queryPaymentInfo(orderInfo : OrderInfo?) {
LoadingManager.showLoading()
val paymentInfoRequest = PaymentInfoRequest(orderInfo?.userOrderId, orderInfo?.taskId)
val eleWorkOrderBean = RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId
?: 0)
val eleWorkOrderBean =
RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId ?: 0)
RetrofitHelper.getDefaultService().paymentInfoQuery(paymentInfoRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<PaymentInfoBean>() {
override fun doSuccess(it : PaymentInfoBean?) {
LoadingManager.hideLoading()
updateState(uiState.value.copy(paymentInfoBean = it, orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean))
updateState(uiState.value.copy(paymentInfoBean = it,
orderInfo = orderInfo,
eleWorkOrderBean = eleWorkOrderBean))
if (eleWorkOrderBean != null && eleWorkOrderBean.driverChoiceNoNeedReceiveMoney == true) {
handlerOtherState(orderInfo, eleWorkOrderBean)
return
}
if (it?.isPayment == true && it.tradeState != 2) {
if (it.askPayment == true) {
updateState(uiState.value.copy(showNoNeedPayDialog = true))
@ -56,28 +61,20 @@ class OrderConfirmInitVm : IServicingVm<OrderConfirmInitVm.Action, OrderConfirmI
return
}
if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1
|| orderInfo?.electronOrderState == 2
) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle))
return
}
if (eleWorkOrderBean?.changeBattery == true) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery))
return
}
if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success))
return
}
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm))
handlerOtherState(orderInfo, eleWorkOrderBean)
}
override fun doFailure(code : Int, msg : String?) {
LoadingManager.hideLoading()
LogUtil.print("eleworkOrder", eleWorkOrderBean.toJson() ?: "")
updateState(uiState.value.copy(orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean))
handlerOtherState(orderInfo, eleWorkOrderBean)
LogUtil.print("queryPaymentInfo", "failed=$msg request=${paymentInfoRequest.toJson()}")
}
})
}
private fun handlerOtherState(orderInfo : OrderInfo?, eleWorkOrderBean : EleWorkOrderBean?) {
if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 || orderInfo?.electronOrderState == 2) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle))
return
@ -87,16 +84,11 @@ class OrderConfirmInitVm : IServicingVm<OrderConfirmInitVm.Action, OrderConfirmI
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery))
return
}
if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) {
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success))
return
}
updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm))
LogUtil.print("queryPaymentInfo", "failed=$msg request=${paymentInfoRequest.toJson()}")
}
})
}
sealed class Action {

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>