From f29cac2d73f800ce1c7aed43f1f0ea9896714fbc Mon Sep 17 00:00:00 2001 From: songzhiling <17630035658@163.com> Date: Wed, 21 May 2025 15:20:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(network):=20=E4=BC=98=E5=8C=96=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86=E5=92=8C=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增网络异常统一处理逻辑 - 实现环境切换功能 - 更新 API接口 - 重构部分代码以提高可维护性 --- .../main/java/com/za/sdk/demo/MainActivity.kt | 2 +- gradle/libs.versions.toml | 2 + servicing/build.gradle | 4 +- servicing/src/main/AndroidManifest.xml | 11 +- .../src/main/java/com/za/base/AppConfig.kt | 212 +++++--- servicing/src/main/java/com/za/base/Const.kt | 8 + .../za/base/NetworkRouteSelectionActivity.kt | 442 +++++++++++++++++ .../main/java/com/za/base/view/AppTipsView.kt | 87 +--- .../src/main/java/com/za/base/view/dialog.kt | 360 +++++++------- .../src/main/java/com/za/base/view/head.kt | 5 +- .../src/main/java/com/za/base/view/loading.kt | 42 +- servicing/src/main/java/com/za/bean/bean.kt | 7 + .../com/za/bean/db/ele/EleWorkOrderBean.kt | 40 +- .../java/com/za/bean/db/order/OrderInfo.kt | 443 +++++++++-------- .../java/com/za/bean/request/OrderRequest.kt | 65 ++- .../src/main/java/com/za/common/GlobalData.kt | 24 + .../src/main/java/com/za/common/ZDManager.kt | 6 +- .../java/com/za/common/util/PermissionUtil.kt | 5 + .../src/main/java/com/za/net/ApiService.kt | 5 + .../src/main/java/com/za/net/BaseObserver.kt | 20 + .../main/java/com/za/net/RetrofitHelper.kt | 6 +- .../src/main/java/com/za/room/RoomHelper.kt | 2 +- .../java/com/za/service/mqtt/MyMqttClient.kt | 8 +- .../com/za/signature/GridPaintActivity.java | 1 - .../za/signature/view/HandWriteEditView.java | 3 +- .../za/ui/camera/ServicePeopleRealActivity.kt | 461 ++++++++++++++++++ .../java/com/za/ui/h5/CommonH5Activity.kt | 8 - .../com/za/ui/new_order/NewOrderActivity.kt | 26 +- .../java/com/za/ui/new_order/NewOrderVm.kt | 453 +++++++++-------- .../OrderDetailItemScreen.kt | 128 ++++- .../in_servicing_setting/OrderDetailScreen.kt | 9 + .../OrderRequirementsScreen.kt | 108 +++- .../ServicePeopleConfirmScreen.kt | 8 +- .../order_confirm/ConfirmH5SuccessVm.kt | 8 +- .../order_confirm/OrderConfirmActivity.kt | 104 ++-- .../order_confirm/OrderConfirmInitVm.kt | 184 ++++--- .../com/za/ui/view/CirclePreviewContainer.kt | 49 ++ .../res/drawable/preview_overlay_mask.xml | 21 + .../layout/activity_service_people_real.xml | 99 ++++ 39 files changed, 2450 insertions(+), 1026 deletions(-) create mode 100644 servicing/src/main/java/com/za/base/NetworkRouteSelectionActivity.kt create mode 100644 servicing/src/main/java/com/za/common/util/PermissionUtil.kt create mode 100644 servicing/src/main/java/com/za/ui/camera/ServicePeopleRealActivity.kt create mode 100644 servicing/src/main/java/com/za/ui/view/CirclePreviewContainer.kt create mode 100644 servicing/src/main/res/drawable/preview_overlay_mask.xml create mode 100644 servicing/src/main/res/layout/activity_service_people_real.xml diff --git a/app/src/main/java/com/za/sdk/demo/MainActivity.kt b/app/src/main/java/com/za/sdk/demo/MainActivity.kt index f4cc486..fcd1301 100644 --- a/app/src/main/java/com/za/sdk/demo/MainActivity.kt +++ b/app/src/main/java/com/za/sdk/demo/MainActivity.kt @@ -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) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff555f7..474e019 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } diff --git a/servicing/build.gradle b/servicing/build.gradle index cd5cbfe..bc30e4c 100644 --- a/servicing/build.gradle +++ b/servicing/build.gradle @@ -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 } diff --git a/servicing/src/main/AndroidManifest.xml b/servicing/src/main/AndroidManifest.xml index 37f85ef..4dd0cbb 100644 --- a/servicing/src/main/AndroidManifest.xml +++ b/servicing/src/main/AndroidManifest.xml @@ -77,10 +77,19 @@ + + + + release() + Const.NetEnv.Review -> review() + } + } else { + when (envType) { + Const.NetEnv.CRM1 -> crm1() + Const.NetEnv.CRM2 -> crm2() + Const.NetEnv.UAT -> uat() + } + } + } - /** - * 审核环境配置 - */ - fun review() { - isRelease = true + 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() + } - // 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" - } - /** - * CRM1 环境配置 - */ - fun crm1() { - isRelease = false + fun release() { + 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" - // 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 配置 + 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"; + } - // H5 配置 - TRAIN_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/diverTrainDocment" - DOCMENT_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/docmentList" - } - /** - * CRM2 环境配置 - */ - fun crm2() { - isRelease = false + /** + * 审核环境配置 + */ + private fun review() { + isRelease = true // API 配置 + GlobalData.networkEnv = Const.NetEnv.Review - // API 配置 - BASE_URL = "https://api2.sino-assist.com" - IMG_BASE_URL = "https://api2.sino-assist.com" - Resource_URL = "https://crm2.sino-assist.com/res" - } + // 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" + } - /** - * 获取培训文档完整地址 - * @param driverId 司机ID - * @param keyword 关键字 - * @return 完整的培训文档URL - */ - fun getTrainUrl(keyWord: String = ""): String { - if (keyWord.isEmpty()) { - return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}" - } - return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord" - } + /** + * CRM1 环境配置 + */ + fun crm1() { + isRelease = false + GlobalData.networkEnv = Const.NetEnv.CRM1 - /** - * 获取中道资料完整地址 - * @param driverId 司机ID - * @param keyword 关键字 - * @return 完整的中道资料URL - */ - fun getDocmentUrl(keyWord: String = ""): String { - if (keyWord.isEmpty()) { - return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}" - } - return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord" - } -} \ No newline at end of file + // API 配置 + BASE_URL = "https://api1.sino-assist.com" + IMG_BASE_URL = "https://api1.sino-assist.com" + Resource_URL = "https://crm1.sino-assist.com/res" + + 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"; + } + + /** + * CRM2 环境配置 + */ + fun crm2() { + isRelease = false + GlobalData.networkEnv = Const.NetEnv.CRM2 + + // API 配置 + BASE_URL = "https://api2.sino-assist.com" + IMG_BASE_URL = "https://api2.sino-assist.com" + Resource_URL = "https://crm2.sino-assist.com/res" + } + + + fun uat() { + isRelease = false + GlobalData.networkEnv = Const.NetEnv.UAT + + BASE_URL = "https://api-uat.sino-assist.com" //crm2 + IMG_BASE_URL = "https://api-uat.sino-assist.com" + Resource_URL = "https://uat.sino-assist.com/res" + documentUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/docmentList" + trainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/diverTrainDocment" + newDriverTrainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/driverTrainingList" + documentUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/docmentList"; + trainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/diverTrainDocment"; + newDriverTrainUrl = "https://uat.sino-assist.com/h5/supplier/dispatch/driverTrainingList"; + } + + /** + * 获取培训文档完整地址 + * @param driverId 司机ID + * @param keyword 关键字 + * @return 完整的培训文档URL + */ + fun getTrainUrl(keyWord : String = "") : String { + if (keyWord.isEmpty()) { + return trainUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}" + } + return trainUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord" + } + + /** + * 获取中道资料完整地址 + * @param driverId 司机ID + * @param keyword 关键字 + * @return 完整的中道资料URL + */ + fun getDocmentUrl(keyWord : String = "") : String { + if (keyWord.isEmpty()) { + return documentUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}" + } + return documentUrl + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfoBean?.userId}&keyword=$keyWord" + } +} diff --git a/servicing/src/main/java/com/za/base/Const.kt b/servicing/src/main/java/com/za/base/Const.kt index 0a2e2ce..ae0330d 100644 --- a/servicing/src/main/java/com/za/base/Const.kt +++ b/servicing/src/main/java/com/za/base/Const.kt @@ -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 //测试环境 + } } \ No newline at end of file diff --git a/servicing/src/main/java/com/za/base/NetworkRouteSelectionActivity.kt b/servicing/src/main/java/com/za/base/NetworkRouteSelectionActivity.kt new file mode 100644 index 0000000..df587a8 --- /dev/null +++ b/servicing/src/main/java/com/za/base/NetworkRouteSelectionActivity.kt @@ -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(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() { + 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> +} + +// 在类的外部定义Retrofit管理器 +private object NetWorkRetrofit { + private val client = + OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS) + .build() + + private val retrofitMap = mutableMapOf() + + 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() + } +} diff --git a/servicing/src/main/java/com/za/base/view/AppTipsView.kt b/servicing/src/main/java/com/za/base/view/AppTipsView.kt index 24036f2..e760519 100644 --- a/servicing/src/main/java/com/za/base/view/AppTipsView.kt +++ b/servicing/src/main/java/com/za/base/view/AppTipsView.kt @@ -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(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,18 +103,20 @@ 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) { return @@ -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() { - override fun doSuccess(it : ReadTrainingCountBean?) { - warnBean.value = it - } - - override fun doFailure(code : Int, msg : String?) { - LogUtil.print("fetchAppTipsData ", "doFailure==$msg") - } - }) } abstract class IWarnBean diff --git a/servicing/src/main/java/com/za/base/view/dialog.kt b/servicing/src/main/java/com/za/base/view/dialog.kt index 8536c22..02b3989 100644 --- a/servicing/src/main/java/com/za/base/view/dialog.kt +++ b/servicing/src/main/java/com/za/base/view/dialog.kt @@ -33,188 +33,220 @@ import com.za.common.util.MapUtil import com.za.servicing.R @Composable -fun CommonDialog( - title: String? = null, - confirmText: String = "确定", - confirm: () -> Unit, - content: @Composable () -> Unit = {}, - message: String? = null, - cancelText: String? = "取消", - cancelEnable: Boolean = true, - cancel: () -> Unit = {}, - dismiss: () -> Unit -) { - Dialog(onDismissRequest = { dismiss() }, - properties = DialogProperties( - dismissOnBackPress = cancelEnable, - dismissOnClickOutside = cancelEnable)) { - Box(modifier = Modifier - .background(color = Color.White, shape = RoundedCornerShape(13.dp))) { - Spacer(modifier = Modifier - .fillMaxWidth() - .height(105.dp) - .background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF), - Color(0x00DAE8FF))), - shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp))) - Column(modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 30.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center) { - Spacer(modifier = Modifier.height(35.dp)) - Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054)) - Spacer(modifier = Modifier.height(16.dp)) - if (message == null) { - content() - } else { - Text(text = message, fontSize = 14.sp, color = Color(0xFF536475)) - } +fun CommonDialog(title : String? = null, + confirmText : String = "确定", + confirm : () -> Unit, + content : @Composable () -> Unit = {}, + message : String? = null, + cancelText : String? = "取消", + cancelEnable : Boolean = true, + cancel : () -> Unit = {}, + dismiss : () -> Unit) { + Dialog(onDismissRequest = { dismiss() }, + properties = DialogProperties(dismissOnBackPress = cancelEnable, + dismissOnClickOutside = cancelEnable)) { + Box(modifier = Modifier.background(color = Color.White, + shape = RoundedCornerShape(13.dp))) { + Spacer(modifier = Modifier + .fillMaxWidth() + .height(105.dp) + .background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF), + Color(0x00DAE8FF))), + shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp))) + Column(modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center) { + Spacer(modifier = Modifier.height(35.dp)) + Text(text = "$title", + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = Color(0xFF2A4054)) + Spacer(modifier = Modifier.height(16.dp)) + if (message == null) { + content() + } else { + Text(text = message, fontSize = 14.sp, color = Color(0xFF536475)) + } - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(24.dp)) - Box(modifier = Modifier - .width(202.dp) - .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) - } + Box(modifier = Modifier + .width(202.dp) + .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) + } - Spacer(modifier = Modifier.height(20.dp)) + Spacer(modifier = Modifier.height(20.dp)) - if (!cancelText.isNullOrBlank() && cancelEnable) { - Box(modifier = Modifier - .fillMaxWidth() - .clickable { cancel() }, contentAlignment = Alignment.Center) { - Text(text = cancelText, fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90) - } - } + if (! cancelText.isNullOrBlank() && cancelEnable) { + Box(modifier = Modifier + .fillMaxWidth() + .clickable { cancel() }, + contentAlignment = Alignment.Center) { + Text(text = cancelText, + fontSize = 15.sp, + fontWeight = FontWeight.Medium, + color = black90) + } + } - Spacer(modifier = Modifier.height(10.dp)) - } - } - } + Spacer(modifier = Modifier.height(10.dp)) + } + } + } } @Composable -fun ReTakePhotoDialog( - title: String? = null, - confirm: () -> Unit, - againSubmit: () -> Unit, - path: String? = null, - showAgain: Boolean = false, - cancel: () -> Unit = {}, - dismiss: () -> Unit -) { - Dialog(onDismissRequest = { dismiss() }, - properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) { - Box(modifier = Modifier - .background(color = Color.White, shape = RoundedCornerShape(13.dp))) { - Spacer(modifier = Modifier - .fillMaxWidth() - .height(105.dp) - .background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF), - Color(0x00DAE8FF))), - shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp))) - Column(modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 30.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center) { - Spacer(modifier = Modifier.height(35.dp)) - 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) { - AsyncImage(model = path, - contentDescription = "", - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.FillWidth) - } - Spacer(modifier = Modifier.height(24.dp)) +fun ReTakePhotoDialog(title : String? = null, + confirm : () -> Unit, + againSubmit : () -> Unit, + path : String? = null, + showAgain : Boolean = false, + cancel : () -> Unit = {}, + dismiss : () -> Unit) { + Dialog(onDismissRequest = { dismiss() }, + properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) { + Box(modifier = Modifier.background(color = Color.White, + shape = RoundedCornerShape(13.dp))) { + Spacer(modifier = Modifier + .fillMaxWidth() + .height(105.dp) + .background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF), + Color(0x00DAE8FF))), + shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp))) + Column(modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 30.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center) { + Spacer(modifier = Modifier.height(35.dp)) + 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) { + AsyncImage(model = path, + contentDescription = "", + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.FillWidth) + } + Spacer(modifier = Modifier.height(24.dp)) - Box(modifier = Modifier - .width(202.dp) - .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) - } + Box(modifier = Modifier + .width(202.dp) + .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) + } - if (showAgain) { - Spacer(modifier = Modifier.height(20.dp)) + if (showAgain) { + Spacer(modifier = Modifier.height(20.dp)) - Box(modifier = Modifier - .width(202.dp) - .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) - } - } + Box(modifier = Modifier + .width(202.dp) + .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) + } + } - Spacer(modifier = Modifier.height(20.dp)) + Spacer(modifier = Modifier.height(20.dp)) - Box(modifier = Modifier - .fillMaxWidth() - .clickable { cancel() }, contentAlignment = Alignment.Center) { - Text(text = "取消", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90) - } + Box(modifier = Modifier + .fillMaxWidth() + .clickable { cancel() }, + contentAlignment = Alignment.Center) { + Text(text = "取消", + fontSize = 15.sp, + fontWeight = FontWeight.Medium, + color = black90) + } - Spacer(modifier = Modifier.height(10.dp)) - } - } - } + Spacer(modifier = Modifier.height(10.dp)) + } + } + } } @Composable -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, - 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)) - Spacer(modifier = Modifier.height(5.dp)) - Text(text = "高德地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) - } - } +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, + 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)) + Spacer(modifier = Modifier.height(5.dp)) + Text(text = "高德地图", + color = Color.Black, + fontWeight = FontWeight.Medium, + fontSize = 14.sp) + } + } - if (MapUtil.isBaiduMapInstalled(context)) { - Column(modifier = Modifier.clickable { - MapUtil.startNavigationBd(context, lat = lat, lng = lng, address = address) - dismiss() - }) { - 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) - } - } + if (MapUtil.isBaiduMapInstalled(context)) { + Column(modifier = Modifier.clickable { + MapUtil.startNavigationBd(context, lat = lat, lng = lng, address = address) + dismiss() + }) { + 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) + } + } - if (MapUtil.isTencentInstalled(context)) { - Column(modifier = Modifier.clickable { - MapUtil.startNavigationTencent(context, lat = lat, lng = lng, address = address) - dismiss() - }) { - 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) - } - } - } - } -} \ No newline at end of file + if (MapUtil.isTencentInstalled(context)) { + Column(modifier = Modifier.clickable { + MapUtil.startNavigationTencent(context, lat = lat, lng = lng, address = address) + dismiss() + }) { + 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) + } + } + } + } +} diff --git a/servicing/src/main/java/com/za/base/view/head.kt b/servicing/src/main/java/com/za/base/view/head.kt index f09d3cd..30e5790 100644 --- a/servicing/src/main/java/com/za/base/view/head.kt +++ b/servicing/src/main/java/com/za/base/view/head.kt @@ -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,9 +42,7 @@ fun HeadView(title : String, }, actions = { action() }) - if (warnType != WarnType.NULL) { - AppTipsView() - } + AppTipsView() } } diff --git a/servicing/src/main/java/com/za/base/view/loading.kt b/servicing/src/main/java/com/za/base/view/loading.kt index 1d10bfb..7209af2 100644 --- a/servicing/src/main/java/com/za/base/view/loading.kt +++ b/servicing/src/main/java/com/za/base/view/loading.kt @@ -16,31 +16,31 @@ import com.za.servicing.R object LoadingManager { - val showLoading = mutableStateOf(false) + val showLoading = mutableStateOf(false) - fun showLoading() { - showLoading.value = true - } + fun showLoading() { + showLoading.value = true + } - fun hideLoading() { - showLoading.value = false - } + fun hideLoading() { + showLoading.value = false + } - @Composable - 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 - .size(70.dp) - .align(Alignment.Center)) - } - } + @Composable + 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 + .size(70.dp) + .align(Alignment.Center)) + } + } } open class LoadingState { - data object Loading : LoadingState() - data object LoadingFailed : LoadingState() - data object LoadingSuccess : LoadingState() + data object Loading : LoadingState() + data class LoadingFailed(val msg : String? = "加载失败") : LoadingState() + data object LoadingSuccess : LoadingState() } \ No newline at end of file diff --git a/servicing/src/main/java/com/za/bean/bean.kt b/servicing/src/main/java/com/za/bean/bean.kt index d44c10a..f2e22ce 100644 --- a/servicing/src/main/java/com/za/bean/bean.kt +++ b/servicing/src/main/java/com/za/bean/bean.kt @@ -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) //车型 \ No newline at end of file diff --git a/servicing/src/main/java/com/za/bean/db/ele/EleWorkOrderBean.kt b/servicing/src/main/java/com/za/bean/db/ele/EleWorkOrderBean.kt index 4965b59..8e998bf 100644 --- a/servicing/src/main/java/com/za/bean/db/ele/EleWorkOrderBean.kt +++ b/servicing/src/main/java/com/za/bean/db/ele/EleWorkOrderBean.kt @@ -4,23 +4,23 @@ import androidx.room.Entity import androidx.room.PrimaryKey @Entity(tableName = "ele_work_order") -data class EleWorkOrderBean( - @PrimaryKey(autoGenerate = false) val orderId: Int, - val userOrderId: Int? = null, - val date: String? = null, //日期 2023-10-20 - val serviceContent: String? = null, //服务须知内容 - val orderWorkStatus: Int? = null, //电子工单状态 1车辆损伤照片 2 客户签名页面 3 服务完成 接车人签名和服务人员签名状态 - val hasBad: Boolean? = null, //车辆是否有损伤 - val carNO: String? = null,//车牌号 - val carVin: String? = null,//车架号 - val orderType: String? = null, //救援类型 - val isSuccess: Int? = null, //服务是否成功 1 成功 其他失败 - val localCustomSignPath: String? = null, - val localAcceptCarSignPath: String? = null,//接车人本地路径 - val localServicePeopleSignPath: String? = null, //服务人员签名 - val serverCustomSignPath: String? = null, - val serverAcceptCarSignPath: String? = null, //远程地址 - val serverServicePeopleSignPath: String? = null, - val hasCreatedEleWorkOrderPhoto: Boolean? = null, //是否已经生成电子工单照片 - val changeBattery: Boolean? = null //是否更换电瓶 -) +data class EleWorkOrderBean(@PrimaryKey(autoGenerate = false) val orderId : Int, + val userOrderId : Int? = null, + val date : String? = null, //日期 2023-10-20 + val serviceContent : String? = null, //服务须知内容 + val orderWorkStatus : Int? = null, //电子工单状态 1车辆损伤照片 2 客户签名页面 3 服务完成 接车人签名和服务人员签名状态 + val hasBad : Boolean? = null, //车辆是否有损伤 + val carNO : String? = null, //车牌号 + val carVin : String? = null, //车架号 + val orderType : String? = null, //救援类型 + val isSuccess : Int? = null, //服务是否成功 1 成功 其他失败 + val localCustomSignPath : String? = null, + val localAcceptCarSignPath : String? = null, //接车人本地路径 + val localServicePeopleSignPath : String? = null, //服务人员签名 + val serverCustomSignPath : String? = null, + val serverAcceptCarSignPath : String? = null, //远程地址 + val serverServicePeopleSignPath : String? = null, + val hasCreatedEleWorkOrderPhoto : Boolean? = null, //是否已经生成电子工单照片 + val changeBattery : Boolean? = null, //是否更换电瓶 + var driverChoiceNoNeedReceiveMoney : Boolean? = null) //师傅是否选择了无需收款 + diff --git a/servicing/src/main/java/com/za/bean/db/order/OrderInfo.kt b/servicing/src/main/java/com/za/bean/db/order/OrderInfo.kt index 92c0867..49c94b8 100644 --- a/servicing/src/main/java/com/za/bean/db/order/OrderInfo.kt +++ b/servicing/src/main/java/com/za/bean/db/order/OrderInfo.kt @@ -7,231 +7,230 @@ import androidx.room.PrimaryKey @Entity(tableName = "order_info") data class OrderInfo( - @PrimaryKey(autoGenerate = false) var taskId: Int? = null, //订单号 "taskId":5313005 - var userOrderId: Int? = null, //订单id - var taskCode: String? = null, //订单编码 "taskCode":"ZD20190308009965" - var customerName: String? = null, //客户姓名 "customerName":"越继安" - var customerPhone: String? = null,//客户电话 "customerPhone":"18078815268" - var carBrand: String? = null,//车辆品牌 "carBrand":"" - var modelVinNo: String? = null, - var carModel: String? = null, //车辆型号 "carModel":"秦" - var carNo: String? = null, //客户车车牌号 "carNo":"粤AF53918" - var carVin: String? = null,//客户车Vin码 "carVin":"LGXC76C30J0132942" - var taskState: String? = null, //订单状态 "taskState":"GOTO" - var nextState: String? = null,//下一步状态 - var electronOrderState: Int? = null, //电子工单状态:1检查损伤 2生成车辆检查表签字 3作业完成签字 - var address: String? = null, //任务地址 "address":"广东省广州市白云区107国道石井凰岗路342号(白云黄石、同德围地区近庆丰兴隆公园)美景大酒店" - var addressProperty: String? = null, //任务地址类型 "addressProperty":"地面" - var addressRemark: String? = null, //事发地地址补充 - var distAddress: String? = null, //目的地地址 "distAddress":"广东省广州市白云区雅岗南大道" - var distAddressRemark: String? = null,//目的地地址补充 - var expectArriveTime: String? = null, //预计到达时间 "expectArriveTime":"2019-03-08 05:11:07" - var serviceTypeName: String? = null, //服务类型 "serviceTypeName":"故障--平板拖车" - var orderSource: String? = null, // "orderSource":"中道救援-比亚迪道路救援项目" - var flowType: Int? = null, //流程类型 0 综合服务 1小修 2拖车 3取送件 4在线技术解决 5组合服务 6拖车简化流程 7武汉交管服务 - var settleType: Int? = null,//结算类型 1 月结 2 现金 - var settleTypeStr: String? = null, //结算类型 - var supplierId: Int? = null, - var startTime: String? = null,//发车时间 - var operationTime: String? = null, //作业完成时间 - var dispatchTime: String? = null,//派单时间 "dispatchTime":"2019-03-08 04:26:07" - var verifyType: Int? = null,//验证类型 "verifyType": 0 跳过验证 1和2 车牌和车架号验证 5 新车验证 - var verifyValue: String? = null,//验证内容 "verifyValue":"粤AF53918" - var holdon: Boolean? = null,//是否被挂起 "holdon":false - var isCurrent: Boolean? = null, //是否为正在做的任务 - var flow: String? = null, //流程状态 "flow":"START,GOTO,VERIFY,EXAMINE,OPERATION,SENDTO,SETTLEMENT,FINISH" - var externalCode: String? = null, // - var plateNumber: String? = null, - var distaddressProperty: String? = null, - var vehiclePointRemark: String? = null, - var destinationRemark: String? = null, - var lat: Double? = null, - var lng: Double? = null, - var distLat: Double? = null, - var distLng: Double? = null, - var hotline: String? = null, - var createTime: String? = null, - var acceptTime: String? = null, - var arriveTime: String? = null, - var arriveDestTime: String? = null, - var needECDevice: Boolean? = null, - var needDestAddress: Boolean? = null, - var linkToDocs: Boolean? = null, - var linkToDaDianH5: Boolean? = null, - var carFactory: Boolean? = null, - var needWaterMarker: Boolean? = null, - var needShowPhoneBrand: Boolean? = null, - var taskNotes: String? = null, //救援要求 - var feeStandard: String? = null, //收费标准 - var customerNotes: String? = null, //客户提醒 - var otherNotes: String? = null, //特殊提醒 - var isNewCar: Boolean? = null, //是否新车 - var ECDeviceString: String? = null,//搭电设备照片提示 - var customerReportImgs: String? = null, //客户上报照片,多张照片用;分隔 - var arriveRemind: String? = null, - var arriveRemindLink: String? = null, - var policyNo: String? = null, // 平安拖车责任险 保单号 - var advanceTime: Long? = null, - var importantTip: String? = null, - var hasReplaceBatteryCapable: Int? = null, //是否有更换电瓶的能力 1 搭电可以更换电瓶 2搭电不可以更换电瓶 其他的不展示 - var taskSuccessStatus: Int? = null, //作业是否完成 0 完成 1未完成 拖车默认完成 - var tyreNumber: Int? = null, //辅助费用 + @PrimaryKey(autoGenerate = false) var taskId : Int? = null, //订单号 "taskId":5313005 + var userOrderId : Int? = null, //订单id + var taskCode : String? = null, //订单编码 "taskCode":"ZD20190308009965" + var customerName : String? = null, //客户姓名 "customerName":"越继安" + var customerPhone : String? = null, //客户电话 "customerPhone":"18078815268" + var carBrand : String? = null, //车辆品牌 "carBrand":"" + var modelVinNo : String? = null, + var carModel : String? = null, //车辆型号 "carModel":"秦" + var carNo : String? = null, //客户车车牌号 "carNo":"粤AF53918" + var carVin : String? = null, //客户车Vin码 "carVin":"LGXC76C30J0132942" + var taskState : String? = null, //订单状态 "taskState":"GOTO" + var nextState : String? = null, //下一步状态 + var electronOrderState : Int? = null, //电子工单状态:1检查损伤 2生成车辆检查表签字 3作业完成签字 + var address : String? = null, //任务地址 "address":"广东省广州市白云区107国道石井凰岗路342号(白云黄石、同德围地区近庆丰兴隆公园)美景大酒店" + var addressProperty : String? = null, //任务地址类型 "addressProperty":"地面" + var addressRemark : String? = null, //事发地地址补充 + var distAddress : String? = null, //目的地地址 "distAddress":"广东省广州市白云区雅岗南大道" + var distAddressRemark : String? = null, //目的地地址补充 + var expectArriveTime : String? = null, //预计到达时间 "expectArriveTime":"2019-03-08 05:11:07" + var serviceTypeName : String? = null, //服务类型 "serviceTypeName":"故障--平板拖车" + var orderSource : String? = null, // "orderSource":"中道救援-比亚迪道路救援项目" + var flowType : Int? = null, //流程类型 0 综合服务 1小修 2拖车 3取送件 4在线技术解决 5组合服务 6拖车简化流程 7武汉交管服务 + var settleType : Int? = null, //结算类型 1 月结 2 现金 + var settleTypeStr : String? = null, //结算类型 + var supplierId : Int? = null, + var startTime : String? = null, //发车时间 + var operationTime : String? = null, //作业完成时间 + var dispatchTime : String? = null, //派单时间 "dispatchTime":"2019-03-08 04:26:07" + var verifyType : Int? = null, //验证类型 "verifyType": 0 跳过验证 1和2 车牌和车架号验证 5 新车验证 + var verifyValue : String? = null, //验证内容 "verifyValue":"粤AF53918" + var holdon : Boolean? = null, //是否被挂起 "holdon":false + var isCurrent : Boolean? = null, //是否为正在做的任务 + var flow : String? = null, //流程状态 "flow":"START,GOTO,VERIFY,EXAMINE,OPERATION,SENDTO,SETTLEMENT,FINISH" + var externalCode : String? = null, // + var plateNumber : String? = null, + var distaddressProperty : String? = null, + var vehiclePointRemark : String? = null, + var destinationRemark : String? = null, + var lat : Double? = null, + var lng : Double? = null, + var distLat : Double? = null, + var distLng : Double? = null, + var hotline : String? = null, + var createTime : String? = null, + var acceptTime : String? = null, + var arriveTime : String? = null, + var arriveDestTime : String? = null, + var needECDevice : Boolean? = null, + var needDestAddress : Boolean? = null, + var linkToDocs : Boolean? = null, + var linkToDaDianH5 : Boolean? = null, + var carFactory : Boolean? = null, + var needWaterMarker : Boolean? = null, + var needShowPhoneBrand : Boolean? = null, + var taskNotes : String? = null, //救援要求 + var feeStandard : String? = null, //收费标准 + var customerNotes : String? = null, //客户提醒 + var otherNotes : String? = null, //特殊提醒 + var isNewCar : Boolean? = null, //是否新车 + var ECDeviceString : String? = null, //搭电设备照片提示 + var customerReportImgs : String? = null, //客户上报照片,多张照片用;分隔 + var arriveRemind : String? = null, + var arriveRemindLink : String? = null, + var policyNo : String? = null, // 平安拖车责任险 保单号 + var advanceTime : Long? = null, + var importantTip : String? = null, + var hasReplaceBatteryCapable : Int? = null, //是否有更换电瓶的能力 1 搭电可以更换电瓶 2搭电不可以更换电瓶 其他的不展示 + var taskSuccessStatus : Int? = null, //作业是否完成 0 完成 1未完成 拖车默认完成 + var tyreNumber : Int? = null, //辅助费用 ) : Parcelable { - constructor(parcel: Parcel) : this( - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readString(), - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readString(), - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readValue(Double::class.java.classLoader) as? Double, - parcel.readValue(Double::class.java.classLoader) as? Double, - parcel.readValue(Double::class.java.classLoader) as? Double, - parcel.readValue(Double::class.java.classLoader) as? Double, - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readValue(Boolean::class.java.classLoader) as? Boolean, - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readString(), - parcel.readValue(Long::class.java.classLoader) as? Long, - parcel.readString(), - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readValue(Int::class.java.classLoader) as? Int, - parcel.readValue(Int::class.java.classLoader) as? Int) { - } + constructor(parcel : Parcel) : this(parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readString(), + parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readString(), + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readValue(Double::class.java.classLoader) as? Double, + parcel.readValue(Double::class.java.classLoader) as? Double, + parcel.readValue(Double::class.java.classLoader) as? Double, + parcel.readValue(Double::class.java.classLoader) as? Double, + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readValue(Boolean::class.java.classLoader) as? Boolean, + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readValue(Long::class.java.classLoader) as? Long, + parcel.readString(), + parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readValue(Int::class.java.classLoader) as? Int, + parcel.readValue(Int::class.java.classLoader) as? Int) { + } - override fun writeToParcel(parcel: Parcel, flags: Int) { - parcel.writeValue(taskId) - parcel.writeValue(userOrderId) - parcel.writeString(taskCode) - parcel.writeString(customerName) - parcel.writeString(customerPhone) - parcel.writeString(carBrand) - parcel.writeString(modelVinNo) - parcel.writeString(carModel) - parcel.writeString(carNo) - parcel.writeString(carVin) - parcel.writeString(taskState) - parcel.writeString(nextState) - parcel.writeValue(electronOrderState) - parcel.writeString(address) - parcel.writeString(addressProperty) - parcel.writeString(addressRemark) - parcel.writeString(distAddress) - parcel.writeString(distAddressRemark) - parcel.writeString(expectArriveTime) - parcel.writeString(serviceTypeName) - parcel.writeString(orderSource) - parcel.writeValue(flowType) - parcel.writeValue(settleType) - parcel.writeString(settleTypeStr) - parcel.writeValue(supplierId) - parcel.writeString(startTime) - parcel.writeString(operationTime) - parcel.writeString(dispatchTime) - parcel.writeValue(verifyType) - parcel.writeString(verifyValue) - parcel.writeValue(holdon) - parcel.writeValue(isCurrent) - parcel.writeString(flow) - parcel.writeString(externalCode) - parcel.writeString(plateNumber) - parcel.writeString(distaddressProperty) - parcel.writeString(vehiclePointRemark) - parcel.writeString(destinationRemark) - parcel.writeValue(lat) - parcel.writeValue(lng) - parcel.writeValue(distLat) - parcel.writeValue(distLng) - parcel.writeString(hotline) - parcel.writeString(createTime) - parcel.writeString(acceptTime) - parcel.writeString(arriveTime) - parcel.writeString(arriveDestTime) - parcel.writeValue(needECDevice) - parcel.writeValue(needDestAddress) - parcel.writeValue(linkToDocs) - parcel.writeValue(linkToDaDianH5) - parcel.writeValue(carFactory) - parcel.writeValue(needWaterMarker) - parcel.writeValue(needShowPhoneBrand) - parcel.writeString(taskNotes) - parcel.writeString(feeStandard) - parcel.writeString(customerNotes) - parcel.writeString(otherNotes) - parcel.writeValue(isNewCar) - parcel.writeString(ECDeviceString) - parcel.writeString(customerReportImgs) - parcel.writeString(arriveRemind) - parcel.writeString(arriveRemindLink) - parcel.writeString(policyNo) - parcel.writeValue(advanceTime) - parcel.writeString(importantTip) - parcel.writeValue(hasReplaceBatteryCapable) - parcel.writeValue(taskSuccessStatus) - parcel.writeValue(tyreNumber) - } + override fun writeToParcel(parcel : Parcel, flags : Int) { + parcel.writeValue(taskId) + parcel.writeValue(userOrderId) + parcel.writeString(taskCode) + parcel.writeString(customerName) + parcel.writeString(customerPhone) + parcel.writeString(carBrand) + parcel.writeString(modelVinNo) + parcel.writeString(carModel) + parcel.writeString(carNo) + parcel.writeString(carVin) + parcel.writeString(taskState) + parcel.writeString(nextState) + parcel.writeValue(electronOrderState) + parcel.writeString(address) + parcel.writeString(addressProperty) + parcel.writeString(addressRemark) + parcel.writeString(distAddress) + parcel.writeString(distAddressRemark) + parcel.writeString(expectArriveTime) + parcel.writeString(serviceTypeName) + parcel.writeString(orderSource) + parcel.writeValue(flowType) + parcel.writeValue(settleType) + parcel.writeString(settleTypeStr) + parcel.writeValue(supplierId) + parcel.writeString(startTime) + parcel.writeString(operationTime) + parcel.writeString(dispatchTime) + parcel.writeValue(verifyType) + parcel.writeString(verifyValue) + parcel.writeValue(holdon) + parcel.writeValue(isCurrent) + parcel.writeString(flow) + parcel.writeString(externalCode) + parcel.writeString(plateNumber) + parcel.writeString(distaddressProperty) + parcel.writeString(vehiclePointRemark) + parcel.writeString(destinationRemark) + parcel.writeValue(lat) + parcel.writeValue(lng) + parcel.writeValue(distLat) + parcel.writeValue(distLng) + parcel.writeString(hotline) + parcel.writeString(createTime) + parcel.writeString(acceptTime) + parcel.writeString(arriveTime) + parcel.writeString(arriveDestTime) + parcel.writeValue(needECDevice) + parcel.writeValue(needDestAddress) + parcel.writeValue(linkToDocs) + parcel.writeValue(linkToDaDianH5) + parcel.writeValue(carFactory) + parcel.writeValue(needWaterMarker) + parcel.writeValue(needShowPhoneBrand) + parcel.writeString(taskNotes) + parcel.writeString(feeStandard) + parcel.writeString(customerNotes) + parcel.writeString(otherNotes) + parcel.writeValue(isNewCar) + parcel.writeString(ECDeviceString) + parcel.writeString(customerReportImgs) + parcel.writeString(arriveRemind) + parcel.writeString(arriveRemindLink) + parcel.writeString(policyNo) + parcel.writeValue(advanceTime) + parcel.writeString(importantTip) + parcel.writeValue(hasReplaceBatteryCapable) + parcel.writeValue(taskSuccessStatus) + parcel.writeValue(tyreNumber) + } - override fun describeContents(): Int { - return 0 - } + override fun describeContents() : Int { + return 0 + } - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): OrderInfo { - return OrderInfo(parcel) - } + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel : Parcel) : OrderInfo { + return OrderInfo(parcel) + } - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } + override fun newArray(size : Int) : Array { + return arrayOfNulls(size) + } + } } \ No newline at end of file diff --git a/servicing/src/main/java/com/za/bean/request/OrderRequest.kt b/servicing/src/main/java/com/za/bean/request/OrderRequest.kt index fa25186..fc23f47 100644 --- a/servicing/src/main/java/com/za/bean/request/OrderRequest.kt +++ b/servicing/src/main/java/com/za/bean/request/OrderRequest.kt @@ -1,56 +1,55 @@ package com.za.bean.request data class OrderListRequest( - val vehicleId: Int? = null, - val deviceId: String? = null, + val vehicleId : Int? = null, + val deviceId : String? = null, ) /** * 照片模版请求数据 */ -data class PhotoTemplateRequest(val userOrderId: Int? = null) +data class PhotoTemplateRequest(val userOrderId : Int? = null) //照片识别 data class OrderPhotoOcrRecognizeRequest( - val userOrderId: Int? = null, - val recognizeType: Int? = null, - val imgUrl: String? = null, + val userOrderId : Int? = null, + val recognizeType : Int? = null, + val imgUrl : String? = null, ) -data class TaskFinishRequest( - val userOrderId: Int? = null, - val lat: Double? = null, - val lng: Double? = null, - val operateTime: Long? = null) +data class TaskFinishRequest(val userOrderId : Int? = null, + val lat : Double? = null, + val lng : Double? = null, + val operateTime : Long? = null) data class GiveUpTaskRequest( - val taskId: Int? = null, //任务id - val userId: Int? = null, //用户id - val vehicleId: Int? = null, //车辆id - val lat: Double? = null, - val lng: Double? = null, - val address: String? = null, - val templatePhotoInfoList: List? = null, //照片数据 - val pushGiveUpFlag: Int? = null, //1 是 0否 + val taskId : Int? = null, //任务id + val userId : Int? = null, //用户id + val vehicleId : Int? = null, //车辆id + val lat : Double? = null, + val lng : Double? = null, + val address : String? = null, + val templatePhotoInfoList : List? = null, //照片数据 + val pushGiveUpFlag : Int? = null, //1 是 0否 ) -data class UpdatePhotoRequest( - val taskId: Int? = null, - val taskState: String? = null, - val picturePosition: Int? = null, //图片位置 - val picturePath: String? = null,//图片路径 - val imgInfo: String? = null -) +data class UpdatePhotoRequest(val taskId : Int? = null, + val taskState : String? = null, + val picturePosition : Int? = null, //图片位置 + val picturePath : String? = null, //图片路径 + val imgInfo : String? = null) -data class SwitchTaskRequest(val currentTaskId: Int? = null, - val nextTaskId: Int? = null, - val userId: Int? = null, - val vehicleId: Int? = null) +data class SwitchTaskRequest(val currentTaskId : Int? = null, + val nextTaskId : Int? = null, + val userId : Int? = null, + val vehicleId : Int? = null) -data class HistoryTasksRequest(val userId: Int? = null, val vehicleId: Int? = null) +data class HistoryTasksRequest(val userId : Int? = null, val vehicleId : Int? = null) -data class HistoryPhotoTemplateRequest(val taskId: Int? = null) +data class HistoryPhotoTemplateRequest(val taskId : Int? = null) -data class HistoryDetailRequest(val taskId: Int? = null) +data class HistoryDetailRequest(val taskId : Int? = null) + +data class TaskNotesRequest(val taskId : Int? = null) diff --git a/servicing/src/main/java/com/za/common/GlobalData.kt b/servicing/src/main/java/com/za/common/GlobalData.kt index 8333d7f..1f634f3 100644 --- a/servicing/src/main/java/com/za/common/GlobalData.kt +++ b/servicing/src/main/java/com/za/common/GlobalData.kt @@ -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() { diff --git a/servicing/src/main/java/com/za/common/ZDManager.kt b/servicing/src/main/java/com/za/common/ZDManager.kt index 5301d89..f30f431 100644 --- a/servicing/src/main/java/com/za/common/ZDManager.kt +++ b/servicing/src/main/java/com/za/common/ZDManager.kt @@ -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) diff --git a/servicing/src/main/java/com/za/common/util/PermissionUtil.kt b/servicing/src/main/java/com/za/common/util/PermissionUtil.kt new file mode 100644 index 0000000..16a8651 --- /dev/null +++ b/servicing/src/main/java/com/za/common/util/PermissionUtil.kt @@ -0,0 +1,5 @@ +package com.za.common.util + +class PermissionUtil { + +} \ No newline at end of file diff --git a/servicing/src/main/java/com/za/net/ApiService.kt b/servicing/src/main/java/com/za/net/ApiService.kt index 084b56d..f90c550 100644 --- a/servicing/src/main/java/com/za/net/ApiService.kt +++ b/servicing/src/main/java/com/za/net/ApiService.kt @@ -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> + + @POST("driverApp/task/getTaskNotes") + fun getTaskNotes(@Body request : TaskNotesRequest) : Observable> } diff --git a/servicing/src/main/java/com/za/net/BaseObserver.kt b/servicing/src/main/java/com/za/net/BaseObserver.kt index bf1ac48..c6d518a 100644 --- a/servicing/src/main/java/com/za/net/BaseObserver.kt +++ b/servicing/src/main/java/com/za/net/BaseObserver.kt @@ -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 : Observer> { override fun onNext(tBaseResponse : BaseResponse) { 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 : Observer> { 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 : Observer> { is SocketTimeoutException -> { doFailure(Const.NetWorkException, "网络连接超时2") LogUtil.print("SocketTimeoutException2", e) + handlerNetError() } else -> { @@ -102,6 +113,15 @@ abstract class BaseObserver : Observer> { 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 { diff --git a/servicing/src/main/java/com/za/net/RetrofitHelper.kt b/servicing/src/main/java/com/za/net/RetrofitHelper.kt index 53a0ec2..cd3bb6f 100644 --- a/servicing/src/main/java/com/za/net/RetrofitHelper.kt +++ b/servicing/src/main/java/com/za/net/RetrofitHelper.kt @@ -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) diff --git a/servicing/src/main/java/com/za/room/RoomHelper.kt b/servicing/src/main/java/com/za/room/RoomHelper.kt index 192e3c8..07a47a0 100644 --- a/servicing/src/main/java/com/za/room/RoomHelper.kt +++ b/servicing/src/main/java/com/za/room/RoomHelper.kt @@ -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 diff --git a/servicing/src/main/java/com/za/service/mqtt/MyMqttClient.kt b/servicing/src/main/java/com/za/service/mqtt/MyMqttClient.kt index c827866..351b9b9 100644 --- a/servicing/src/main/java/com/za/service/mqtt/MyMqttClient.kt +++ b/servicing/src/main/java/com/za/service/mqtt/MyMqttClient.kt @@ -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() { diff --git a/servicing/src/main/java/com/za/signature/GridPaintActivity.java b/servicing/src/main/java/com/za/signature/GridPaintActivity.java index 59bdbe6..d16e739 100644 --- a/servicing/src/main/java/com/za/signature/GridPaintActivity.java +++ b/servicing/src/main/java/com/za/signature/GridPaintActivity.java @@ -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; diff --git a/servicing/src/main/java/com/za/signature/view/HandWriteEditView.java b/servicing/src/main/java/com/za/signature/view/HandWriteEditView.java index d4ed89b..cfd127e 100644 --- a/servicing/src/main/java/com/za/signature/view/HandWriteEditView.java +++ b/servicing/src/main/java/com/za/signature/view/HandWriteEditView.java @@ -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(); //获取光标所在位置 diff --git a/servicing/src/main/java/com/za/ui/camera/ServicePeopleRealActivity.kt b/servicing/src/main/java/com/za/ui/camera/ServicePeopleRealActivity.kt new file mode 100644 index 0000000..f48380a --- /dev/null +++ b/servicing/src/main/java/com/za/ui/camera/ServicePeopleRealActivity.kt @@ -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() { + 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, + 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) + } +} \ No newline at end of file diff --git a/servicing/src/main/java/com/za/ui/h5/CommonH5Activity.kt b/servicing/src/main/java/com/za/ui/h5/CommonH5Activity.kt index 6e577b7..a690574 100644 --- a/servicing/src/main/java/com/za/ui/h5/CommonH5Activity.kt +++ b/servicing/src/main/java/com/za/ui/h5/CommonH5Activity.kt @@ -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 diff --git a/servicing/src/main/java/com/za/ui/new_order/NewOrderActivity.kt b/servicing/src/main/java/com/za/ui/new_order/NewOrderActivity.kt index a0bd082..8d28236 100644 --- a/servicing/src/main/java/com/za/ui/new_order/NewOrderActivity.kt +++ b/servicing/src/main/java/com/za/ui/new_order/NewOrderActivity.kt @@ -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), diff --git a/servicing/src/main/java/com/za/ui/new_order/NewOrderVm.kt b/servicing/src/main/java/com/za/ui/new_order/NewOrderVm.kt index 66b78aa..236ed28 100644 --- a/servicing/src/main/java/com/za/ui/new_order/NewOrderVm.kt +++ b/servicing/src/main/java/com/za/ui/new_order/NewOrderVm.kt @@ -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 @@ -37,237 +39,272 @@ import java.util.Date import java.util.Locale class NewOrderVm : BaseVm() { - private val _uiState = MutableStateFlow(UiState()) - val uiState get() = _uiState + private val _uiState = MutableStateFlow(UiState()) + val uiState get() = _uiState - private var timerJob: Job? = null + private var timerJob : Job? = null - override fun dispatch(action: Action) { - when (action) { - is Action.Init -> init(action.jpushBean) - is Action.AcceptOrder -> acceptOrder() - is Action.RefuseOrder -> refuseOrder() - is Action.StartTimer -> startTimer() - is Action.UpdateTimer -> updateTimer(action.remainingTime) - else -> {} - } - } + override fun dispatch(action : Action) { + when (action) { + is Action.Init -> init(action.jpushBean) + is Action.AcceptOrder -> acceptOrder() + is Action.RefuseOrder -> refuseOrder() + is Action.StartTimer -> startTimer() + is Action.ShowTaskNotes -> showTaskNotes() + is Action.UpdateTimer -> updateTimer(action.remainingTime) + else -> {} + } + } - override fun updateState(uiState: UiState) { - _uiState.value = uiState - } + override fun updateState(uiState : UiState) { + _uiState.value = uiState + } - private fun init(jpushBean: JpushBean?) { - updateState(uiState.value.copy(jpushBean = jpushBean)) - buildMarkers(jpushBean) - searchDrivingRoute(jpushBean) - startTimer() - } + private fun init(jpushBean : JpushBean?) { + updateState(uiState.value.copy(jpushBean = jpushBean)) + buildMarkers(jpushBean) + searchDrivingRoute(jpushBean) + startTimer() + getTaskNote() + } - private fun buildMarkers(jpushBean: JpushBean?) { - val markers = arrayListOf() - 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) - markers.add(startMarkers) - } + private fun getTaskNote() { + val taskNotesRequest = TaskNotesRequest(taskId = uiState.value.jpushBean?.taskId) + RetrofitHelper.getDefaultService().getTaskNotes(taskNotesRequest) + .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BaseObserver() { + override fun doSuccess(it : TaskNotesBean?) { + updateState(uiState.value.copy(taskNotesBean = it)) + } - 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) - markers.add(startMarkers) - } - updateState(uiState.value.copy(markers = markers)) - } + override fun doFailure(code : Int, msg : String?) { + LogUtil.print("获取任务备注失败", "request=$taskNotesRequest msg=$msg") + } + }) + } - private fun acceptOrder() { - LoadingManager.showLoading() - ZdLocationManager.getSingleLocation(success = { - val orderInfo = uiState.value.jpushBean - val acceptOrderRequest = AcceptOrderRequest() - acceptOrderRequest.taskId = orderInfo?.taskId - acceptOrderRequest.vehicleId = GlobalData.driverInfoBean?.vehicleId - acceptOrderRequest.userId = GlobalData.driverInfoBean?.userId - acceptOrderRequest.taskCode = orderInfo?.taskCode - acceptOrderRequest.deviceId = DeviceUtil.getAndroidId(ActivityUtils.getTopActivity()) - acceptOrderRequest.lat = it.latitude - acceptOrderRequest.lng = it.longitude - RetrofitHelper.getDefaultService().acceptOrder(acceptOrderRequest) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BaseObserver() { - override fun doSuccess(it: String?) { - LoadingManager.hideLoading() - LogUtil.print("接单成功", "request=$acceptOrderRequest") - updateState(uiState.value.copy(showCallPhoneDialog = orderInfo?.isNeedCallCustomPhone())) - } + private fun buildMarkers(jpushBean : JpushBean?) { + val markers = arrayListOf() + 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) + markers.add(startMarkers) + } - override fun doFailure(code: Int, msg: String?) { - LoadingManager.hideLoading() - LogUtil.print("接单失败", "request=$acceptOrderRequest msg=$msg") - } - }) - }, failed = { - LoadingManager.hideLoading() - LogUtil.print("接单时获取定位失败", it) - }) - } + 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) + markers.add(startMarkers) + } + updateState(uiState.value.copy(markers = markers)) + } - private fun refuseOrder() { - LoadingManager.showLoading() - 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() { - override fun doSuccess(it: String?) { - LoadingManager.hideLoading() - updateState(uiState.value.copy(refuseSuccess = true)) - LogUtil.print("refuseOrder", "订单拒绝成功") - } + private fun showTaskNotes() { + if (uiState.value.taskNotesBean != null && isNeedShowNotes()) { + updateState(uiState.value.copy(showTaskNotesDialog = true)) + return + } - override fun doFailure(code: Int, msg: String?) { - LoadingManager.hideLoading() - ToastUtils.showShort(msg) - LogUtil.print("refuseOrder", "failed=$msg") - } - }) - } + acceptOrder() + } - private fun startTimer() { - timerJob?.cancel() - timerJob = viewModelScope.launch { - try { - var timeLeft = 120 - while (timeLeft > 0 && isActive) { - delay(1000) - timeLeft-- - updateState(uiState.value.copy(remainingTime = timeLeft)) - } - if (timeLeft == 0 && isActive) { - // 倒计时结束,显示订单超时 - updateState(uiState.value.copy( - isTimeout = true, - showTimeoutDialog = true - )) - } - } catch (e: Exception) { - LogUtil.print("startTimer", "倒计时异常: ${e.message}") - } - } - } + private fun acceptOrder() { + LoadingManager.showLoading() + ZdLocationManager.getSingleLocation(success = { + val orderInfo = uiState.value.jpushBean + val acceptOrderRequest = AcceptOrderRequest() + acceptOrderRequest.taskId = orderInfo?.taskId + acceptOrderRequest.vehicleId = GlobalData.driverInfoBean?.vehicleId + acceptOrderRequest.userId = GlobalData.driverInfoBean?.userId + acceptOrderRequest.taskCode = orderInfo?.taskCode + acceptOrderRequest.deviceId = DeviceUtil.getAndroidId(ActivityUtils.getTopActivity()) + acceptOrderRequest.lat = it.latitude + acceptOrderRequest.lng = it.longitude + RetrofitHelper.getDefaultService().acceptOrder(acceptOrderRequest) + .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BaseObserver() { + override fun doSuccess(it : String?) { + LoadingManager.hideLoading() + LogUtil.print("接单成功", "request=$acceptOrderRequest") + updateState(uiState.value.copy(showCallPhoneDialog = orderInfo?.isNeedCallCustomPhone())) + } - private fun updateTimer(remainingTime: Int) { - updateState(uiState.value.copy(remainingTime = remainingTime)) - } + override fun doFailure(code : Int, msg : String?) { + LoadingManager.hideLoading() + LogUtil.print("接单失败", "request=$acceptOrderRequest msg=$msg") + } + }) + }, failed = { + LoadingManager.hideLoading() + LogUtil.print("接单时获取定位失败", it) + }) + } - private fun searchDrivingRoute(jpushBean: JpushBean?) { - if (GlobalData.currentLocation == null) { - ToastUtils.showShort("获取当前位置失败") - return - } + private fun refuseOrder() { + LoadingManager.showLoading() + 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() { + override fun doSuccess(it : String?) { + LoadingManager.hideLoading() + updateState(uiState.value.copy(refuseSuccess = true)) + LogUtil.print("refuseOrder", "订单拒绝成功") + } - updateState(uiState.value.copy(isLoading = true)) + override fun doFailure(code : Int, msg : String?) { + LoadingManager.hideLoading() + ToastUtils.showShort(msg) + LogUtil.print("refuseOrder", "failed=$msg") + } + }) + } - val startPoint = LatLonPoint( - GlobalData.currentLocation?.latitude ?: 0.0, - GlobalData.currentLocation?.longitude ?: 0.0 - ) + private fun startTimer() { + timerJob?.cancel() + timerJob = viewModelScope.launch { + try { + var timeLeft = 120 + while (timeLeft > 0 && isActive) { + delay(1000) + timeLeft -- + updateState(uiState.value.copy(remainingTime = timeLeft)) + } + if (timeLeft == 0 && isActive) { // 倒计时结束,显示订单超时 + updateState(uiState.value.copy(isTimeout = true, showTimeoutDialog = true)) + } + } catch (e : Exception) { + LogUtil.print("startTimer", "倒计时异常: ${e.message}") + } + } + } - val endPoint = when { - 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 -> { - LatLonPoint(jpushBean.lat, jpushBean.lng) - } - else -> null - } + private fun isNeedShowNotes() : Boolean { + if (! uiState.value.taskNotesBean?.taskNotes.isNullOrBlank()) { + return true + } - if (endPoint == null) return + if (! uiState.value.taskNotesBean?.customerNotes.isNullOrBlank()) { + return true + } - val fromAndTo = RouteSearch.FromAndTo(startPoint, endPoint) - val query = RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DrivingDefault, null, null, "") + if (! uiState.value.taskNotesBean?.feeStandard.isNullOrBlank()) { + return true + } - RouteSearch(GlobalData.application).apply { - setRouteSearchListener(object : RouteSearch.OnRouteSearchListener { - override fun onDriveRouteSearched(result: DriveRouteResult?, errorCode: Int) { - updateState(uiState.value.copy(isLoading = false)) - - if (errorCode == 1000 && result != null && result.paths.isNotEmpty()) { - val path = result.paths[0] - val points = path.steps.flatMap { step -> - step.polyline.map { LatLng(it.latitude, it.longitude) } - } - - val duration = path.duration - val arrivalTime = System.currentTimeMillis() + duration * 1000 - val dateFormat = SimpleDateFormat("HH:mm", Locale.getDefault()) - val estimatedTime = dateFormat.format(Date(arrivalTime)) - - updateState(uiState.value.copy( - routePoints = points, - remainingDistance = path.distance.toDouble(), - estimatedArrivalTime = estimatedTime - )) - } else { - ToastUtils.showShort("路线规划失败,请重试") - LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode") - } - } + if (! uiState.value.taskNotesBean?.otherNotes.isNullOrBlank()) { + return true + } - override fun onBusRouteSearched(p0: BusRouteResult?, p1: Int) {} - override fun onWalkRouteSearched(p0: WalkRouteResult?, p1: Int) {} - override fun onRideRouteSearched(p0: RideRouteResult?, p1: Int) {} - }) - calculateDriveRouteAsyn(query) - } - } + return false + } - override fun onCleared() { - super.onCleared() - try { - timerJob?.cancel() - timerJob = null - } catch (e: Exception) { - LogUtil.print("onCleared", "取消倒计时异常: ${e.message}") - } - } + private fun updateTimer(remainingTime : Int) { + updateState(uiState.value.copy(remainingTime = remainingTime)) + } - sealed class Action { - data class Init(val jpushBean: JpushBean?) : Action() - data object AcceptOrder : Action() - data class UpdateState(val uiState: UiState) : Action() - data object RefuseOrder : Action() - data object StartTimer : Action() - data class UpdateTimer(val remainingTime: Int) : Action() - } + private fun searchDrivingRoute(jpushBean : JpushBean?) { + if (GlobalData.currentLocation == null) { + ToastUtils.showShort("获取当前位置失败") + return + } - data class UiState( - val jpushBean: JpushBean? = null, - val showCallPhoneDialog: Boolean? = false, - val markers: ArrayList? = null, - val refuseSuccess: Boolean? = false, - val remainingTime: Int = 50, - val routePoints: List? = null, - val remainingDistance: Double = 0.0, - val estimatedArrivalTime: String = "", - val isLoading: Boolean = false, - val isTimeout: Boolean = false, - val showTimeoutDialog: Boolean = false - ) + updateState(uiState.value.copy(isLoading = true)) + + 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 -> { + LatLonPoint(jpushBean.distLat, jpushBean.distLng) + } + + jpushBean?.lat != null && jpushBean.lat != 0.0 && jpushBean.lng != null && jpushBean.lng != 0.0 -> { + LatLonPoint(jpushBean.lat, jpushBean.lng) + } + + else -> null + } + + if (endPoint == null) return + + val fromAndTo = RouteSearch.FromAndTo(startPoint, endPoint) + val query = + RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DrivingDefault, null, null, "") + + RouteSearch(GlobalData.application).apply { + setRouteSearchListener(object : RouteSearch.OnRouteSearchListener { + override fun onDriveRouteSearched(result : DriveRouteResult?, errorCode : Int) { + updateState(uiState.value.copy(isLoading = false)) + + if (errorCode == 1000 && result != null && result.paths.isNotEmpty()) { + val path = result.paths[0] + val points = path.steps.flatMap { step -> + step.polyline.map { LatLng(it.latitude, it.longitude) } + } + + val duration = path.duration + val arrivalTime = System.currentTimeMillis() + duration * 1000 + val dateFormat = SimpleDateFormat("HH:mm", Locale.getDefault()) + val estimatedTime = dateFormat.format(Date(arrivalTime)) + + updateState(uiState.value.copy(routePoints = points, + remainingDistance = path.distance.toDouble(), + estimatedArrivalTime = estimatedTime)) + } else { + ToastUtils.showShort("路线规划失败,请重试") + LogUtil.print("searchDrivingRoute", "路径规划失败: errorCode=$errorCode") + } + } + + override fun onBusRouteSearched(p0 : BusRouteResult?, p1 : Int) {} + override fun onWalkRouteSearched(p0 : WalkRouteResult?, p1 : Int) {} + override fun onRideRouteSearched(p0 : RideRouteResult?, p1 : Int) {} + }) + calculateDriveRouteAsyn(query) + } + } + + override fun onCleared() { + super.onCleared() + try { + timerJob?.cancel() + timerJob = null + } catch (e : Exception) { + LogUtil.print("onCleared", "取消倒计时异常: ${e.message}") + } + } + + sealed class Action { + data class Init(val jpushBean : JpushBean?) : Action() + data object AcceptOrder : Action() + data class UpdateState(val uiState : UiState) : Action() + data object RefuseOrder : Action() + data object ShowTaskNotes : Action() + data object StartTimer : Action() + data class UpdateTimer(val remainingTime : Int) : Action() + } + + data class UiState(val jpushBean : JpushBean? = null, + val showCallPhoneDialog : Boolean? = false, + val markers : ArrayList? = null, + val refuseSuccess : Boolean? = false, + val taskNotesBean : TaskNotesBean? = null, + val showTaskNotesDialog : Boolean? = false, + val acceptOrderDialog : Boolean? = null, + val remainingTime : Int = 50, + val routePoints : List? = null, + val remainingDistance : Double = 0.0, + val estimatedArrivalTime : String = "", + val isLoading : Boolean = false, + val isTimeout : Boolean = false, + val showTimeoutDialog : Boolean = false) } \ No newline at end of file diff --git a/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderDetailItemScreen.kt b/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderDetailItemScreen.kt index 9f346c2..bb7ff7d 100644 --- a/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderDetailItemScreen.kt +++ b/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderDetailItemScreen.kt @@ -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,18 +186,40 @@ private fun OrderDetailBaseInformationView(orderInfo : OrderInfo?) { modifier = Modifier.size(15.dp)) } - 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?.carModel}", - color = contentColor, - fontSize = titleSize, - fontWeight = FontWeight.Medium) + + if (! orderInfo?.carModel.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?.carModel}", + color = contentColor, + fontSize = titleSize, + fontWeight = FontWeight.Medium) + } + } + + //拖车责任险 + if (! orderInfo?.policyNo.isNullOrBlank()) { + Spacer(modifier = Modifier.height(10.dp)) + Row(modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically) { + Text(text = "拖车责任险", + color = titleColor, + fontSize = titleSize, + fontWeight = FontWeight.Medium, + modifier = Modifier.width(75.dp)) + Spacer(modifier = Modifier.width(5.dp)) + Text(text = "${orderInfo?.policyNo}", + color = contentColor, + fontSize = titleSize, + fontWeight = FontWeight.Medium) + } } } @@ -211,7 +232,6 @@ private fun OrderDetailServiceInformationView(orderInfo : OrderInfo?) { val titleSize = 12.sp val titleColor = Color(0xFF7A7A7A) val contentColor = Color.Black - val context = LocalContext.current // 1 事发地 2 目的地 var showChoiceMapDialog by remember { mutableStateOf(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)) + } } \ No newline at end of file diff --git a/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderDetailScreen.kt b/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderDetailScreen.kt index 7d2095c..b7c836a 100644 --- a/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderDetailScreen.kt +++ b/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderDetailScreen.kt @@ -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() diff --git a/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderRequirementsScreen.kt b/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderRequirementsScreen.kt index b7b8093..cce9af3 100644 --- a/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderRequirementsScreen.kt +++ b/servicing/src/main/java/com/za/ui/servicing/in_servicing_setting/OrderRequirementsScreen.kt @@ -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, @@ -100,4 +112,90 @@ private fun CarModeView(orderInfo : OrderInfo?) { Spacer(modifier = Modifier.height(12.dp)) HorizontalDivider(color = black5, modifier = Modifier.fillMaxWidth()) } -} \ No newline at end of file +} + +@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() } + } + } +} diff --git a/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/ServicePeopleConfirmScreen.kt b/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/ServicePeopleConfirmScreen.kt index 8431fa2..0820dc0 100644 --- a/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/ServicePeopleConfirmScreen.kt +++ b/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/ServicePeopleConfirmScreen.kt @@ -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) }) } diff --git a/servicing/src/main/java/com/za/ui/servicing/order_confirm/ConfirmH5SuccessVm.kt b/servicing/src/main/java/com/za/ui/servicing/order_confirm/ConfirmH5SuccessVm.kt index 42753e3..55fdfa4 100644 --- a/servicing/src/main/java/com/za/ui/servicing/order_confirm/ConfirmH5SuccessVm.kt +++ b/servicing/src/main/java/com/za/ui/servicing/order_confirm/ConfirmH5SuccessVm.kt @@ -37,7 +37,8 @@ class ConfirmH5SuccessVm : IServicingVm 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 ChangeBatteryScreen() - is OrderConfirmInitVm.OrderConfirmState.ConfirmEle -> ConfirmEleScreen() - is OrderConfirmInitVm.OrderConfirmState.ConfirmH5Success -> ConfirmH5SuccessScreen() - is OrderConfirmInitVm.OrderConfirmState.Init -> { - Box { - LoadingManager.showLoading() - } - } + when (uiState.value.orderConfirmState) { + is OrderConfirmInitVm.OrderConfirmState.ChangeBattery -> ChangeBatteryScreen() + is OrderConfirmInitVm.OrderConfirmState.ConfirmEle -> ConfirmEleScreen() + is OrderConfirmInitVm.OrderConfirmState.ConfirmH5Success -> ConfirmH5SuccessScreen() + is OrderConfirmInitVm.OrderConfirmState.Init -> { + Box { + LoadingManager.showLoading() + } + } - is OrderConfirmInitVm.OrderConfirmState.Failed -> { - Box(modifier = Modifier.clickable { - vm.dispatch(OrderConfirmInitVm.Action.Init) - }) { - Text("点击重试") - } - } + is OrderConfirmInitVm.OrderConfirmState.Failed -> { + Box(modifier = Modifier.clickable { + vm.dispatch(OrderConfirmInitVm.Action.Init) + }) { + Text("点击重试") + } + } - is OrderConfirmInitVm.OrderConfirmState.PaymentInfo -> { - ReceiveMoneyActivity.goReceiveMoney(context, userOrderId = uiState.value.orderInfo?.userOrderId - ?: 0, taskId = uiState.value.orderInfo?.taskId ?: 0) - } + is OrderConfirmInitVm.OrderConfirmState.PaymentInfo -> { + ReceiveMoneyActivity.goReceiveMoney(context, + userOrderId = uiState.value.orderInfo?.userOrderId ?: 0, + taskId = uiState.value.orderInfo?.taskId ?: 0) + } - else -> RealOrderConfirmActivity.goRealOrderConfirm(context) - } + else -> RealOrderConfirmActivity.goRealOrderConfirm(context) + } } \ No newline at end of file diff --git a/servicing/src/main/java/com/za/ui/servicing/order_confirm/OrderConfirmInitVm.kt b/servicing/src/main/java/com/za/ui/servicing/order_confirm/OrderConfirmInitVm.kt index ffa2b61..bee8002 100644 --- a/servicing/src/main/java/com/za/ui/servicing/order_confirm/OrderConfirmInitVm.kt +++ b/servicing/src/main/java/com/za/ui/servicing/order_confirm/OrderConfirmInitVm.kt @@ -17,110 +17,102 @@ import io.reactivex.rxjava3.schedulers.Schedulers import kotlinx.coroutines.flow.MutableStateFlow class OrderConfirmInitVm : IServicingVm() { - private val _uiState = MutableStateFlow(UiState()) - val uiState get() = _uiState - override fun updateState(uiState: UiState) { - _uiState.value = uiState - } + private val _uiState = MutableStateFlow(UiState()) + val uiState get() = _uiState + override fun updateState(uiState : UiState) { + _uiState.value = uiState + } - override fun dispatch(action: Action) { - when (action) { - is Action.Init -> init() - is Action.UpdateState -> updateState(action.uiState) - } - } + override fun dispatch(action : Action) { + when (action) { + is Action.Init -> init() + is Action.UpdateState -> updateState(action.uiState) + } + } - private fun init() { - updateState(uiState.value.copy(orderInfo = getCurrentOrder())) - queryPaymentInfo(uiState.value.orderInfo) - } + private fun init() { + updateState(uiState.value.copy(orderInfo = getCurrentOrder())) + queryPaymentInfo(uiState.value.orderInfo) + } - private fun queryPaymentInfo(orderInfo: OrderInfo?) { - LoadingManager.showLoading() - val paymentInfoRequest = PaymentInfoRequest(orderInfo?.userOrderId, orderInfo?.taskId) - val eleWorkOrderBean = RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId - ?: 0) - RetrofitHelper.getDefaultService().paymentInfoQuery(paymentInfoRequest) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : BaseObserver() { - override fun doSuccess(it: PaymentInfoBean?) { - LoadingManager.hideLoading() - updateState(uiState.value.copy(paymentInfoBean = it, orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean)) - if (it?.isPayment == true && it.tradeState != 2) { - if (it.askPayment == true) { - updateState(uiState.value.copy(showNoNeedPayDialog = true)) - return - } - updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.PaymentInfo)) - return - } + private fun queryPaymentInfo(orderInfo : OrderInfo?) { + LoadingManager.showLoading() + val paymentInfoRequest = PaymentInfoRequest(orderInfo?.userOrderId, orderInfo?.taskId) + val eleWorkOrderBean = + RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId ?: 0) + RetrofitHelper.getDefaultService().paymentInfoQuery(paymentInfoRequest) + .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : BaseObserver() { + override fun doSuccess(it : PaymentInfoBean?) { + LoadingManager.hideLoading() + 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)) + return + } + updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.PaymentInfo)) + return + } - if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 - || orderInfo?.electronOrderState == 2 - ) { - updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle)) - return - } + handlerOtherState(orderInfo, eleWorkOrderBean) + } - 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)) - } + 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()}") + } + }) + } - override fun doFailure(code: Int, msg: String?) { - LoadingManager.hideLoading() - LogUtil.print("eleworkOrder", eleWorkOrderBean.toJson() ?: "") - updateState(uiState.value.copy(orderInfo = orderInfo, eleWorkOrderBean = eleWorkOrderBean)) - if (orderInfo?.electronOrderState == 0 || orderInfo?.electronOrderState == 1 || orderInfo?.electronOrderState == 2) { - updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmEle)) - return - } + 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 + } - if (eleWorkOrderBean?.changeBattery == true) { - updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ChangeBattery)) - 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)) + } - if (eleWorkOrderBean?.hasCreatedEleWorkOrderPhoto == null || eleWorkOrderBean.hasCreatedEleWorkOrderPhoto != true) { - updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.ConfirmH5Success)) - return - } + sealed class Action { + data object Init : Action() + data class UpdateState(val uiState : UiState) : Action() + } - updateState(uiState.value.copy(orderConfirmState = OrderConfirmState.OrderConfirm)) - LogUtil.print("queryPaymentInfo", "failed=$msg request=${paymentInfoRequest.toJson()}") - } - }) - } + data class UiState( + val orderConfirmState : OrderConfirmState = OrderConfirmState.Init, + val orderInfo : OrderInfo? = null, + val eleWorkOrderBean : EleWorkOrderBean? = null, + val goNextPage : UpdateTaskBean? = null, + val isGoNextPageDialog : Boolean? = null, + val paymentInfoBean : PaymentInfoBean? = null, + val showNoNeedPayDialog : Boolean? = null, + ) - sealed class Action { - data object Init : Action() - data class UpdateState(val uiState: UiState) : Action() - } - - data class UiState( - val orderConfirmState: OrderConfirmState = OrderConfirmState.Init, - val orderInfo: OrderInfo? = null, - val eleWorkOrderBean: EleWorkOrderBean? = null, - val goNextPage: UpdateTaskBean? = null, - val isGoNextPageDialog: Boolean? = null, - val paymentInfoBean: PaymentInfoBean? = null, - val showNoNeedPayDialog: Boolean? = null, - ) - - sealed class OrderConfirmState { - data object Init : OrderConfirmState() - data object Failed : OrderConfirmState() - data object PaymentInfo : OrderConfirmState() - data object ConfirmEle : OrderConfirmState() - data object ChangeBattery : OrderConfirmState() - data object ConfirmH5Success : OrderConfirmState() - data object OrderConfirm : OrderConfirmState() - } + sealed class OrderConfirmState { + data object Init : OrderConfirmState() + data object Failed : OrderConfirmState() + data object PaymentInfo : OrderConfirmState() + data object ConfirmEle : OrderConfirmState() + data object ChangeBattery : OrderConfirmState() + data object ConfirmH5Success : OrderConfirmState() + data object OrderConfirm : OrderConfirmState() + } } \ No newline at end of file diff --git a/servicing/src/main/java/com/za/ui/view/CirclePreviewContainer.kt b/servicing/src/main/java/com/za/ui/view/CirclePreviewContainer.kt new file mode 100644 index 0000000..c0786b4 --- /dev/null +++ b/servicing/src/main/java/com/za/ui/view/CirclePreviewContainer.kt @@ -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 + } + } + } +} \ No newline at end of file diff --git a/servicing/src/main/res/drawable/preview_overlay_mask.xml b/servicing/src/main/res/drawable/preview_overlay_mask.xml new file mode 100644 index 0000000..691c21f --- /dev/null +++ b/servicing/src/main/res/drawable/preview_overlay_mask.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + diff --git a/servicing/src/main/res/layout/activity_service_people_real.xml b/servicing/src/main/res/layout/activity_service_people_real.xml new file mode 100644 index 0000000..38a76bf --- /dev/null +++ b/servicing/src/main/res/layout/activity_service_people_real.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + +