feat(network): 优化网络异常处理和环境切换
- 新增网络异常统一处理逻辑 - 实现环境切换功能 - 更新 API接口 - 重构部分代码以提高可维护性
This commit is contained in:
@ -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)
|
||||
}
|
||||
|
@ -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" }
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -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 //测试环境
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
@ -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) //车型
|
@ -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) //师傅是否选择了无需收款
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
|
@ -0,0 +1,5 @@
|
||||
package com.za.common.util
|
||||
|
||||
class PermissionUtil {
|
||||
|
||||
}
|
@ -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>>
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
//获取光标所在位置
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
||||
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
@ -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()
|
||||
|
@ -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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -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)))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
servicing/src/main/res/drawable/preview_overlay_mask.xml
Normal file
21
servicing/src/main/res/drawable/preview_overlay_mask.xml
Normal 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>
|
@ -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>
|
Reference in New Issue
Block a user