feat(servicing): 新增快速登录功能并优化用户信息处理- 新增 FastLoginRequest 数据类用于快速登录

- 添加 iaiCompareFace API 接口用于人脸识别比较
- 更新 DriverInfo 数据类,增加 loginLogId 字段- 重构 GlobalData 中的用户信息存储逻辑,使用 MMKV替代数据库
- 优化 InServicingBottomView 中的订单放弃和拨打电话功能
- 更新 JpushBean,增加 userOrderId 字段
- 修改 AndroidManifest.xml 中的权限声明,使用动态应用ID
This commit is contained in:
songzhiling
2025-04-17 15:08:29 +08:00
parent 0f24648cb1
commit eb7a4585fa
30 changed files with 1626 additions and 1196 deletions

View File

@ -33,13 +33,22 @@ android {
] ]
} }
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
signingConfigs {
release {
storeFile file('E:\\workspace\\study\\zd_sdk_demo\\zd_sdk_demo.jks')
storePassword '123456'
keyAlias 'key0'
keyPassword '123456'
}
}
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11

View File

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

View File

@ -13,7 +13,8 @@ jcore = "3.3.2"
jpush = "4.8.1" jpush = "4.8.1"
location = "5.6.1" location = "5.6.1"
loggingInterceptor = "4.11.0" loggingInterceptor = "4.11.0"
mmkv = "2.1.0" #这是一个长期维护的版本,不建议升级
mmkv = "1.3.11"
orgEclipsePahoAndroidService = "1.1.1" orgEclipsePahoAndroidService = "1.1.1"
orgEclipsePahoClientMqttv3 = "1.2.5" orgEclipsePahoClientMqttv3 = "1.2.5"
permissionx = "1.8.0" permissionx = "1.8.0"

View File

@ -3,8 +3,9 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-sdk tools:overrideLibrary="androidx.camera.view,androidx.camera:camera-camera2,androidx.camera.camera2,androidx.camera.lifecycle,androidx.camera.core" /> <uses-sdk tools:overrideLibrary="androidx.camera.view,androidx.camera:camera-camera2,androidx.camera.camera2,androidx.camera.lifecycle,androidx.camera.core" />
<permission <permission
android:name="com.za.rescue.dealer.permission.JPUSH_MESSAGE" android:name="${applicationId}.permission.JPUSH_MESSAGE"
android:protectionLevel="signature" /> <!-- 位置相关权限 --> android:protectionLevel="signature" /> <!-- 位置相关权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
@ -78,9 +79,7 @@
<activity <activity
android:name="com.za.ui.main.ServiceLauncherActivity" android:name="com.za.ui.main.ServiceLauncherActivity"
android:exported="true" android:exported="true"
android:theme="@style/Theme.Dealer"> android:theme="@style/Theme.Dealer" />
</activity>
<activity <activity
android:name="com.za.ui.main.ServicingMainActivity" android:name="com.za.ui.main.ServicingMainActivity"
android:exported="false" android:exported="false"
@ -162,6 +161,7 @@
<activity <activity
android:name="com.za.ui.camera.ZdCameraXActivity" android:name="com.za.ui.camera.ZdCameraXActivity"
android:exported="false" android:exported="false"
android:theme="@style/Theme.Dealer"
android:screenOrientation="portrait"> android:screenOrientation="portrait">
<meta-data <meta-data
android:name="android.app.lib_name" android:name="android.app.lib_name"
@ -202,7 +202,6 @@
android:exported="true" android:exported="true"
tools:node="replace"> tools:node="replace">
<intent-filter> <intent-filter>
<action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_ACTION" /> <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_ACTION" />
<action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED_ACTION" /> <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED_ACTION" />
<action android:name="cn.jpush.android.intent.CONNECTION" /> <action android:name="cn.jpush.android.intent.CONNECTION" />
@ -243,6 +242,7 @@
<category android:name="${applicationId}" /> <category android:name="${applicationId}" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<service <service
android:name="cn.jpush.android.service.DaemonService" android:name="cn.jpush.android.service.DaemonService"
android:enabled="true" android:enabled="true"

View File

@ -30,6 +30,7 @@ data class GeneralInfo(
val vehicleName : String? = null, val vehicleName : String? = null,
val vehicleState : Int? = null, //车辆状态 0 空闲 1 忙碌 val vehicleState : Int? = null, //车辆状态 0 空闲 1 忙碌
val supplierId : Int? = null, val supplierId : Int? = null,
val loginLogId : Int? = null,
val supplierName : String? = null, val supplierName : String? = null,
val supplierType : Int?, val supplierType : Int?,
val plateNumber : String? = null, //车牌号 val plateNumber : String? = null, //车牌号

View File

@ -16,7 +16,7 @@ data class JpushBean(
val carModel: String? = null,//车辆型号 "carModel":"秦" val carModel: String? = null,//车辆型号 "carModel":"秦"
val contract: String? = null, //车辆型号 "carModel":"秦" val contract: String? = null, //车辆型号 "carModel":"秦"
val typeDesc: String? = null, //推送的附加消息 revoke 撤回 giveUp放弃 reDispatch改派 val typeDesc: String? = null, //推送的附加消息 revoke 撤回 giveUp放弃 reDispatch改派
val userOrderId: Int? = null,
val carNo: String? = null, //客户车车牌号 "carNo":"粤AF53918" val carNo: String? = null, //客户车车牌号 "carNo":"粤AF53918"
val taskState: String? = null, //订单状态 "taskState":"GOTO" val taskState: String? = null, //订单状态 "taskState":"GOTO"
val address: String? = null, //任务地址 "address":"广东省广州市白云区107国道石井凰岗路342号(白云黄石、同德围地区近庆丰兴隆公园)美景大酒店" val address: String? = null, //任务地址 "address":"广东省广州市白云区107国道石井凰岗路342号(白云黄石、同德围地区近庆丰兴隆公园)美景大酒店"

View File

@ -0,0 +1,15 @@
package com.za.bean
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.za.bean.db.order.OrderInfo
class OrderInfoPreviewParameters : PreviewParameterProvider<OrderInfo> {
override val values : Sequence<OrderInfo>
get() = sequenceOf(OrderInfo(taskCode = "123456789",
address = "上海市浦东新区",
distAddress = "上海市浦东新区",
addressProperty = "服务中",
serviceTypeName = "救援",
plateNumber = "沪A12345",
importantTip = "请保持电话畅通,服务人员将尽快联系您。"))
}

View File

@ -1,5 +1,11 @@
package com.za.bean.request package com.za.bean.request
data class DriverFaceCompareRequest(val vehicleId: Int? = null, val driverId: Int? = null, val photoUrl: String? = null) data class DriverFaceCompareRequest(val vehicleId : Int? = null,
val driverId : Int? = null,
val photoUrl : String? = null)
data class DriverFaceCompareBean(val flowId: Int? = null, val shortLink: String? = null, val url: String? = null) data class DriverFaceCompareBean(val flowId : Int? = null,
val shortLink : String? = null,
val url : String? = null)
data class IaiCompareFaceBean(val score : Float? = null, val compareResult : Boolean? = null)

View File

@ -15,6 +15,8 @@ data class LoginRequest(
var dashboardPath: String? = null //里程表照片路径 var dashboardPath: String? = null //里程表照片路径
) )
data class FastLoginRequest(var deviceId: String? = null)
data class PhoneBean( data class PhoneBean(
var versionRelease: String? = null,//系统版本 var versionRelease: String? = null,//系统版本
var model: String? = null,//手机型号 var model: String? = null,//手机型号

View File

@ -5,60 +5,82 @@ import com.amap.api.location.AMapLocation
import com.blankj.utilcode.util.AppUtils import com.blankj.utilcode.util.AppUtils
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.common.log.LogUtil
import com.za.room.RoomHelper import com.za.room.RoomHelper
import com.za.room.db.user.DriverInfoBean import com.za.room.db.user.DriverInfoBean
object GlobalData { object GlobalData {
lateinit var application : Application lateinit var application : Application
private val mmkv : MMKV by lazy { MMKV.defaultMMKV() }
var activityCount : Int = 0 var activityCount : Int = 0
var isMaster = AppUtils.getAppPackageName() == "com.za.rescue.dealer" var isMaster = AppUtils.getAppPackageName() == "com.za.rescue.dealer"
var token : String? = null var token : String? = null
get() { get() {
return MMKV.defaultMMKV().decodeString("ZD_TOKEN", null) return mmkv.decodeString("ZD_TOKEN", null)
} }
set(value) { set(value) {
MMKV.defaultMMKV().encode("ZD_TOKEN", value) mmkv.encode("ZD_TOKEN", value)
field = value field = value
} }
var regid : String? = null var regid : String? = null
get() { get() {
return MMKV.defaultMMKV().decodeString("regid", null) return mmkv.decodeString("regid", null)
} }
set(value) { set(value) {
MMKV.defaultMMKV().encode("regid", value) mmkv.encode("regid", value)
field = value field = value
} }
var aesKey : String? = null var aesKey : String? = null
get() { get() {
return MMKV.defaultMMKV().decodeString("AES_KEY", null) return mmkv.decodeString("AES_KEY", null)
} }
set(value) { set(value) {
MMKV.defaultMMKV().encode("AES_KEY", value) mmkv.encode("AES_KEY", value)
field = value field = value
} }
//新订单是否已经被处理 //新订单是否已经被处理
var isHandlerNewOrder : Boolean? = false var isHandlerNewOrder : Boolean? = false
var driverInfoBean : DriverInfoBean? = null private val lock = Any()
var driverInfoBean : DriverInfoBean?
get() { get() {
val driverInfoBean = RoomHelper.db?.driverInfoDao()?.getDriverInfoFromUserId() synchronized(lock) {
val driverInfoBean =
mmkv.decodeParcelable("driverInfoBean", DriverInfoBean::class.java)
LogUtil.print("driverInfo get", "driverInfoBean = $driverInfoBean")
return driverInfoBean
}
}
set(value) {
synchronized(lock) {
mmkv.encode("driverInfoBean", value)
if (value != null) {
lastLoginBean = value
}
LogUtil.print("driverInfo set", "driverInfoBean = $value")
}
}
var lastLoginBean : DriverInfoBean? = null
get() {
val driverInfoBean = mmkv.decodeParcelable("lastLoginBean", DriverInfoBean::class.java)
field = driverInfoBean field = driverInfoBean
return driverInfoBean return driverInfoBean
} }
set(value) { set(value) {
RoomHelper.db?.driverInfoDao()?.updateDriverInfo(value) mmkv.encode("lastLoginBean", value)
} }
var currentOrder : OrderInfo? = null var currentOrder : OrderInfo? = null
get() { get() {
return MMKV.defaultMMKV().decodeParcelable("currentOrder", OrderInfo::class.java) return mmkv.decodeParcelable("currentOrder", OrderInfo::class.java)
} }
set(value) { set(value) {
MMKV.defaultMMKV().encode("currentOrder", value) mmkv.encode("currentOrder", value)
if (RoomHelper.db?.orderDao()?.getCurrentOrder() == null && value != null) { if (RoomHelper.db?.orderDao()?.getCurrentOrder() == null && value != null) {
RoomHelper.db?.orderDao()?.insertOrder(value) RoomHelper.db?.orderDao()?.insertOrder(value)
} else if (value != null) { } else if (value != null) {
@ -78,10 +100,10 @@ object GlobalData {
var loginTime : Long? = null var loginTime : Long? = null
get() { get() {
return MMKV.defaultMMKV().decodeLong("loginTime", System.currentTimeMillis()) return mmkv.decodeLong("loginTime", System.currentTimeMillis())
} }
set(value) { set(value) {
MMKV.defaultMMKV().encode("loginTime", value ?: System.currentTimeMillis()) mmkv.encode("loginTime", value ?: System.currentTimeMillis())
field = value field = value
} }
@ -89,6 +111,7 @@ object GlobalData {
token = null token = null
aesKey = null aesKey = null
currentLocation = null currentLocation = null
driverInfoBean = null
loginTime = null loginTime = null
} }

View File

@ -37,12 +37,14 @@ import com.za.bean.request.CustomerPaymentCreateRequest
import com.za.bean.request.DriverFaceCompareBean import com.za.bean.request.DriverFaceCompareBean
import com.za.bean.request.DriverFaceCompareRequest import com.za.bean.request.DriverFaceCompareRequest
import com.za.bean.request.ElectronOrderResponse import com.za.bean.request.ElectronOrderResponse
import com.za.bean.request.FastLoginRequest
import com.za.bean.request.FetchVehicleMaintenanceSubmitHistoryRequestBean import com.za.bean.request.FetchVehicleMaintenanceSubmitHistoryRequestBean
import com.za.bean.request.GeneralInfoRequest import com.za.bean.request.GeneralInfoRequest
import com.za.bean.request.GiveUpTaskRequest import com.za.bean.request.GiveUpTaskRequest
import com.za.bean.request.HistoryDetailRequest import com.za.bean.request.HistoryDetailRequest
import com.za.bean.request.HistoryPhotoTemplateRequest import com.za.bean.request.HistoryPhotoTemplateRequest
import com.za.bean.request.HistoryTasksRequest import com.za.bean.request.HistoryTasksRequest
import com.za.bean.request.IaiCompareFaceBean
import com.za.bean.request.LoginRequest import com.za.bean.request.LoginRequest
import com.za.bean.request.OrderListRequest import com.za.bean.request.OrderListRequest
import com.za.bean.request.OrderPhotoOcrRecognizeRequest import com.za.bean.request.OrderPhotoOcrRecognizeRequest
@ -116,6 +118,9 @@ interface ApiService {
@POST("/driverApp/task/login") @POST("/driverApp/task/login")
fun login(@Body info : LoginRequest) : Observable<BaseResponse<DriverInfo?>> fun login(@Body info : LoginRequest) : Observable<BaseResponse<DriverInfo?>>
@POST("/driverApp/supplier/fastLogin")
fun fastLogin(@Body info : FastLoginRequest) : Observable<BaseResponse<DriverInfo?>>
//获取该手机号下面服务商列表 //获取该手机号下面服务商列表
@POST("/driverApp/supplier/getDriverListInfo") @POST("/driverApp/supplier/getDriverListInfo")
fun getDriverListInfo(@Body info : VerifyCodeRequest) : Observable<BaseResponse<List<AppNewDriverInfoDTO>>> fun getDriverListInfo(@Body info : VerifyCodeRequest) : Observable<BaseResponse<List<AppNewDriverInfoDTO>>>
@ -271,4 +276,7 @@ interface ApiService {
@POST("driverApp/base/getVoiceUrl") @POST("driverApp/base/getVoiceUrl")
fun getVoiceUrl(@Body info : AppNewOrderVoiceRequest) : Observable<BaseResponse<String>> fun getVoiceUrl(@Body info : AppNewOrderVoiceRequest) : Observable<BaseResponse<String>>
@POST("driverApp/supplier/iaiCompareFace")
fun iaiCompareFace(@Body info : DriverFaceCompareRequest) : Observable<BaseResponse<IaiCompareFaceBean>>
} }

View File

@ -2,7 +2,6 @@ package com.za.net
import android.net.ParseException import android.net.ParseException
import com.blankj.utilcode.util.ActivityUtils import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.NetworkUtils
import com.blankj.utilcode.util.ToastUtils import com.blankj.utilcode.util.ToastUtils
import com.google.gson.JsonParseException import com.google.gson.JsonParseException
import com.za.base.Const import com.za.base.Const
@ -105,21 +104,6 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
GlobalData.clearUserCache() GlobalData.clearUserCache()
ZdLocationManager.stopContinuousLocation() ZdLocationManager.stopContinuousLocation()
ActivityUtils.finishAllActivities() ActivityUtils.finishAllActivities()
// val oldLoginInfo: LoginInfo = DbManager.getInstance(context).queryLoginInfo()
// val loginInfo: LoginInfo = LoginInfo()
// loginInfo.setSupplierId(oldLoginInfo.getSupplierId())
// loginInfo.setSupplierCode(oldLoginInfo.getSupplierCode())
// loginInfo.setJobNumber(oldLoginInfo.getJobNumber())
// loginInfo.setPassword(oldLoginInfo.getPassword())
// DbManager.getInstance(context).deleteLoginInfo()
// DbManager.getInstance(context).saveLoginInfo(loginInfo)
// SPKit.saveString(context, "userId", "-1")
// SPKit.saveString(context, "vehicleId", "-1")
//
// LBSManager.getInstance().cancel()
// DaemonService.stopService()
// //跳转
// RsaRouter.navigateNoFlag(context, "/page/login")
} catch (e: Exception) { } catch (e: Exception) {
LogUtil.print("handlerTokenExpired", e) LogUtil.print("handlerTokenExpired", e)
} }

View File

@ -27,6 +27,7 @@ import com.za.common.util.DeviceUtil
import com.za.ext.toJson import com.za.ext.toJson
import com.za.offline.OfflineManager import com.za.offline.OfflineManager
import com.za.room.RoomHelper import com.za.room.RoomHelper
import com.za.room.db.user.DriverInfoBean
import com.za.ui.new_order.NewOrderActivity import com.za.ui.new_order.NewOrderActivity
import com.za.ui.order_report.ReportFloatingManager import com.za.ui.order_report.ReportFloatingManager
import com.za.water_marker.WaterMarkerTemplateManager import com.za.water_marker.WaterMarkerTemplateManager
@ -113,6 +114,8 @@ object CommonMethod {
}) })
} }
private var lastFetchGenerateInfoTime : Long = 0L
fun getGenerateInfo(vehicleId : Int? = null, fun getGenerateInfo(vehicleId : Int? = null,
userId : Int? = null, userId : Int? = null,
success : (GeneralInfo) -> Unit = {}, success : (GeneralInfo) -> Unit = {},
@ -130,22 +133,46 @@ object CommonMethod {
LogUtil.print("getGenerateInfo", "获取车辆信息失败") LogUtil.print("getGenerateInfo", "获取车辆信息失败")
return return
} }
val driverInfoBean = GlobalData.driverInfoBean if (GlobalData.driverInfoBean != null && (System.currentTimeMillis() - lastFetchGenerateInfoTime < 1000 * 10)) {
GlobalData.driverInfoBean = driverInfoBean?.copy(vehicleId = it.vehicleId, LogUtil.print("getGenerateInfo", "获取车辆信息成功但是时间间隔小于10秒不更新车辆信息")
vehicleName = it.vehicleName, success(it)
userName = it.userName, return
userPhone = it.userPhone, }
plateNumber = it.plateNumber, if (GlobalData.driverInfoBean == null) {
vehicleState = it.vehicleState, GlobalData.driverInfoBean = DriverInfoBean(vehicleId = it.vehicleId,
supplierType = it.supplierType, vehicleName = it.vehicleName,
userPortrait = it.userPortrait, userName = it.userName,
userId = it.userId, loginLogId = it.loginLogId,
supplierId = it.supplierId, userPhone = it.userPhone,
deviceId = it.deviceId, plateNumber = it.plateNumber,
supplierName = it.supplierName, vehicleState = it.vehicleState,
authStatus = it.authStatus, supplierType = it.supplierType,
serviceList = it.serviceList?.toString()) userPortrait = it.userPortrait,
userId = it.userId,
supplierId = it.supplierId,
deviceId = it.deviceId,
supplierName = it.supplierName,
authStatus = it.authStatus,
serviceList = it.serviceList?.toString())
} else {
GlobalData.driverInfoBean =
GlobalData.driverInfoBean?.copy(vehicleId = it.vehicleId,
vehicleName = it.vehicleName,
userName = it.userName,
userPhone = it.userPhone,
plateNumber = it.plateNumber,
vehicleState = it.vehicleState,
supplierType = it.supplierType,
userPortrait = it.userPortrait,
userId = it.userId,
loginLogId = it.loginLogId,
supplierId = it.supplierId,
deviceId = it.deviceId,
supplierName = it.supplierName,
authStatus = it.authStatus,
serviceList = it.serviceList?.toString())
}
lastFetchGenerateInfoTime = System.currentTimeMillis()
LogUtil.print("GlobalData.driverInfoBean", LogUtil.print("GlobalData.driverInfoBean",
"${GlobalData.driverInfoBean?.toJson()}}") "${GlobalData.driverInfoBean?.toJson()}}")

View File

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

View File

@ -18,14 +18,12 @@ import com.za.room.db.ele_work.EleWorkOrderDao
import com.za.room.db.order.ChangeBatteryDao import com.za.room.db.order.ChangeBatteryDao
import com.za.room.db.order.OrderDao import com.za.room.db.order.OrderDao
import com.za.room.db.order.PhotoTemplateDao import com.za.room.db.order.PhotoTemplateDao
import com.za.room.db.user.DriverInfoBean
import com.za.room.db.user.DriverInfoDao
import com.za.room.db.user.LocalResourceBean import com.za.room.db.user.LocalResourceBean
import com.za.room.db.user.LocalResourceDao import com.za.room.db.user.LocalResourceDao
import com.za.room.db.water_marker.WaterMarkerDao import com.za.room.db.water_marker.WaterMarkerDao
@Database(entities = [EleWorkOrderBean::class, EleCarDamagePhotoBean::class, LocalResourceBean::class, WaterMarkerTemplateBean::class, WaterMarkerItemBean::class, ChangeBatteryPhoto::class, NewPhotoTemplateBean::class, OrderInfo::class, OfflineUpdateTaskBean::class, DriverInfoBean::class, PhotoTemplateInfo::class], @Database(entities = [EleWorkOrderBean::class, EleCarDamagePhotoBean::class, LocalResourceBean::class, WaterMarkerTemplateBean::class, WaterMarkerItemBean::class, ChangeBatteryPhoto::class, NewPhotoTemplateBean::class, OrderInfo::class, OfflineUpdateTaskBean::class, PhotoTemplateInfo::class],
version = RoomHelper.VERSION, version = RoomHelper.VERSION,
exportSchema = false) exportSchema = false)
abstract class GlobalRoom : RoomDatabase() { abstract class GlobalRoom : RoomDatabase() {
@ -43,7 +41,5 @@ abstract class GlobalRoom : RoomDatabase() {
abstract fun offlineTaskDao() : OfflineDao abstract fun offlineTaskDao() : OfflineDao
abstract fun driverInfoDao() : DriverInfoDao
abstract fun localResourceDao() : LocalResourceDao abstract fun localResourceDao() : LocalResourceDao
} }

View File

@ -1,11 +1,10 @@
package com.za.room.db.user package com.za.room.db.user
import androidx.room.Entity import android.os.Parcel
import androidx.room.PrimaryKey import android.os.Parcelable
@Entity(tableName = "driver_info")
data class DriverInfoBean( data class DriverInfoBean(
@PrimaryKey(autoGenerate = false) val userId : Int? = null, //用户id val userId : Int? = null, //用户id
val userName : String? = null, //用户姓名 val userName : String? = null, //用户姓名
val userPhone : String? = null, //用户手机号 val userPhone : String? = null, //用户手机号
val userPortrait : String? = null, //用户头像 val userPortrait : String? = null, //用户头像
@ -23,4 +22,59 @@ data class DriverInfoBean(
val vehicleState : Int? = null, //车辆状态 0 空闲 1 忙碌 val vehicleState : Int? = null, //车辆状态 0 空闲 1 忙碌
val plateNumber : String? = null, //车牌号 val plateNumber : String? = null, //车牌号
val deviceId : String? = null, val deviceId : String? = null,
) ) : Parcelable {
constructor(parcel : Parcel) : this(parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readString(),
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readString(),
parcel.readString(),
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readString(),
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readString(),
parcel.readString()) {
}
override fun writeToParcel(parcel : Parcel, flags : Int) {
parcel.writeValue(userId)
parcel.writeString(userName)
parcel.writeString(userPhone)
parcel.writeString(userPortrait)
parcel.writeString(rongyunToken)
parcel.writeString(logTime)
parcel.writeValue(supplierId)
parcel.writeString(supplierName)
parcel.writeValue(supplierType)
parcel.writeValue(loginLogId)
parcel.writeString(serviceList)
parcel.writeString(assistUserCode)
parcel.writeValue(authStatus)
parcel.writeValue(vehicleId)
parcel.writeString(vehicleName)
parcel.writeValue(vehicleState)
parcel.writeString(plateNumber)
parcel.writeString(deviceId)
}
override fun describeContents() : Int {
return 0
}
companion object CREATOR : Parcelable.Creator<DriverInfoBean> {
override fun createFromParcel(parcel : Parcel) : DriverInfoBean {
return DriverInfoBean(parcel)
}
override fun newArray(size : Int) : Array<DriverInfoBean?> {
return arrayOfNulls(size)
}
}
}

View File

@ -1,40 +0,0 @@
package com.za.room.db.user
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import com.za.common.log.LogUtil
@Dao
interface DriverInfoDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(driverInfoBean : DriverInfoBean)
@Query("SELECT * FROM driver_info")
fun getDriverInfoFromUserId() : DriverInfoBean?
@Query("DELETE FROM driver_info")
fun deleteAll()
@Update(onConflict = OnConflictStrategy.REPLACE)
fun update(driverInfoBean : DriverInfoBean)
@Transaction
fun updateDriverInfo(driverInfoBean : DriverInfoBean?) {
if (driverInfoBean == null) {
LogUtil.print("DriverInfoDao", "updateDriverInfo driverInfoBean is null")
return
}
val driverInfo = getDriverInfoFromUserId()
if (driverInfo != null && driverInfo.userId == driverInfoBean.userId) {
update(driverInfoBean)
return
}
deleteAll()
insert(driverInfoBean)
}
}

View File

@ -65,457 +65,490 @@ import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
class ZdCameraXActivity : AppCompatActivity() { class ZdCameraXActivity : AppCompatActivity() {
private var exposurePopupWindow: PopupWindow? = null private var exposurePopupWindow : PopupWindow? = null
internal enum class FlashMode { internal enum class FlashMode {
auto, open, light, close auto, open, light, close
} }
private var rootView: ConstraintLayout? = null private var rootView : ConstraintLayout? = null
private var imageCapture: ImageCapture? = null private var imageCapture : ImageCapture? = null
private var cameraInfo: CameraInfo? = null private var cameraInfo : CameraInfo? = null
private var exposureState: ExposureState? = null private var exposureState : ExposureState? = null
private var cameraExecutor: ExecutorService? = null private var cameraExecutor : ExecutorService? = null
private var zoomState: LiveData<ZoomState>? = null private var zoomState : LiveData<ZoomState>? = null
private var viewFinder: PreviewView? = null private var viewFinder : PreviewView? = null
private var takePhoto: TakePhotoButtonView? = null private var takePhoto : TakePhotoButtonView? = null
private var ivFlash: ImageView? = null private var ivFlash : ImageView? = null
private var ivChangeCamera: ImageView? = null private var ivChangeCamera : ImageView? = null
private var ivConfirm: ImageView? = null private var ivConfirm : ImageView? = null
private var ivCancel: ImageView? = null private var ivCancel : ImageView? = null
private lateinit var ivPreview: ImageFilterView private lateinit var ivPreview : ImageFilterView
private var ivBack: ImageView? = null private var ivBack : ImageView? = null
private var sliderExposure: AppCompatSeekBar? = null private var sliderExposure : AppCompatSeekBar? = null
private var groupOperation: Group? = null private var groupOperation : Group? = null
private var groupPreview: Group? = null private var groupPreview : Group? = null
private var uri: Uri? = null private var uri : Uri? = null
private var animatorSet: AnimatorSet? = null private var animatorSet : AnimatorSet? = null
private var location: Location? = null private var location : Location? = null
private var camera: Camera? = null private var camera : Camera? = null
private var isBack = true private var isBack = true
private var orientationEventListener: OrientationEventListener? = null private var orientationEventListener : OrientationEventListener? = null
private var flashPopWindow: PopupWindow? = null private var flashPopWindow : PopupWindow? = null
private var flashMode = FlashMode.close private var flashMode = FlashMode.close
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_zd_camera_xactivity) setContentView(R.layout.activity_zd_camera_xactivity)
initView() isBack = intent.getBooleanExtra("isBack", true)
initLocation() initView()
startCamera() initLocation()
setOnClick() startCamera()
setOnClick()
orientationEventListener = object : OrientationEventListener(this) { orientationEventListener = object : OrientationEventListener(this) {
override fun onOrientationChanged(orientation: Int) { override fun onOrientationChanged(orientation : Int) {
if (orientation == ORIENTATION_UNKNOWN || imageCapture == null) { if (orientation == ORIENTATION_UNKNOWN || imageCapture == null) {
print("zdCamerax orientation", "orientation==$orientation") print("zdCamerax orientation", "orientation==$orientation")
return return
} }
when (orientation) { when (orientation) {
in 45..134 -> { in 45 .. 134 -> {
imageCapture?.targetRotation = Surface.ROTATION_270 imageCapture?.targetRotation = Surface.ROTATION_270
} }
in 135..224 -> {
imageCapture?.targetRotation = Surface.ROTATION_180
}
in 225..314 -> {
imageCapture?.targetRotation = Surface.ROTATION_90
}
else -> {
imageCapture?.targetRotation = Surface.ROTATION_0
}
}
}
}
}
@SuppressLint("ObjectAnimatorBinding") in 135 .. 224 -> {
private fun initView() { imageCapture?.targetRotation = Surface.ROTATION_180
cameraExecutor = Executors.newSingleThreadExecutor() }
rootView = findViewById(R.id.viewGroup_parent)
viewFinder = findViewById(R.id.viewFinder)
takePhoto = findViewById(R.id.iv_takePhoto)
ivConfirm = findViewById(R.id.iv_confirm)
ivCancel = findViewById(R.id.iv_cancel)
ivBack = findViewById(R.id.iv_back)
sliderExposure = findViewById(R.id.slider_exposureState)
groupOperation = findViewById(R.id.group_opera)
groupPreview = findViewById(R.id.group_preview)
groupPreview?.visibility = View.GONE
ivChangeCamera = findViewById(R.id.iv_changeCamera)
ivPreview = findViewById(R.id.iv_preview)
ivFlash = findViewById(R.id.iv_flash)
animatorSet = AnimatorSet()
animatorSet?.playTogether(ObjectAnimator.ofFloat(ivCancel, "translationX", 0f, -200f), ObjectAnimator.ofFloat(ivConfirm, "translationX", 200f))
animatorSet?.setDuration(500) in 225 .. 314 -> {
} imageCapture?.targetRotation = Surface.ROTATION_90
}
@SuppressLint("ClickableViewAccessibility") else -> {
private fun setOnClick() { imageCapture?.targetRotation = Surface.ROTATION_0
takePhoto?.setOnClickListener(ClickProxy { v: View? -> takePhoto() }) }
}
}
}
}
ivChangeCamera?.setOnClickListener(ClickProxy { v: View? -> @SuppressLint("ObjectAnimatorBinding")
isBack = !isBack private fun initView() {
if (!isBack) { cameraExecutor = Executors.newSingleThreadExecutor()
if (flashPopWindow != null && flashPopWindow?.isShowing == true) { rootView = findViewById(R.id.viewGroup_parent)
flashPopWindow?.dismiss() viewFinder = findViewById(R.id.viewFinder)
} takePhoto = findViewById(R.id.iv_takePhoto)
flashMode = FlashMode.close ivConfirm = findViewById(R.id.iv_confirm)
ivFlash?.setImageResource(R.drawable.picture_ic_flash_off) ivCancel = findViewById(R.id.iv_cancel)
} ivBack = findViewById(R.id.iv_back)
startCamera() sliderExposure = findViewById(R.id.slider_exposureState)
}) groupOperation = findViewById(R.id.group_opera)
groupPreview = findViewById(R.id.group_preview)
groupPreview?.visibility = View.GONE
ivChangeCamera = findViewById(R.id.iv_changeCamera)
ivPreview = findViewById(R.id.iv_preview)
ivFlash = findViewById(R.id.iv_flash)
animatorSet = AnimatorSet()
animatorSet?.playTogether(ObjectAnimator.ofFloat(ivCancel, "translationX", 0f, - 200f),
ObjectAnimator.ofFloat(ivConfirm, "translationX", 200f))
ivFlash?.setOnClickListener { animatorSet?.setDuration(500)
if (!isBack) { }
ToastUtils.showShort("前置模式下无法开启!")
return@setOnClickListener
}
if (flashPopWindow != null && flashPopWindow?.isShowing==true) {
flashPopWindow?.dismiss()
} else {
showFlashView()
}
}
ivCancel?.setOnClickListener { @SuppressLint("ClickableViewAccessibility")
groupPreview?.visibility = View.GONE private fun setOnClick() {
groupOperation?.visibility = View.VISIBLE takePhoto?.setOnClickListener(ClickProxy { v : View? -> takePhoto() })
}
ivConfirm?.setOnClickListener { ivChangeCamera?.setOnClickListener(ClickProxy { v : View? ->
val intent = Intent() isBack = ! isBack
if (uri != null) { if (! isBack) {
intent.putExtra("path", UriUtils.uri2File(uri).absolutePath) if (flashPopWindow != null && flashPopWindow?.isShowing == true) {
} flashPopWindow?.dismiss()
setResult(RESULT_OK, intent) }
finish() flashMode = FlashMode.close
} ivFlash?.setImageResource(R.drawable.picture_ic_flash_off)
}
startCamera()
})
ivBack?.setOnClickListener { finish() } ivFlash?.setOnClickListener {
if (! isBack) {
ToastUtils.showShort("前置模式下无法开启!")
return@setOnClickListener
}
if (flashPopWindow != null && flashPopWindow?.isShowing == true) {
flashPopWindow?.dismiss()
} else {
showFlashView()
}
}
sliderExposure?.setOnSeekBarChangeListener(object : OnSeekBarChangeListener { ivCancel?.setOnClickListener {
@SuppressLint("SetTextI18n") groupPreview?.visibility = View.GONE
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { groupOperation?.visibility = View.VISIBLE
if (exposureState != null) { }
camera?.cameraControl?.setExposureCompensationIndex(Math.round(progress.toFloat()))
if (exposurePopupWindow != null) {
val textView = exposurePopupWindow?.contentView?.findViewWithTag<TextView>("tv_exposureView")
textView?.text = progress.toString() + ""
}
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) { ivConfirm?.setOnClickListener {
showExposureView() val intent = Intent()
} if (uri != null) {
intent.putExtra("path", UriUtils.uri2File(uri).absolutePath)
}
setResult(RESULT_OK, intent)
finish()
}
override fun onStopTrackingTouch(seekBar: SeekBar) { ivBack?.setOnClickListener { finish() }
viewFinder?.postDelayed({ exposurePopupWindow?.dismiss() }, 1000)
}
})
val cameraXPreviewViewTouchListener = CameraXPreviewViewTouchListener(this) sliderExposure?.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
viewFinder?.setOnTouchListener(cameraXPreviewViewTouchListener) @SuppressLint("SetTextI18n")
cameraXPreviewViewTouchListener.setCustomTouchListener(object : CustomTouchListener { override fun onProgressChanged(seekBar : SeekBar, progress : Int, fromUser : Boolean) {
override fun zoom(delta: Float) { if (exposureState != null) {
if (zoomState == null) { camera?.cameraControl?.setExposureCompensationIndex(Math.round(progress.toFloat()))
return if (exposurePopupWindow != null) {
} val textView =
val currentZoomRatio = zoomState?.value?.zoomRatio exposurePopupWindow?.contentView?.findViewWithTag<TextView>("tv_exposureView")
currentZoomRatio?.let { textView?.text = progress.toString() + ""
camera?.cameraControl?.setZoomRatio(it.times(delta)) }
} }
}
} override fun onStartTrackingTouch(seekBar : SeekBar) {
showExposureView()
}
override fun click(event: MotionEvent) { override fun onStopTrackingTouch(seekBar : SeekBar) {
val action = viewFinder?.meteringPointFactory?.createPoint(event.x, event.y)?.let { FocusMeteringAction.Builder(it).build() } viewFinder?.postDelayed({ exposurePopupWindow?.dismiss() }, 1000)
rootView?.post { showTapView(event.rawX.toInt(), event.rawY.toInt()) } }
action?.let { })
camera?.cameraControl?.startFocusAndMetering(action)
}
}
override fun doubleClick(x: Float, y: Float) { val cameraXPreviewViewTouchListener = CameraXPreviewViewTouchListener(this)
if (zoomState == null) { viewFinder?.setOnTouchListener(cameraXPreviewViewTouchListener)
return cameraXPreviewViewTouchListener.setCustomTouchListener(object : CustomTouchListener {
} override fun zoom(delta : Float) {
val currentZoomRatio = zoomState?.value?.zoomRatio if (zoomState == null) {
zoomState?.value?.minZoomRatio?.let { return
if (currentZoomRatio!! > it) { }
camera?.cameraControl?.setLinearZoom(0f) val currentZoomRatio = zoomState?.value?.zoomRatio
} else { currentZoomRatio?.let {
camera?.cameraControl?.setLinearZoom(0.5f) camera?.cameraControl?.setZoomRatio(it.times(delta))
} }
}
} }
})
}
private fun takePhoto() { override fun click(event : MotionEvent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val action = viewFinder?.meteringPointFactory?.createPoint(event.x, event.y)
ThreadUtils.runOnUiThread { ?.let { FocusMeteringAction.Builder(it).build() }
PermissionX.init(this@ZdCameraXActivity).permissions(Manifest.permission.ACCESS_MEDIA_LOCATION) rootView?.post { showTapView(event.rawX.toInt(), event.rawY.toInt()) }
.request { allGranted: Boolean, grantedList: List<String?>?, deniedList: List<String?>? -> action?.let {
print("ZDCamerax ACCESS_MEDIA_LOCATION 权限请求结果", "allGranted==$allGranted") camera?.cameraControl?.startFocusAndMetering(action)
if (!allGranted) { }
ToastUtils.showLong("权限获取失败") }
finish()
} override fun doubleClick(x : Float, y : Float) {
} if (zoomState == null) {
} return
} }
if (imageCapture == null) { val currentZoomRatio = zoomState?.value?.zoomRatio
ToastUtils.showShort("相机打开失败") zoomState?.value?.minZoomRatio?.let {
finish() if (currentZoomRatio !! > it) {
} camera?.cameraControl?.setLinearZoom(0f)
// val filename = TimeUtils.date2String(Date(), "yyyyMMddHHmmss") } else {
// val contentValues = ContentValues() camera?.cameraControl?.setLinearZoom(0.5f)
// contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, filename) }
// contentValues.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/jpeg") }
// contentValues.put(MediaStore.Images.ImageColumns.DATE_TAKEN, System.currentTimeMillis())
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { }
// contentValues.put(MediaStore.Images.ImageColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) })
// } else { }
// ImageUtils.save2Album()
// contentValues.put(MediaStore.Images.ImageColumns.DATA, getTakePictureParentPath() + File.separator + filename + ".jpg") private fun takePhoto() {
// } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// ThreadUtils.runOnUiThread {
// val outputOptions = ImageCapture.OutputFileOptions.Builder(contentResolver, PermissionX.init(this@ZdCameraXActivity)
// MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues).build() .permissions(Manifest.permission.ACCESS_MEDIA_LOCATION)
.request { allGranted : Boolean, grantedList : List<String?>?, deniedList : List<String?>? ->
print("ZDCamerax ACCESS_MEDIA_LOCATION 权限请求结果",
"allGranted==$allGranted")
if (! allGranted) {
ToastUtils.showLong("权限获取失败")
finish()
}
}
}
}
if (imageCapture == null) {
ToastUtils.showShort("相机打开失败")
finish()
} // val filename = TimeUtils.date2String(Date(), "yyyyMMddHHmmss")
// val contentValues = ContentValues()
// contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, filename)
// contentValues.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/jpeg")
// contentValues.put(MediaStore.Images.ImageColumns.DATE_TAKEN, System.currentTimeMillis())
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// contentValues.put(MediaStore.Images.ImageColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
// } else {
// ImageUtils.save2Album()
// contentValues.put(MediaStore.Images.ImageColumns.DATA, getTakePictureParentPath() + File.separator + filename + ".jpg")
// }
//
// val outputOptions = ImageCapture.OutputFileOptions.Builder(contentResolver,
// MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues).build()
// Create time stamped name and MediaStore entry.
val filename = SimpleDateFormat("yyyyMMddHHmmss",
Locale.getDefault()).format(System.currentTimeMillis())
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/中道救援")
}
}
// Create time stamped name and MediaStore entry. // Create output options object which contains file + metadata
val filename = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()) val outputOptions = ImageCapture.OutputFileOptions.Builder(contentResolver,
.format(System.currentTimeMillis()) MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
val contentValues = ContentValues().apply { contentValues).build()
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/中道救援")
}
}
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions
.Builder(contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
.build()
takePhoto?.startAnimation() takePhoto?.startAnimation()
imageCapture?.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { imageCapture?.takePicture(outputOptions,
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { ContextCompat.getMainExecutor(this),
this@ZdCameraXActivity.uri = outputFileResults.savedUri object : ImageCapture.OnImageSavedCallback {
//保存照片信息 override fun onImageSaved(outputFileResults : ImageCapture.OutputFileResults) {
val exifInterface: ExifInterface this@ZdCameraXActivity.uri = outputFileResults.savedUri //保存照片信息
try { val exifInterface : ExifInterface
exifInterface = ExifInterface(UriUtils.uri2File(outputFileResults.savedUri).absolutePath) try {
exifInterface.setAttribute(ExifInterface.TAG_DATETIME, filename) exifInterface =
if (location != null) { ExifInterface(UriUtils.uri2File(outputFileResults.savedUri).absolutePath)
exifInterface.setGpsInfo(location) exifInterface.setAttribute(ExifInterface.TAG_DATETIME, filename)
if (location?.latitude!! > 0f && location?.longitude != null && location?.longitude!! > 0f) { if (location != null) {
exifInterface.setLatLong(location?.latitude!!, location?.longitude!!) exifInterface.setGpsInfo(location)
} if (location?.latitude !! > 0f && location?.longitude != null && location?.longitude !! > 0f) {
} exifInterface.setLatLong(location?.latitude !!,
location?.longitude !!)
}
}
exifInterface.saveAttributes() exifInterface.saveAttributes()
takePhoto?.cancelAnimation() takePhoto?.cancelAnimation()
groupPreview?.visibility = View.VISIBLE groupPreview?.visibility = View.VISIBLE
if (!this@ZdCameraXActivity.isFinishing) { if (! this@ZdCameraXActivity.isFinishing) {
Glide.with(this@ZdCameraXActivity).load(outputFileResults.savedUri).into(ivPreview) Glide.with(this@ZdCameraXActivity).load(outputFileResults.savedUri)
} .into(ivPreview)
groupOperation?.visibility = View.GONE }
animatorSet?.start() groupOperation?.visibility = View.GONE
} catch (e: IOException) { animatorSet?.start()
takePhoto?.cancelAnimation() } catch (e : IOException) {
ToastUtils.showShort("照片保存失败" + e.message) takePhoto?.cancelAnimation()
print("照片保存失败", e) ToastUtils.showShort("照片保存失败" + e.message)
} print("照片保存失败", e)
} }
}
override fun onError(exception: ImageCaptureException) { override fun onError(exception : ImageCaptureException) {
takePhoto?.cancelAnimation() takePhoto?.cancelAnimation()
ToastUtils.showShort("Photo capture failed" + exception.message) ToastUtils.showShort("Photo capture failed" + exception.message)
print("onCameraError", exception) print("onCameraError", exception)
} }
}) })
} }
private fun startCamera() { private fun startCamera() {
imageCapture = ImageCapture.Builder().setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY).build() imageCapture =
val cameraProviderFuture = ProcessCameraProvider.getInstance(this) ImageCapture.Builder().setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
cameraProviderFuture.addListener({ .build()
try { val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
val cameraProvider = cameraProviderFuture.get() cameraProviderFuture.addListener({
val preview = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3).build() try {
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3).build()
preview.setSurfaceProvider(viewFinder?.surfaceProvider) preview.surfaceProvider = viewFinder?.surfaceProvider
val cameraSelector = if (isBack) CameraSelector.DEFAULT_BACK_CAMERA else CameraSelector.DEFAULT_FRONT_CAMERA val cameraSelector =
preview.targetRotation = Surface.ROTATION_0 if (isBack) CameraSelector.DEFAULT_BACK_CAMERA else CameraSelector.DEFAULT_FRONT_CAMERA
cameraProvider.unbindAll() preview.targetRotation = Surface.ROTATION_0
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture) cameraProvider.unbindAll()
cameraInfo = camera?.cameraInfo camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
zoomState = cameraInfo?.zoomState cameraInfo = camera?.cameraInfo
exposureState = cameraInfo?.exposureState zoomState = cameraInfo?.zoomState
if (exposureState?.isExposureCompensationSupported == true) { exposureState = cameraInfo?.exposureState
sliderExposure?.visibility = View.VISIBLE if (exposureState?.isExposureCompensationSupported == true) {
sliderExposure?.max = exposureState?.exposureCompensationRange?.upper ?: 1 sliderExposure?.visibility = View.VISIBLE
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { sliderExposure?.max = exposureState?.exposureCompensationRange?.upper ?: 1
sliderExposure?.min = exposureState?.exposureCompensationRange?.lower ?: 1 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
} sliderExposure?.min = exposureState?.exposureCompensationRange?.lower ?: 1
} else { }
sliderExposure?.visibility = View.GONE } else {
} sliderExposure?.visibility = View.GONE
} catch (e: ExecutionException) { }
finish() } catch (e : ExecutionException) {
print("相机初始化失败", e) finish()
} catch (e: InterruptedException) { print("相机初始化失败", e)
finish() } catch (e : InterruptedException) {
print("相机初始化失败", e) finish()
} print("相机初始化失败", e)
}, ContextCompat.getMainExecutor(this)) }
} }, ContextCompat.getMainExecutor(this))
}
private fun initLocation() { private fun initLocation() {
val locationManager = ContextCompat.getSystemService(this, LocationManager::class.java) val locationManager = ContextCompat.getSystemService(this, LocationManager::class.java)
if ((ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { if ((ActivityCompat.checkSelfPermission(this,
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA), 102) Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) && ActivityCompat.checkSelfPermission(
} else { this,
if (locationManager != null) { Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) this,
} Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
} this,
} Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA),
102)
} else {
if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
}
}
}
@SuppressLint("RtlHardcoded") @SuppressLint("RtlHardcoded")
private fun showTapView(x: Int, y: Int) { private fun showTapView(x : Int, y : Int) {
if (takePhoto?.visibility == View.GONE) { if (takePhoto?.visibility == View.GONE) {
return return
} }
try { try {
val popupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) val popupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT,
popupWindow.width = 200 ViewGroup.LayoutParams.WRAP_CONTENT)
popupWindow.height = 200 popupWindow.width = 200
val imageView = ImageView(this) popupWindow.height = 200
imageView.setImageResource(R.drawable.focus_focusing) val imageView = ImageView(this)
imageView.scaleType = ImageView.ScaleType.CENTER_CROP imageView.setImageResource(R.drawable.focus_focusing)
popupWindow.contentView = imageView imageView.scaleType = ImageView.ScaleType.CENTER_CROP
popupWindow.showAtLocation(rootView, Gravity.LEFT or Gravity.TOP, x, y) popupWindow.contentView = imageView
viewFinder?.postDelayed({ popupWindow.dismiss() }, 1000) popupWindow.showAtLocation(rootView, Gravity.LEFT or Gravity.TOP, x, y)
viewFinder?.playSoundEffect(SoundEffectConstants.CLICK) viewFinder?.postDelayed({ popupWindow.dismiss() }, 1000)
} catch (e: Exception) { viewFinder?.playSoundEffect(SoundEffectConstants.CLICK)
print("zdCameraX showTapView", e) } catch (e : Exception) {
} print("zdCameraX showTapView", e)
} }
}
@SuppressLint("RtlHardcoded", "SetTextI18n") @SuppressLint("RtlHardcoded", "SetTextI18n")
private fun showExposureView() { private fun showExposureView() {
if (takePhoto?.visibility == View.GONE) { if (takePhoto?.visibility == View.GONE) {
return return
} }
exposurePopupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) exposurePopupWindow =
exposurePopupWindow?.width = 300 PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
exposurePopupWindow?.height = 200 exposurePopupWindow?.width = 300
val textView = TextView(this) exposurePopupWindow?.height = 200
textView.setTextColor(Color.YELLOW) val textView = TextView(this)
textView.textSize = 50f textView.setTextColor(Color.YELLOW)
textView.tag = "tv_exposureView" textView.textSize = 50f
textView.gravity = Gravity.CENTER textView.tag = "tv_exposureView"
textView.text = exposureState?.exposureCompensationIndex.toString() + "" textView.gravity = Gravity.CENTER
exposurePopupWindow?.contentView = textView textView.text = exposureState?.exposureCompensationIndex.toString() + ""
exposurePopupWindow?.showAtLocation(rootView, Gravity.CENTER, 0, 0) exposurePopupWindow?.contentView = textView
} exposurePopupWindow?.showAtLocation(rootView, Gravity.CENTER, 0, 0)
}
@SuppressLint("RtlHardcoded") @SuppressLint("RtlHardcoded")
private fun showFlashView() { private fun showFlashView() {
if (flashPopWindow != null) { if (flashPopWindow != null) {
flashPopWindow = null flashPopWindow = null
} }
flashPopWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) flashPopWindow =
@SuppressLint("InflateParams") val view = LayoutInflater.from(this).inflate(R.layout.item_flash_mode, null) PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val tvFlashAuto = view.findViewById<TextView>(R.id.tvFlashAuto) @SuppressLint("InflateParams") val view =
val tvFlashOpen = view.findViewById<TextView>(R.id.tvFlashOpen) LayoutInflater.from(this).inflate(R.layout.item_flash_mode, null)
val tvFlashLight = view.findViewById<TextView>(R.id.tvFlashLight) val tvFlashAuto = view.findViewById<TextView>(R.id.tvFlashAuto)
val tvFlashClose = view.findViewById<TextView>(R.id.tvFlashClose) val tvFlashOpen = view.findViewById<TextView>(R.id.tvFlashOpen)
val tvFlashLight = view.findViewById<TextView>(R.id.tvFlashLight)
val tvFlashClose = view.findViewById<TextView>(R.id.tvFlashClose)
when (flashMode) { when (flashMode) {
FlashMode.auto -> tvFlashAuto.setTextColor(Color.YELLOW) FlashMode.auto -> tvFlashAuto.setTextColor(Color.YELLOW)
FlashMode.open -> tvFlashOpen.setTextColor(Color.YELLOW) FlashMode.open -> tvFlashOpen.setTextColor(Color.YELLOW)
FlashMode.light -> tvFlashLight.setTextColor(Color.YELLOW) FlashMode.light -> tvFlashLight.setTextColor(Color.YELLOW)
FlashMode.close -> tvFlashClose.setTextColor(Color.YELLOW) FlashMode.close -> tvFlashClose.setTextColor(Color.YELLOW)
} }
tvFlashAuto.setOnClickListener { v: View? -> tvFlashAuto.setOnClickListener { v : View? ->
if (imageCapture != null) { if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false) camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_AUTO imageCapture?.flashMode = ImageCapture.FLASH_MODE_AUTO
ivFlash?.setImageResource(R.drawable.picture_ic_flash_auto) ivFlash?.setImageResource(R.drawable.picture_ic_flash_auto)
flashMode = FlashMode.auto flashMode = FlashMode.auto
} }
flashPopWindow?.dismiss() flashPopWindow?.dismiss()
} }
tvFlashOpen.setOnClickListener { v: View? -> tvFlashOpen.setOnClickListener { v : View? ->
if (imageCapture != null) { if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false) camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_ON imageCapture?.flashMode = ImageCapture.FLASH_MODE_ON
ivFlash?.setImageResource(R.drawable.picture_ic_flash_on) ivFlash?.setImageResource(R.drawable.picture_ic_flash_on)
flashMode = FlashMode.open flashMode = FlashMode.open
} }
flashPopWindow?.dismiss() flashPopWindow?.dismiss()
} }
tvFlashLight.setOnClickListener { v: View? -> tvFlashLight.setOnClickListener { v : View? ->
if (camera != null) { if (camera != null) {
camera?.cameraControl?.enableTorch(true) camera?.cameraControl?.enableTorch(true)
ivFlash?.setImageResource(R.drawable.ic_flash_light) ivFlash?.setImageResource(R.drawable.ic_flash_light)
flashMode = FlashMode.light flashMode = FlashMode.light
} }
flashPopWindow?.dismiss() flashPopWindow?.dismiss()
} }
tvFlashClose.setOnClickListener { v: View? -> tvFlashClose.setOnClickListener { v : View? ->
if (imageCapture != null) { if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false) camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF
ivFlash?.setImageResource(R.drawable.picture_ic_flash_off) ivFlash?.setImageResource(R.drawable.picture_ic_flash_off)
flashMode = FlashMode.close flashMode = FlashMode.close
} }
flashPopWindow?.dismiss() flashPopWindow?.dismiss()
} }
flashPopWindow?.contentView = view flashPopWindow?.contentView = view
flashPopWindow?.showAtLocation(ivFlash, Gravity.LEFT or Gravity.TOP, 0, ivFlash?.y?.toInt()?.minus(view.height) flashPopWindow?.showAtLocation(ivFlash,
?: 0) Gravity.LEFT or Gravity.TOP,
} 0,
ivFlash?.y?.toInt()?.minus(view.height) ?: 0)
}
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
if (camera != null && imageCapture != null) { if (camera != null && imageCapture != null) {
camera?.cameraControl?.enableTorch(false) camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF
ivFlash?.setImageResource(R.drawable.picture_ic_flash_off) ivFlash?.setImageResource(R.drawable.picture_ic_flash_off)
} }
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
orientationEventListener?.enable() orientationEventListener?.enable()
} }
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
orientationEventListener?.disable() orientationEventListener?.disable()
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
cameraExecutor?.shutdown() cameraExecutor?.shutdown()
} }
} }

View File

@ -16,6 +16,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.tooling.preview.Preview
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.blankj.utilcode.util.ToastUtils import com.blankj.utilcode.util.ToastUtils
import com.permissionx.guolindev.PermissionX import com.permissionx.guolindev.PermissionX
@ -195,6 +196,7 @@ class ServiceLauncherActivity : BaseActivity() {
} }
} }
@Preview
@Composable @Composable
private fun LauncherScreen() { private fun LauncherScreen() {
Scaffold { paddingValues -> Scaffold { paddingValues ->

View File

@ -2,22 +2,65 @@ package com.za.ui.main
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
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.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.blankj.utilcode.util.ActivityUtils import com.blankj.utilcode.util.ActivityUtils
import com.za.base.BaseActivity import com.za.base.BaseActivity
import com.za.base.theme.bgColor
import com.za.base.view.CommonButton
import com.za.base.view.HeadView
import com.za.bean.db.order.OrderInfo
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.util.DeviceUtil import com.za.common.util.DeviceUtil
import com.za.ext.convertToFlowName
import com.za.ext.copy
import com.za.ext.finish import com.za.ext.finish
import com.za.ext.goStatusPage import com.za.ext.goStatusPage
import com.za.service.ServiceManager import com.za.service.ServiceManager
import com.za.service.location.ZdLocationManager import com.za.service.location.ZdLocationManager
import com.za.servicing.R
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class ServicingMainActivity : BaseActivity() { class ServicingMainActivity : BaseActivity() {
@ -59,8 +102,8 @@ private fun ServicingMainScreen(jobCode : String? = null,
rescueVehicle : String? = null, rescueVehicle : String? = null,
vm : ServicingMainVm = viewModel()) { vm : ServicingMainVm = viewModel()) {
val uiState = vm.uiState.collectAsStateWithLifecycle() val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
vm.dispatch(ServicingMainVm.Action.Init(jobCode, vm.dispatch(ServicingMainVm.Action.Init(jobCode,
@ -75,12 +118,200 @@ private fun ServicingMainScreen(jobCode : String? = null,
} }
} }
if (uiState.value.state == 2) { Scaffold(topBar = { HeadView("订单信息确认", onBack = { context.finish() }) }, bottomBar = {
Box { if (uiState.value.state == 1) {
Text(text = "加载失败") CommonButton(text = "已确认,去服务") {
GlobalData.currentOrder?.goStatusPage(ActivityUtils.getTopActivity())
}
}
}) {
Box(modifier = Modifier
.fillMaxSize()
.background(bgColor)
.padding(it)) {
if (uiState.value.state == 2) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CommonButton(text = "订单获取异常,点击重新获取") {
vm.dispatch(ServicingMainVm.Action.Init(jobCode,
phone,
taskCode,
vehicleId,
deviceId,
rescueVehicle))
}
}
} else {
Box(modifier = Modifier
.fillMaxSize()
.padding(top = 10.dp),
contentAlignment = Alignment.TopCenter) {
OrderItemView(GlobalData.currentOrder)
}
}
} }
} else {
GlobalData.currentOrder?.goStatusPage(ActivityUtils.getTopActivity())
} }
}
@Composable
private fun OrderItemView(orderInfo : OrderInfo?) {
val context = LocalContext.current
Card(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp, vertical = 5.dp),
shape = RoundedCornerShape(16.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp, hoveredElevation = 4.dp),
colors = CardDefaults.cardColors(containerColor = Color.White)) {
Column(modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)) { // 订单头部
Row(modifier = Modifier
.fillMaxWidth()
.background(brush = Brush.linearGradient(colors = listOf(Color(0xFFF5F9FF),
Color(0xFFEDF4FF)),
start = androidx.compose.ui.geometry.Offset(0f, 0f),
end = androidx.compose.ui.geometry.Offset(Float.POSITIVE_INFINITY, 0f)),
shape = RoundedCornerShape(12.dp))
.padding(horizontal = 12.dp, vertical = 10.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
Row(verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Text(text = orderInfo?.taskCode ?: "",
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF1A1A1A))
Icon(painter = painterResource(R.drawable.sv_copy),
contentDescription = "复制",
modifier = Modifier
.size(25.dp)
.clickable {
orderInfo?.taskCode?.copy(context)
},
tint = Color(0xFF666666))
}
Button(onClick = {
},
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFFF5A2C)),
contentPadding = PaddingValues(horizontal = 14.dp, vertical = 2.dp),
shape = RoundedCornerShape(16.dp),
modifier = Modifier.height(28.dp)) {
Text(text = orderInfo?.convertToFlowName() ?: "",
fontSize = 13.sp,
fontWeight = FontWeight.Medium)
}
}
// 车辆位于
if (! orderInfo?.addressProperty.isNullOrBlank()) {
Row(modifier = Modifier
.fillMaxWidth()
.background(color = Color(0xFFF8F9FA), shape = RoundedCornerShape(10.dp))
.padding(vertical = 12.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically) {
Icon(painter = rememberVectorPainter(image = Icons.Default.Menu),
contentDescription = null,
modifier = Modifier.size(20.dp),
tint = Color(0xFF3364B7))
Spacer(modifier = Modifier.width(8.dp))
Text(text = "服务类型:", fontSize = 13.sp, color = Color(0x802F3059))
Spacer(modifier = Modifier.weight(1f))
Text(text = orderInfo?.serviceTypeName ?: "",
fontSize = 15.sp,
fontWeight = FontWeight.SemiBold,
color = Color(0xFF2F3059))
}
}
// 车牌信息
if (! orderInfo?.plateNumber.isNullOrBlank()) {
Row(modifier = Modifier
.fillMaxWidth()
.background(color = Color(0xFFF8F9FA), shape = RoundedCornerShape(10.dp))
.padding(vertical = 12.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically) {
Icon(painter = painterResource(id = R.drawable.sv_map_marker_driver),
contentDescription = null,
modifier = Modifier.size(20.dp),
tint = Color(0xFF3364B7))
Spacer(modifier = Modifier.width(8.dp))
Text(text = "车牌号:", fontSize = 13.sp, color = Color(0x802F3059))
Spacer(modifier = Modifier.weight(1f))
Text(text = orderInfo?.plateNumber ?: "",
fontSize = 15.sp,
fontWeight = FontWeight.SemiBold,
color = Color(0xFF2F3059))
}
}
// 地址信息容器
Column(modifier = Modifier
.fillMaxWidth()
.background(color = Color(0xFFFAFBFC), shape = RoundedCornerShape(12.dp))
.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { // 救援地址信息
AddressItem(icon = {
Box(modifier = Modifier
.size(10.dp)
.background(Color(0xFFFD8205), CircleShape))
}, label = "救援地", address = orderInfo?.address ?: "")
// 只有当目的地不为空时才显示分割线和目的地信息
if (! orderInfo?.distAddress.isNullOrBlank()) {
HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp),
color = Color(0xFFEEEEEE))
AddressItem(icon = {
Box(modifier = Modifier
.size(10.dp)
.background(Color(0xFF5CC4BF), CircleShape))
}, label = "目的地", address = orderInfo?.distAddress ?: "")
}
}
//重要提示
if (! orderInfo?.importantTip.isNullOrBlank()) { // 订单状态
Box(modifier = Modifier
.fillMaxWidth()
.background(brush = Brush.linearGradient(colors = listOf(Color(0xFFFFF3E0),
Color(0xFFFFE0B2)),
start = androidx.compose.ui.geometry.Offset(0f, 0f),
end = androidx.compose.ui.geometry.Offset(Float.POSITIVE_INFINITY, 0f)),
shape = RoundedCornerShape(10.dp))
.padding(12.dp),
contentAlignment = Alignment.Center) {
Text(text = orderInfo?.importantTip ?: "",
color = Color(0xFFFF5722),
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.basicMarquee())
}
}
}
}
}
@Composable
private fun AddressItem(icon : @Composable () -> Unit, label : String, address : String) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Row(verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)) {
icon()
Text(text = label, fontSize = 13.sp, color = Color(0x802F3059))
}
Text(text = address,
fontSize = 15.sp,
color = Color(0xFF2F3059),
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 18.dp))
}
}
@Preview
@Composable
private fun ServiceMainPreview() {
ServicingMainScreen()
} }

View File

@ -48,6 +48,9 @@ class ServicingMainVm : BaseVm<ServicingMainVm.Action, ServicingMainVm.UiState>(
LoadingManager.hideLoading() LoadingManager.hideLoading()
GlobalData.currentOrder = orderInfo GlobalData.currentOrder = orderInfo
updateState(uiState.value.copy(state = 1)) updateState(uiState.value.copy(state = 1))
if (orderInfos.isNullOrEmpty()) {
ToastUtils.showShort("未查询到订单")
}
}, },
failed = { failed = {
updateState(uiState.value.copy(state = 2)) updateState(uiState.value.copy(state = 2))

View File

@ -57,7 +57,6 @@ import coil.compose.AsyncImage
import com.amap.api.location.AMapLocationClient import com.amap.api.location.AMapLocationClient
import com.amap.api.maps.CameraUpdateFactory import com.amap.api.maps.CameraUpdateFactory
import com.amap.api.maps.MapView import com.amap.api.maps.MapView
import com.amap.api.maps.model.BitmapDescriptorFactory
import com.amap.api.maps.model.LatLng import com.amap.api.maps.model.LatLng
import com.amap.api.maps.model.LatLngBounds import com.amap.api.maps.model.LatLngBounds
import com.amap.api.maps.model.MarkerOptions import com.amap.api.maps.model.MarkerOptions

View File

@ -43,102 +43,124 @@ import com.za.ui.view.SignatureView
class OrderGiveUpActivity : BaseActivity() { class OrderGiveUpActivity : BaseActivity() {
@Composable @Composable
override fun ContentView() { override fun ContentView() {
val orderInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val orderInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra("orderInfo", OrderInfo::class.java) intent.getParcelableExtra("orderInfo", OrderInfo::class.java)
} else { } else {
intent.getParcelableExtra("orderInfo") intent.getParcelableExtra("orderInfo")
} }
OrderGiveUpScreen(orderInfo = orderInfo, taskId = intent.getIntExtra("taskId", 0), giveUpType = intent.getIntExtra("giveUpType", 0)) OrderGiveUpScreen(orderInfo = orderInfo,
} taskId = intent.getIntExtra("taskId", 0),
giveUpType = intent.getIntExtra("giveUpType", 0))
}
companion object { companion object {
//giveUp type //giveUp type
//0 师傅手机操作放弃 1 后台操作放弃 2 师傅出发前后台操作放弃 //0 师傅手机操作放弃 1 后台操作放弃 2 师傅出发前后台操作放弃
fun goOrderGiveUpActivity(context: Context, fun goOrderGiveUpActivity(context : Context,
orderInfo: OrderInfo? = null, orderInfo : OrderInfo? = null,
taskId: Int? = null, taskId : Int? = null,
giveUpType: Int) { userOrderId : Int? = null,
val intent = Intent(context, OrderGiveUpActivity::class.java) giveUpType : Int) {
intent.putExtra("giveUpType", giveUpType) val intent = Intent(context, OrderGiveUpActivity::class.java)
intent.putExtra("orderInfo", orderInfo) intent.putExtra("giveUpType", giveUpType)
intent.putExtra("taskId", taskId) intent.putExtra("orderInfo", orderInfo)
context.startActivity(intent) intent.putExtra("taskId", taskId)
} intent.putExtra("userOrderId", userOrderId)
} context.startActivity(intent)
}
}
} }
@Composable @Composable
fun OrderGiveUpScreen(vm: OrderGiveUpVm = viewModel(), orderInfo: OrderInfo?, taskId: Int, giveUpType: Int) { fun OrderGiveUpScreen(vm : OrderGiveUpVm = viewModel(),
val uiState = vm.uiState.collectAsStateWithLifecycle() orderInfo : OrderInfo?,
val context = LocalContext.current taskId : Int,
giveUpType : Int) {
val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
LaunchedEffect(key1 = Unit) { LaunchedEffect(key1 = Unit) {
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(orderInfo = orderInfo, taskId = taskId, giveUpType = giveUpType))) vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(orderInfo = orderInfo,
vm.dispatch(OrderGiveUpVm.Action.Init) taskId = taskId,
} giveUpType = giveUpType)))
vm.dispatch(OrderGiveUpVm.Action.Init)
}
if (uiState.value.orderGiveUpSuccess == true) { if (uiState.value.orderGiveUpSuccess == true) {
context.finish() context.finish()
} }
if (uiState.value.isGoNextPageDialog == true) { if (uiState.value.isGoNextPageDialog == true) {
CommonDialog(cancelText = "取消", confirmText = "是否确认放弃订单", title = "放弃订单", cancelEnable = true, cancel = { CommonDialog(cancelText = "取消",
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false))) confirmText = "是否确认放弃订单",
}, dismiss = { title = "放弃订单",
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false))) cancelEnable = true,
}, confirm = { cancel = {
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false))) vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
vm.dispatch(OrderGiveUpVm.Action.UpdateTask) },
}) dismiss = {
} vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
},
confirm = {
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
vm.dispatch(OrderGiveUpVm.Action.UpdateTask)
})
}
Scaffold(topBar = { Scaffold(topBar = {
HeadView(title = "放弃信息", onBack = { context.finish() }) HeadView(title = "放弃信息", onBack = { context.finish() })
}, bottomBar = { }, bottomBar = {
CommonButton(text = "提交信息,确认放弃案件") { CommonButton(text = "提交信息,确认放弃案件") {
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = true))) vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = true)))
} }
}) { it -> }) { it ->
LazyColumn(modifier = Modifier LazyColumn(modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(it), contentPadding = PaddingValues(10.dp)) { .padding(it),
contentPadding = PaddingValues(10.dp)) {
itemsIndexed(items = uiState.value.photoTemplateList itemsIndexed(items = uiState.value.photoTemplateList ?: arrayListOf(),
?: arrayListOf(), key = { _, item -> item.hashCode() }) { index: Int, item: PhotoTemplateInfo -> key = { _, item -> item.hashCode() }) { index : Int, item : PhotoTemplateInfo ->
InServicingPhotoView(photoTemplateInfo = item, index = index + 1, success = { InServicingPhotoView(photoTemplateInfo = item, index = index + 1, success = {
vm.dispatch(OrderGiveUpVm.Action.UpdatePhotoTemplate(it)) vm.dispatch(OrderGiveUpVm.Action.UpdatePhotoTemplate(it))
}) })
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
} }
if (uiState.value.giveUpType == 0) { if (uiState.value.giveUpType == 0) {
item { item {
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 10.dp) .padding(horizontal = 10.dp)
.background(color = Color.White, shape = RoundedCornerShape(6.dp)) .background(color = Color.White, shape = RoundedCornerShape(6.dp))
.padding(10.dp)) { .padding(10.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier.fillMaxWidth(),
Text(text = "司机签名", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) verticalAlignment = Alignment.CenterVertically) {
Spacer(Modifier.weight(1f)) Text(text = "司机签名",
AsyncImage(model = R.drawable.sv_sign_new, contentDescription = "", modifier = Modifier.size(20.dp)) color = Color.Black,
} fontWeight = FontWeight.Medium,
fontSize = 14.sp)
Spacer(Modifier.weight(1f))
AsyncImage(model = R.drawable.sv_sign_new,
contentDescription = "",
modifier = Modifier.size(20.dp))
}
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
SignatureView(modifier = Modifier SignatureView(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(122.dp), success = { .height(122.dp), success = {
vm.dispatch(OrderGiveUpVm.Action.UploadSign(it)) vm.dispatch(OrderGiveUpVm.Action.UploadSign(it))
}, serverPath = uiState.value.serverServicePeopleSignPath) }, serverPath = uiState.value.serverServicePeopleSignPath)
} }
} }
} }
} }
} }
} }

View File

@ -46,186 +46,287 @@ import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: OrderInfo?, isCanBack: Boolean = true) { fun InServicingHeadView(title : String,
val context = LocalContext.current onBack : () -> Unit = {},
val showBottomSheetDialog = remember { mutableStateOf(false) } orderInfo : OrderInfo?,
val showNavigationDialog = remember { mutableStateOf(false) } isCanBack : Boolean = true) {
val showCallPhoneDialog = remember { mutableStateOf(false) } val context = LocalContext.current
val showBottomSheetDialog = remember { mutableStateOf(false) }
val showNavigationDialog = remember { mutableStateOf(false) }
val showCallPhoneDialog = remember { mutableStateOf(false) }
val showCallServicePhoneDialog = remember { mutableStateOf(false) }
if (showNavigationDialog.value) { if (showNavigationDialog.value) {
StartNavigationView(orderInfo = orderInfo, dismiss = { showNavigationDialog.value = false }) StartNavigationView(orderInfo = orderInfo, dismiss = { showNavigationDialog.value = false })
} }
if (showCallPhoneDialog.value) { if (showCallPhoneDialog.value) {
CommonDialog( CommonDialog(cancelText = "取消",
cancelText = "取消", confirmText = "确定",
confirmText = "确定", title = "是否联系客户?",
title = "是否联系客户?", cancelEnable = true,
cancelEnable = true, cancel = {
cancel = { showCallPhoneDialog.value = false
showCallPhoneDialog.value = false },
}, dismiss = { showCallPhoneDialog.value = false },
dismiss = { showCallPhoneDialog.value = false }, confirm = {
confirm = { showCallPhoneDialog.value = false
showCallPhoneDialog.value = false context.callPhone(orderInfo?.customerPhone)
context.callPhone(orderInfo?.customerPhone) context.finish()
context.finish() })
}) }
}
if (showCallServicePhoneDialog.value) {
CommonDialog(cancelText = "取消",
confirmText = "确定",
title = "是否联系中道客服?",
cancelEnable = true,
cancel = {
showCallServicePhoneDialog.value = false
},
dismiss = { showCallServicePhoneDialog.value = false },
confirm = {
showCallServicePhoneDialog.value = false
context.callPhone(orderInfo?.hotline)
context.finish()
})
}
if (showBottomSheetDialog.value) { if (showBottomSheetDialog.value) {
ModalBottomSheet(containerColor = Color.White, ModalBottomSheet(containerColor = Color.White,
onDismissRequest = { showBottomSheetDialog.value = false }) { onDismissRequest = { showBottomSheetDialog.value = false }) {
Column(modifier = Modifier Column(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(10.dp)) { .padding(10.dp)) {
if (!orderInfo?.customerReportImgs.isNullOrBlank()) { if (! orderInfo?.customerReportImgs.isNullOrBlank()) {
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { .clickable {
showBottomSheetDialog.value = false showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ON_SITE_PHOTO) OrderRequirementsActivity.goOrderRequirementsActivity(context,
}, contentAlignment = Alignment.Center) { orderInfo,
Text(text = "现场照片", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black) type = Const.InServiceSettingType.ON_SITE_PHOTO)
} }, contentAlignment = Alignment.Center) {
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5) Text(text = "现场照片",
} fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
}
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { .clickable {
showNavigationDialog.value = true showNavigationDialog.value = true
showBottomSheetDialog.value = false showBottomSheetDialog.value = false
}, contentAlignment = Alignment.Center) { }, contentAlignment = Alignment.Center) {
Text(text = "开启外部导航", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black) Text(text = "开启外部导航",
} fontSize = 15.sp,
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5) fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { .clickable {
showBottomSheetDialog.value = false showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ORDER_REQUIREMENTS) OrderRequirementsActivity.goOrderRequirementsActivity(context,
}, contentAlignment = Alignment.Center) { orderInfo,
Text(text = "案件要求", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black) type = Const.InServiceSettingType.ORDER_REQUIREMENTS)
} }, contentAlignment = Alignment.Center) {
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5) Text(text = "案件要求",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { .clickable {
showBottomSheetDialog.value = false showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ORDER_DETAIL) OrderRequirementsActivity.goOrderRequirementsActivity(context,
}, contentAlignment = Alignment.Center) { orderInfo,
Text(text = "案件详情", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black) type = Const.InServiceSettingType.ORDER_DETAIL)
} }, contentAlignment = Alignment.Center) {
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5) Text(text = "案件详情",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { .clickable {
showBottomSheetDialog.value = false showBottomSheetDialog.value = false
OrderGiveUpActivity.goOrderGiveUpActivity(context, orderInfo = orderInfo, giveUpType = 0) OrderGiveUpActivity.goOrderGiveUpActivity(context,
}, contentAlignment = Alignment.Center) { orderInfo = orderInfo,
Text(text = "订单放弃", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Red) userOrderId = orderInfo?.userOrderId,
} giveUpType = 0)
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5) }, contentAlignment = Alignment.Center) {
Text(text = "订单放弃",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { .clickable {
showBottomSheetDialog.value = false showBottomSheetDialog.value = false
showCallPhoneDialog.value = true showCallPhoneDialog.value = true
}, contentAlignment = Alignment.Center) { }, contentAlignment = Alignment.Center) {
Text(text = "拨打电话", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black) Text(text = "拨打客户电话",
} fontSize = 15.sp,
Spacer(modifier = Modifier.height(10.dp)) fontWeight = FontWeight.Medium,
color = Color.Black)
}
} HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
}
} Box(modifier = Modifier
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
showCallServicePhoneDialog.value = true
}, contentAlignment = Alignment.Center) {
Text(text = "联系中道客服",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
Spacer(modifier = Modifier.height(10.dp))
}
}
}
CenterAlignedTopAppBar(modifier = Modifier CenterAlignedTopAppBar(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.background(color = headBgColor) .background(color = headBgColor)
.padding(top = headPadding), .padding(top = headPadding),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors().copy(containerColor = headBgColor, titleContentColor = Color.White), colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) }, .copy(containerColor = headBgColor, titleContentColor = Color.White),
navigationIcon = { title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
if (isCanBack) { navigationIcon = {
AsyncImage(model = R.drawable.sv_back, contentDescription = "", modifier = Modifier if (isCanBack) {
.size(40.dp) AsyncImage(model = R.drawable.sv_back,
.clickable { onBack() } contentDescription = "",
.padding(10.dp)) modifier = Modifier
} .size(40.dp)
}, actions = { .clickable { onBack() }
Box(modifier = Modifier .padding(10.dp))
.size(39.dp) }
.clickable { showBottomSheetDialog.value = true } },
.padding(10.dp), contentAlignment = Alignment.Center) { actions = {
AsyncImage(model = R.drawable.sv_setting, contentDescription = "", modifier = Modifier.fillMaxSize()) Box(modifier = Modifier
} .size(39.dp)
}) .clickable { showBottomSheetDialog.value = true }
.padding(10.dp), contentAlignment = Alignment.Center) {
AsyncImage(model = R.drawable.sv_setting,
contentDescription = "",
modifier = Modifier.fillMaxSize())
}
})
} }
@Composable @Composable
fun StartNavigationView(orderInfo: OrderInfo?, dismiss: () -> Unit) { fun StartNavigationView(orderInfo : OrderInfo?, dismiss : () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
Dialog(onDismissRequest = { dismiss() }) { Dialog(onDismissRequest = { dismiss() }) {
Row(modifier = Modifier Row(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(180.dp) .height(180.dp)
.background(color = Color.White, shape = RoundedCornerShape(8.dp)) .background(color = Color.White, shape = RoundedCornerShape(8.dp))
.padding(10.dp), verticalAlignment = Alignment.CenterVertically, .padding(10.dp),
horizontalArrangement = Arrangement.SpaceAround) { verticalAlignment = Alignment.CenterVertically,
if (MapUtil.isGdMapInstalled(context)) { horizontalArrangement = Arrangement.SpaceAround) {
Column(modifier = Modifier.clickable { if (MapUtil.isGdMapInstalled(context)) {
if ("SENDTO" == orderInfo?.taskState) { Column(modifier = Modifier.clickable {
MapUtil.startNavigationGd(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress) if ("SENDTO" == orderInfo?.taskState) {
} else { MapUtil.startNavigationGd(context,
MapUtil.startNavigationGd(context, orderInfo?.lat, lng = orderInfo?.lng, address = orderInfo?.address) orderInfo.distLat,
} lng = orderInfo.distLng,
dismiss() address = orderInfo.distAddress)
}) { } else {
AsyncImage(model = R.drawable.sv_amap_icon, contentDescription = "", modifier = Modifier.size(60.dp)) MapUtil.startNavigationGd(context,
Spacer(modifier = Modifier.height(5.dp)) orderInfo?.lat,
Text(text = "高德地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) lng = orderInfo?.lng,
} address = orderInfo?.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)) { if (MapUtil.isBaiduMapInstalled(context)) {
Column(modifier = Modifier.clickable { Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) { if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationBd(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress) MapUtil.startNavigationBd(context,
} else { orderInfo.distLat,
MapUtil.startNavigationBd(context, orderInfo?.lat, lng = orderInfo?.lng, address = orderInfo?.address) lng = orderInfo.distLng,
} address = orderInfo.distAddress)
dismiss() } else {
}) { MapUtil.startNavigationBd(context,
AsyncImage(model = R.drawable.sv_baidu_icon, contentDescription = "", modifier = Modifier.size(60.dp)) orderInfo?.lat,
Spacer(modifier = Modifier.height(5.dp)) lng = orderInfo?.lng,
Text(text = "百度地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) address = orderInfo?.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)) { if (MapUtil.isTencentInstalled(context)) {
Column(modifier = Modifier.clickable { Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) { if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationTencent(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress) MapUtil.startNavigationTencent(context,
} else { orderInfo.distLat,
MapUtil.startNavigationTencent(context, orderInfo?.lat, lng = orderInfo?.lng, address = orderInfo?.address) lng = orderInfo.distLng,
} address = orderInfo.distAddress)
dismiss() } else {
}) { MapUtil.startNavigationTencent(context,
AsyncImage(model = R.drawable.sv_tencent_icon, contentDescription = "", modifier = Modifier.size(60.dp)) orderInfo?.lat,
Spacer(modifier = Modifier.height(5.dp)) lng = orderInfo?.lng,
Text(text = "腾讯地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp) address = orderInfo?.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)
}
}
}
}
} }

View File

@ -1,8 +1,12 @@
package com.za.ui.servicing.wait_to_start package com.za.ui.servicing.wait_to_start
import android.app.Activity
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -55,476 +59,367 @@ import com.amap.api.maps.model.LatLng
import com.amap.api.maps.model.LatLngBounds import com.amap.api.maps.model.LatLngBounds
import com.amap.api.maps.model.MarkerOptions import com.amap.api.maps.model.MarkerOptions
import com.amap.api.maps.model.PolylineOptions import com.amap.api.maps.model.PolylineOptions
import com.blankj.utilcode.util.ToastUtils
import com.za.base.BaseActivity import com.za.base.BaseActivity
import com.za.base.theme.headBgColor import com.za.base.theme.headBgColor
import com.za.base.view.CommonDialog import com.za.base.view.CommonDialog
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.util.ImageUtil import com.za.common.util.ImageUtil
import com.za.common.util.ServicingSpeechManager import com.za.common.util.ServicingSpeechManager
import com.za.ext.copy import com.za.ext.copy
import com.za.ext.finish import com.za.ext.finish
import com.za.ext.goNextPage import com.za.ext.goNextPage
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.camera.ZdCameraXActivity
import com.za.ui.servicing.view.InServicingHeadView import com.za.ui.servicing.view.InServicingHeadView
class WaitToStartActivity : BaseActivity() { class WaitToStartActivity : BaseActivity() {
@Composable @Composable
override fun ContentView() { override fun ContentView() {
WaitToStartScreen() WaitToStartScreen()
} }
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) { fun WaitToStartScreen(vm : WaitToStartVm = viewModel()) {
val uiState = vm.uiState.collectAsStateWithLifecycle() val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
val mapView = remember { MapView(context) } val mapView = remember { MapView(context) }
// 添加 BottomSheet 状态 val getResult =
val bottomSheetState = rememberStandardBottomSheetState( rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { it ->
initialValue = SheetValue.Expanded if (it.resultCode == Activity.RESULT_OK) {
) val value = it.data?.getStringExtra("path")
val scaffoldState = rememberBottomSheetScaffoldState( LogUtil.print("takePhoto", "path==$value")
bottomSheetState = bottomSheetState if (value.isNullOrBlank()) {
) ToastUtils.showLong("照片路径为空,请重新拍摄!")
return@rememberLauncherForActivityResult
}
vm.dispatch(WaitToStartVm.Action.CompareServicePeople(value))
}
}
DisposableEffect(key1 = lifecycleOwner) { // 添加 BottomSheet 状态
val observer = LifecycleEventObserver { _, event -> val bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.Expanded)
when (event) { val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
Lifecycle.Event.ON_CREATE -> {
mapView.onCreate(Bundle())
vm.dispatch(WaitToStartVm.Action.Init)
ServicingSpeechManager.playStartTip(context)
}
Lifecycle.Event.ON_RESUME -> mapView.onResume() DisposableEffect(key1 = lifecycleOwner) {
Lifecycle.Event.ON_PAUSE -> mapView.onPause() val observer = LifecycleEventObserver { _, event ->
Lifecycle.Event.ON_DESTROY -> mapView.onDestroy() when (event) {
else -> {} Lifecycle.Event.ON_CREATE -> {
} mapView.onCreate(Bundle())
} vm.dispatch(WaitToStartVm.Action.Init)
ServicingSpeechManager.playStartTip(context)
}
lifecycleOwner.lifecycle.addObserver(observer) Lifecycle.Event.ON_RESUME -> mapView.onResume()
onDispose { Lifecycle.Event.ON_PAUSE -> mapView.onPause()
lifecycleOwner.lifecycle.removeObserver(observer) Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
} else -> {}
} }
}
if (uiState.value.goNextPage != null) { lifecycleOwner.lifecycle.addObserver(observer)
goNextPage(uiState.value.goNextPage?.nextState, context) onDispose {
} lifecycleOwner.lifecycle.removeObserver(observer)
}
}
if (uiState.value.isGoNextPageDialog == true) { if (uiState.value.goNextPage != null) {
CommonDialog( goNextPage(uiState.value.goNextPage?.nextState, context)
cancelText = "取消", vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(goNextPage = null)))
confirmText = "前往下一步", }
title = "是否前往下一步?",
cancelEnable = true,
cancel = {
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
},
dismiss = { vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false))) },
confirm = {
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
vm.dispatch(WaitToStartVm.Action.UpdateTask)
}
)
}
BottomSheetScaffold( if (uiState.value.showServicePeopleConfirmDialog == true) {
scaffoldState = scaffoldState, CommonDialog(cancelText = "取消",
topBar = { confirmText = "去拍照",
InServicingHeadView( title = "服务人员确认",
title = "调度成功,等待发车", cancelEnable = true,
onBack = { context.finish() }, cancel = {
orderInfo = uiState.value.orderInfo vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
) showServicePeopleConfirmDialog = false)))
}, },
sheetContent = { dismiss = {
Column( vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
modifier = Modifier showServicePeopleConfirmDialog = false)))
.fillMaxWidth() },
.wrapContentHeight() confirm = {
.verticalScroll(rememberScrollState()) vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
) { showServicePeopleConfirmDialog = false)))
// 滑动指示器 val intent = Intent(context, ZdCameraXActivity::class.java)
Box( intent.putExtra("isBack", false)
modifier = Modifier getResult.launch(intent)
.fillMaxWidth() })
.padding(vertical = 8.dp), }
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.width(32.dp)
.height(4.dp)
.background(
color = Color(0xFFE0E0E0),
shape = RoundedCornerShape(2.dp)
)
)
}
// 距离和时间信息 BottomSheetScaffold(scaffoldState = scaffoldState,
Row( topBar = {
modifier = Modifier InServicingHeadView(title = "调度成功,等待发车",
.fillMaxWidth() onBack = { context.finish() },
.padding(horizontal = 16.dp, vertical = 12.dp), orderInfo = uiState.value.orderInfo)
horizontalArrangement = Arrangement.SpaceBetween, },
verticalAlignment = Alignment.CenterVertically sheetContent = {
) { Column(modifier = Modifier
Text( .fillMaxWidth()
text = if (uiState.value.estimatedArrivalTime.isNotEmpty()) .wrapContentHeight()
"预计到达: ${uiState.value.estimatedArrivalTime}" .verticalScroll(rememberScrollState())) { // 滑动指示器
else Box(modifier = Modifier
"计算中...", .fillMaxWidth()
color = Color(0xFF666666), .padding(vertical = 8.dp),
fontSize = 14.sp contentAlignment = Alignment.Center) {
) Box(modifier = Modifier
.width(32.dp)
.height(4.dp)
.background(color = Color(0xFFE0E0E0), shape = RoundedCornerShape(2.dp)))
}
Text( // 距离和时间信息
text = if (uiState.value.remainingDistance > 0) Row(modifier = Modifier
"距离救援地: %.1fkm".format(uiState.value.remainingDistance / 1000f) .fillMaxWidth()
else .padding(horizontal = 16.dp, vertical = 12.dp),
"计算中...", horizontalArrangement = Arrangement.SpaceBetween,
color = Color(0xFFFF4D4F), verticalAlignment = Alignment.CenterVertically) {
fontSize = 14.sp Text(text = if (uiState.value.estimatedArrivalTime.isNotEmpty()) "预计到达: ${uiState.value.estimatedArrivalTime}"
) else "计算中...", color = Color(0xFF666666), fontSize = 14.sp)
}
HorizontalDivider( Text(text = if (uiState.value.remainingDistance > 0) "距离救援地: %.1fkm".format(
modifier = Modifier uiState.value.remainingDistance / 1000f)
.fillMaxWidth() else "计算中...", color = Color(0xFFFF4D4F), fontSize = 14.sp)
.alpha(0.1f) }
)
// 订单信息 HorizontalDivider(modifier = Modifier
Column( .fillMaxWidth()
modifier = Modifier .alpha(0.1f))
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)
) {
// 订单类型
Text(
text = uiState.value.orderInfo?.serviceTypeName ?: "",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black
)
Spacer(modifier = Modifier.height(8.dp)) // 订单信息
Column(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)) { // 订单类型
Text(text = uiState.value.orderInfo?.serviceTypeName ?: "",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black)
// 订单标签 Spacer(modifier = Modifier.height(8.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Box(
modifier = Modifier
.background(Color(0xFF9BA1B2), RoundedCornerShape(4.dp))
.padding(horizontal = 6.dp, vertical = 2.dp)
) {
Text(
text = "月结".takeIf { uiState.value.orderInfo?.settleType == 1 }
?: "现金",
color = Color.White,
fontSize = 12.sp
)
}
Text( // 订单标签
text = uiState.value.orderInfo?.orderSource ?: "", Row(verticalAlignment = Alignment.CenterVertically,
color = Color.Black, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
fontSize = 12.sp Box(modifier = Modifier
) .background(Color(0xFF9BA1B2), RoundedCornerShape(4.dp))
.padding(horizontal = 6.dp, vertical = 2.dp)) {
Text(text = "月结".takeIf { uiState.value.orderInfo?.settleType == 1 }
?: "现金", color = Color.White, fontSize = 12.sp)
}
Text( Text(text = uiState.value.orderInfo?.orderSource ?: "",
text = uiState.value.orderInfo?.addressProperty ?: "", color = Color.Black,
color = Color(0xFFFD8205), fontSize = 12.sp)
fontSize = 12.sp,
fontWeight = FontWeight.Medium
)
}
Spacer(modifier = Modifier.height(12.dp)) Text(text = uiState.value.orderInfo?.addressProperty ?: "",
color = Color(0xFFFD8205),
fontSize = 12.sp,
fontWeight = FontWeight.Medium)
}
// 订单号 Spacer(modifier = Modifier.height(12.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { uiState.value.orderInfo?.taskCode?.copy(context) }
) {
Text(
text = "单号",
color = Color(0xFF999999),
fontSize = 13.sp
)
Spacer(modifier = Modifier.width(8.dp)) // 订单号
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable {
uiState.value.orderInfo?.taskCode?.copy(context)
}) {
Text(text = "单号", color = Color(0xFF999999), fontSize = 13.sp)
Text( Spacer(modifier = Modifier.width(8.dp))
text = uiState.value.orderInfo?.taskCode ?: "",
color = Color(0xFF666666),
fontSize = 14.sp
)
Spacer(modifier = Modifier.width(8.dp)) Text(text = uiState.value.orderInfo?.taskCode ?: "",
color = Color(0xFF666666),
fontSize = 14.sp)
AsyncImage( Spacer(modifier = Modifier.width(8.dp))
model = R.drawable.sv_copy,
contentDescription = "copy",
modifier = Modifier.size(16.dp)
)
}
Spacer(modifier = Modifier.height(16.dp)) AsyncImage(model = R.drawable.sv_copy,
contentDescription = "copy",
modifier = Modifier.size(16.dp))
}
// 地址信息 Spacer(modifier = Modifier.height(16.dp))
Column(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// 救援地
Row(
verticalAlignment = Alignment.Top,
modifier = Modifier.clickable {
// 点击救援地时移动地图到救援位置
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 &&
order.lng != null && order.lng != 0.0
) {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(order.lat!!, order.lng!!),
16f
)
)
}
}
}
) {
AsyncImage(
model = R.drawable.sv_rescuing,
contentDescription = "rescue",
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(8.dp)) // 地址信息
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { // 救援地
Row(verticalAlignment = Alignment.Top,
modifier = Modifier.clickable { // 点击救援地时移动地图到救援位置
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0) {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngZoom(
LatLng(order.lat !!, order.lng !!),
16f))
}
}
}) {
AsyncImage(model = R.drawable.sv_rescuing,
contentDescription = "rescue",
modifier = Modifier.size(16.dp))
Text( Spacer(modifier = Modifier.width(8.dp))
text = uiState.value.orderInfo?.address ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium
)
}
// 目的地 Text(text = uiState.value.orderInfo?.address ?: "",
if (!uiState.value.orderInfo?.distAddress.isNullOrBlank()) { color = Color.Black,
Row( fontSize = 15.sp,
verticalAlignment = Alignment.Top, fontWeight = FontWeight.Medium)
modifier = Modifier.clickable { }
// 点击目的地时移动地图到目的地位置
uiState.value.orderInfo?.let { order ->
if (order.distLat != null && order.distLat != 0.0 &&
order.distLng != null && order.distLng != 0.0
) {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(order.distLat!!, order.distLng!!),
16f
)
)
}
}
}
) {
AsyncImage(
model = R.drawable.sv_dist,
contentDescription = "destination",
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(8.dp)) // 目的地
if (! uiState.value.orderInfo?.distAddress.isNullOrBlank()) {
Row(verticalAlignment = Alignment.Top,
modifier = Modifier.clickable { // 点击目的地时移动地图到目的地位置
uiState.value.orderInfo?.let { order ->
if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0) {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngZoom(
LatLng(order.distLat !!, order.distLng !!),
16f))
}
}
}) {
AsyncImage(model = R.drawable.sv_dist,
contentDescription = "destination",
modifier = Modifier.size(16.dp))
Text( Spacer(modifier = Modifier.width(8.dp))
text = uiState.value.orderInfo?.distAddress ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium
)
}
}
}
Spacer(modifier = Modifier.height(16.dp)) Text(text = uiState.value.orderInfo?.distAddress ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
}
}
}
// 发车按钮 Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = { vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = true))) },
modifier = Modifier
.fillMaxWidth()
.height(44.dp)
.padding(horizontal = 16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = headBgColor
),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = "发车",
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
}
Spacer(modifier = Modifier.height(16.dp)) // 发车按钮
} Button(onClick = {
} vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
}, showServicePeopleConfirmDialog = true)))
sheetPeekHeight = 180.dp, },
sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp), modifier = Modifier
sheetContainerColor = Color.White, .fillMaxWidth()
sheetDragHandle = null, .height(44.dp)
sheetSwipeEnabled = true .padding(horizontal = 16.dp),
) { paddingValues -> colors = ButtonDefaults.buttonColors(containerColor = headBgColor),
Box( shape = RoundedCornerShape(8.dp)) {
modifier = Modifier Text(text = "发车",
.fillMaxSize() color = Color.White,
.padding(paddingValues) fontSize = 16.sp,
) { fontWeight = FontWeight.Medium)
AndroidView( }
modifier = Modifier.fillMaxSize(),
factory = {
AMapLocationClient.updatePrivacyShow(context, true, true)
AMapLocationClient.updatePrivacyAgree(context, true)
mapView.apply {
map.apply {
isTrafficEnabled = false
isMyLocationEnabled = false
uiSettings.isMyLocationButtonEnabled = false
uiSettings.setLogoBottomMargin(-100)
uiSettings.isZoomControlsEnabled = false
// 修改标记点点击事件 Spacer(modifier = Modifier.height(16.dp))
setOnMarkerClickListener { marker -> }
marker.showInfoWindow() }
// 800ms后隐藏信息窗口 },
Handler(Looper.getMainLooper()).postDelayed({ sheetPeekHeight = 180.dp,
marker.hideInfoWindow() sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp),
}, 800) sheetContainerColor = Color.White,
true sheetDragHandle = null,
} sheetSwipeEnabled = true) { paddingValues ->
} Box(modifier = Modifier
} .fillMaxSize()
}, .padding(paddingValues)) {
update = { AndroidView(modifier = Modifier.fillMaxSize(), factory = {
// 清除旧标记和路线 AMapLocationClient.updatePrivacyShow(context, true, true)
mapView.map.clear() AMapLocationClient.updatePrivacyAgree(context, true)
mapView.apply {
map.apply {
isTrafficEnabled = false
isMyLocationEnabled = false
uiSettings.isMyLocationButtonEnabled = false
uiSettings.setLogoBottomMargin(- 100)
uiSettings.isZoomControlsEnabled = false
// 先绘制路线 // 修改标记点点击事件
uiState.value.routePoints?.let { points -> setOnMarkerClickListener { marker ->
mapView.map.addPolyline( marker.showInfoWindow() // 800ms后隐藏信息窗口
PolylineOptions() Handler(Looper.getMainLooper()).postDelayed({
.addAll(points) marker.hideInfoWindow()
.width(15f) }, 800)
.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.icon_road_green_arrow)) true
.zIndex(1f) }
) }
} }
}, update = { // 清除旧标记和路线
mapView.map.clear()
// 再添加标记点,确保标记点在路线上层 // 先绘制路线
// 添加当前位置标记 uiState.value.routePoints?.let { points ->
if (GlobalData.currentLocation != null) { mapView.map.addPolyline(PolylineOptions().addAll(points).width(15f)
mapView.map.addMarker( .setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.icon_road_green_arrow))
MarkerOptions() .zIndex(1f))
.position(LatLng( }
GlobalData.currentLocation?.latitude!!,
GlobalData.currentLocation?.longitude!!
))
.title("当前位置")
.icon(ImageUtil.vectorToBitmap(context,R.drawable.ic_current_location))
.anchor(0.5f, 0.5f)
.visible(true)
)
}
// 添加救援地标记 // 添加标记点,确保标记点在路线上层
uiState.value.orderInfo?.let { order -> // 添加当前位置标记
if (order.lat != null && order.lat != 0.0 && if (GlobalData.currentLocation != null) {
order.lng != null && order.lng != 0.0 && mapView.map.addMarker(MarkerOptions().position(LatLng(GlobalData.currentLocation?.latitude !!,
GlobalData.currentLocation != null GlobalData.currentLocation?.longitude !!)).title("当前位置")
) { .icon(ImageUtil.vectorToBitmap(context, R.drawable.ic_current_location))
mapView.map.addMarker( .anchor(0.5f, 0.5f).visible(true))
MarkerOptions() }
.position(LatLng(order.lat!!, order.lng!!))
.title("救援地点")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.anchor(0.5f, 0.5f)
)
}
// 添加目的地标记 // 添加救援地标记
if (order.distLat != null && order.distLat != 0.0 && uiState.value.orderInfo?.let { order ->
order.distLng != null && order.distLng != 0.0 && if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0 && GlobalData.currentLocation != null) {
GlobalData.currentLocation != null mapView.map.addMarker(MarkerOptions().position(LatLng(order.lat !!,
) { order.lng !!)).title("救援地点")
mapView.map.addMarker( .icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
MarkerOptions() .anchor(0.5f, 0.5f))
.position(LatLng(order.distLat!!, order.distLng!!)) }
.title("目的地")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_dist_map))
.anchor(0.5f, 0.5f)
)
}
}
// 最后调整地图显示范围 // 添加目的地标记
// 计算地图显示范围 if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0 && GlobalData.currentLocation != null) {
val bounds = LatLngBounds.Builder().apply { mapView.map.addMarker(MarkerOptions().position(LatLng(order.distLat !!,
// 添加当前位置 order.distLng !!)).title("目的地")
GlobalData.currentLocation?.let { .icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_dist_map))
include(LatLng(it.latitude, it.longitude)) .anchor(0.5f, 0.5f))
} }
}
// 添加救援地点 // 最后调整地图显示范围
uiState.value.orderInfo?.let { order -> // 计算地图显示范围
if (order.lat != null && order.lat != 0.0 && val bounds = LatLngBounds.Builder().apply { // 添加当前位置
order.lng != null && order.lng != 0.0 GlobalData.currentLocation?.let {
) { include(LatLng(it.latitude, it.longitude))
include(LatLng(order.lat!!, order.lng!!)) }
}
// 添加目的地 // 添加救援地点
if (order.distLat != null && order.distLat != 0.0 && uiState.value.orderInfo?.let { order ->
order.distLng != null && order.distLng != 0.0 if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0) {
) { include(LatLng(order.lat !!, order.lng !!))
include(LatLng(order.distLat!!, order.distLng!!)) }
}
}
}.build()
// 调整地图显示范围,确保所有点都可见 // 添加目的地
try { if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0) {
mapView.map.animateCamera( include(LatLng(order.distLat !!, order.distLng !!))
CameraUpdateFactory.newLatLngBounds(bounds, 100) }
) }
} catch (e: Exception) { }.build()
// 如果计算边界失败,则使用默认缩放级别
GlobalData.currentLocation?.let { // 调整地图显示范围,确保所有点都可见
mapView.map.animateCamera( try {
CameraUpdateFactory.newLatLngZoom( mapView.map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100))
LatLng(it.latitude, it.longitude), } catch (e : Exception) { // 如果计算边界失败,则使用默认缩放级别
15f GlobalData.currentLocation?.let {
) mapView.map.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(it.latitude,
) it.longitude), 15f))
} }
} }
} })
) }
} }
}
} }

View File

@ -15,20 +15,27 @@ import com.blankj.utilcode.util.ToastUtils
import com.za.base.IServicingVm import com.za.base.IServicingVm
import com.za.base.view.LoadingManager import com.za.base.view.LoadingManager
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.bean.request.DriverFaceCompareRequest
import com.za.bean.request.IaiCompareFaceBean
import com.za.bean.request.UpdateTaskBean import com.za.bean.request.UpdateTaskBean
import com.za.bean.request.UpdateTaskRequest import com.za.bean.request.UpdateTaskRequest
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.ext.toJson import com.za.ext.toJson
import com.za.net.BaseObserver
import com.za.net.CommonMethod import com.za.net.CommonMethod
import com.za.net.RetrofitHelper
import com.za.service.location.ZdLocationManager import com.za.service.location.ZdLocationManager
import com.za.servicing.R import com.za.servicing.R
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale
@ -47,6 +54,7 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
is Action.UpdateState -> updateState(action.uiState) is Action.UpdateState -> updateState(action.uiState)
is Action.StartTimer -> startTimer() is Action.StartTimer -> startTimer()
is Action.UpdateTimer -> updateTimer() is Action.UpdateTimer -> updateTimer()
is Action.CompareServicePeople -> compareServicePeople(action.localPath)
} }
} }
@ -216,6 +224,52 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
private fun updateTimer() { // 在这里处理倒计时更新逻辑 private fun updateTimer() { // 在这里处理倒计时更新逻辑
} }
private fun compareServicePeople(localPath : String) {
LoadingManager.showLoading()
CommonMethod.uploadImage(file = File(localPath), success = { it ->
compilePeople(it, success = {
LoadingManager.hideLoading()
updateTask()
}, failed = {
LoadingManager.hideLoading()
ToastUtils.showLong(it)
})
}, failed = {
LoadingManager.hideLoading()
ToastUtils.showLong(it)
})
}
private fun compilePeople(url : String?,
success : (IaiCompareFaceBean?) -> Unit,
failed : (String) -> Unit) {
if (url.isNullOrBlank()) {
ToastUtils.showLong("图片路径为空!")
return
}
val driverFaceCompareRequest =
DriverFaceCompareRequest(vehicleId = GlobalData.driverInfoBean?.vehicleId,
driverId = GlobalData.driverInfoBean?.userId,
photoUrl = url)
RetrofitHelper.getDefaultService().iaiCompareFace(driverFaceCompareRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<IaiCompareFaceBean>() {
override fun doSuccess(it : IaiCompareFaceBean?) {
if (it?.compareResult == true) {
success(it)
return
}
failed("人脸对比失败")
LogUtil.print("face", "人脸对比成功:$it")
}
override fun doFailure(code : Int, msg : String?) {
failed(msg ?: "人脸对比失败")
LogUtil.print("face", "人脸对比失败:$msg")
}
})
}
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
timerJob?.cancel() timerJob?.cancel()
@ -227,14 +281,15 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
data class UpdateState(val uiState : UiState) : Action() data class UpdateState(val uiState : UiState) : Action()
data object StartTimer : Action() data object StartTimer : Action()
data object UpdateTimer : Action() data object UpdateTimer : Action()
data class CompareServicePeople(val localPath : String) : Action()
} }
data class UiState(val orderInfo : OrderInfo? = null, data class UiState(val orderInfo : OrderInfo? = null,
val showCallPhoneDialog : Boolean? = false, val showCallPhoneDialog : Boolean? = false,
val markers : ArrayList<MarkerOptions>? = null, val markers : ArrayList<MarkerOptions>? = null,
val goNextPage : UpdateTaskBean? = null, val goNextPage : UpdateTaskBean? = null,
val isGoNextPageDialog : Boolean? = null,
val routePoints : List<LatLng>? = null, val routePoints : List<LatLng>? = null,
val remainingDistance : Float = 0f, val remainingDistance : Float = 0f,
val showServicePeopleConfirmDialog : Boolean? = false,
val estimatedArrivalTime : String = "") val estimatedArrivalTime : String = "")
} }

View File

@ -1,14 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="11dp" android:width="128dp"
android:height="11dp" android:height="128dp"
android:viewportWidth="11" android:viewportWidth="1024"
android:viewportHeight="11"> android:viewportHeight="1024">
<path <path
android:pathData="M3.25,8.804L3.26,8.793L7.74,8.793C8.301,8.793 8.76,8.334 8.76,7.773L8.76,4.257L8.757,1.244L8.76,1.245L8.76,1.053C8.76,0.492 8.301,0.033 7.74,0.033L1.02,0.033C0.459,0.033 0,0.492 0,1.053L0,7.773C0,8.334 0.459,8.793 1.02,8.793L2.14,8.793L2.15,8.804L3.25,8.804ZM7.18,7.872L1.58,7.872C1.189,7.872 0.921,7.604 0.921,7.213L0.921,1.613C0.921,1.222 1.189,0.953 1.58,0.953L7.18,0.953C7.571,0.953 7.84,1.222 7.84,1.613L7.84,7.213C7.84,7.604 7.571,7.872 7.18,7.872ZM9.98,11.033C10.541,11.033 11,10.574 11,10.013L11,3.293C11,2.732 10.541,2.273 9.98,2.273L9.556,2.273L9.557,3.205C9.872,3.261 10.079,3.51 10.079,3.853L10.079,9.453C10.079,9.844 9.811,10.112 9.42,10.112L3.82,10.112C3.482,10.112 3.235,9.911 3.175,9.604L2.239,9.604L2.24,10.013C2.24,10.574 2.699,11.033 3.26,11.033L9.98,11.033Z" android:pathData="M675.9,265.9H388.9c-68,0 -123.1,55.1 -123.1,123.1v287c-53.3,-2.6 -82,-33.8 -82,-89.3V273.3c0,-58 31.5,-89.5 89.5,-89.5h313.3c55.5,0 86.7,28.8 89.3,82z"
android:strokeAlpha="0.8312872" android:strokeAlpha="0.3"
android:strokeWidth="1" android:fillColor="#1A73E8"
android:fillColor="#9197AB" android:fillAlpha="0.3"/>
android:fillType="nonZero" <path
android:strokeColor="#00000000" android:pathData="M437.4,347.9h313.3c58,0 89.5,31.5 89.5,89.5v313.3c0,58 -31.5,89.5 -89.5,89.5H437.4c-58,0 -89.5,-31.5 -89.5,-89.5V437.4c0,-58 31.5,-89.5 89.5,-89.5z"
android:fillAlpha="0.8312872"/> android:fillColor="#1A73E8"/>
</vector> </vector>

View File

@ -18,4 +18,5 @@
9.拖车服务时,救援人员仅按照道路救援中心的指令,将车辆拖往指定目的地,如果顾客需要变更拖车目的地,请与道路救援中心联系。\n 9.拖车服务时,救援人员仅按照道路救援中心的指令,将车辆拖往指定目的地,如果顾客需要变更拖车目的地,请与道路救援中心联系。\n
10.顾客如果要求自费服务项目,请与第三方公司或人员协商相关费用并现场向其支付。因为此类服务发生任何争议,请与数援人员双方自行解决,救援中心不再介入和负责。\n 10.顾客如果要求自费服务项目,请与第三方公司或人员协商相关费用并现场向其支付。因为此类服务发生任何争议,请与数援人员双方自行解决,救援中心不再介入和负责。\n
11.服务完成后客户接收车辆时,应再次签字确认服务完成及车辆无损,中道救援不接受客户己在服务完成记录中签字后的任何索赔和投诉:若接车方为维修站,且维修站接车员签字确认车辆无损,则事后发现的任何车辆损伤及相关责任应由维修站承担。如客户认为被救车辆损伤由中道救援服务商造成,应出具权威部门出具的检测报告,中道救援将根据检测报告确定服务商是否进行赔偿。</string> 11.服务完成后客户接收车辆时,应再次签字确认服务完成及车辆无损,中道救援不接受客户己在服务完成记录中签字后的任何索赔和投诉:若接车方为维修站,且维修站接车员签字确认车辆无损,则事后发现的任何车辆损伤及相关责任应由维修站承担。如客户认为被救车辆损伤由中道救援服务商造成,应出具权威部门出具的检测报告,中道救援将根据检测报告确定服务商是否进行赔偿。</string>
<string name="title_activity_service_people_confirm">ServicePeopleConfirmActivity</string>
</resources> </resources>

View File

@ -2,4 +2,6 @@
<resources> <resources>
<style name="Theme.Dealer" parent="Theme.AppCompat.Light.NoActionBar" /> <style name="Theme.Dealer" parent="Theme.AppCompat.Light.NoActionBar" />
<style name="Theme.Zd_sdk_demo" parent="android:Theme.Material.Light.NoActionBar" />
</resources> </resources>

BIN
zd_sdk_demo.jks Normal file

Binary file not shown.