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 {
release {
minifyEnabled false
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 {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11

View File

@ -27,7 +27,7 @@ class MainActivity : ComponentActivity() {
.fillMaxSize()
.clickable {
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)
startActivity(intent)
}

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ data class JpushBean(
val carModel: String? = null,//车辆型号 "carModel":"秦"
val contract: String? = null, //车辆型号 "carModel":"秦"
val typeDesc: String? = null, //推送的附加消息 revoke 撤回 giveUp放弃 reDispatch改派
val userOrderId: Int? = null,
val carNo: String? = null, //客户车车牌号 "carNo":"粤AF53918"
val taskState: String? = null, //订单状态 "taskState":"GOTO"
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
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 //里程表照片路径
)
data class FastLoginRequest(var deviceId: String? = null)
data class PhoneBean(
var versionRelease: 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.tencent.mmkv.MMKV
import com.za.bean.db.order.OrderInfo
import com.za.common.log.LogUtil
import com.za.room.RoomHelper
import com.za.room.db.user.DriverInfoBean
object GlobalData {
lateinit var application : Application
private val mmkv : MMKV by lazy { MMKV.defaultMMKV() }
var activityCount : Int = 0
var isMaster = AppUtils.getAppPackageName() == "com.za.rescue.dealer"
var token : String? = null
get() {
return MMKV.defaultMMKV().decodeString("ZD_TOKEN", null)
return mmkv.decodeString("ZD_TOKEN", null)
}
set(value) {
MMKV.defaultMMKV().encode("ZD_TOKEN", value)
mmkv.encode("ZD_TOKEN", value)
field = value
}
var regid : String? = null
get() {
return MMKV.defaultMMKV().decodeString("regid", null)
return mmkv.decodeString("regid", null)
}
set(value) {
MMKV.defaultMMKV().encode("regid", value)
mmkv.encode("regid", value)
field = value
}
var aesKey : String? = null
get() {
return MMKV.defaultMMKV().decodeString("AES_KEY", null)
return mmkv.decodeString("AES_KEY", null)
}
set(value) {
MMKV.defaultMMKV().encode("AES_KEY", value)
mmkv.encode("AES_KEY", value)
field = value
}
//新订单是否已经被处理
var isHandlerNewOrder : Boolean? = false
var driverInfoBean : DriverInfoBean? = null
private val lock = Any()
var driverInfoBean : DriverInfoBean?
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
return driverInfoBean
}
set(value) {
RoomHelper.db?.driverInfoDao()?.updateDriverInfo(value)
mmkv.encode("lastLoginBean", value)
}
var currentOrder : OrderInfo? = null
get() {
return MMKV.defaultMMKV().decodeParcelable("currentOrder", OrderInfo::class.java)
return mmkv.decodeParcelable("currentOrder", OrderInfo::class.java)
}
set(value) {
MMKV.defaultMMKV().encode("currentOrder", value)
mmkv.encode("currentOrder", value)
if (RoomHelper.db?.orderDao()?.getCurrentOrder() == null && value != null) {
RoomHelper.db?.orderDao()?.insertOrder(value)
} else if (value != null) {
@ -78,10 +100,10 @@ object GlobalData {
var loginTime : Long? = null
get() {
return MMKV.defaultMMKV().decodeLong("loginTime", System.currentTimeMillis())
return mmkv.decodeLong("loginTime", System.currentTimeMillis())
}
set(value) {
MMKV.defaultMMKV().encode("loginTime", value ?: System.currentTimeMillis())
mmkv.encode("loginTime", value ?: System.currentTimeMillis())
field = value
}
@ -89,6 +111,7 @@ object GlobalData {
token = null
aesKey = null
currentLocation = null
driverInfoBean = 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.DriverFaceCompareRequest
import com.za.bean.request.ElectronOrderResponse
import com.za.bean.request.FastLoginRequest
import com.za.bean.request.FetchVehicleMaintenanceSubmitHistoryRequestBean
import com.za.bean.request.GeneralInfoRequest
import com.za.bean.request.GiveUpTaskRequest
import com.za.bean.request.HistoryDetailRequest
import com.za.bean.request.HistoryPhotoTemplateRequest
import com.za.bean.request.HistoryTasksRequest
import com.za.bean.request.IaiCompareFaceBean
import com.za.bean.request.LoginRequest
import com.za.bean.request.OrderListRequest
import com.za.bean.request.OrderPhotoOcrRecognizeRequest
@ -116,6 +118,9 @@ interface ApiService {
@POST("/driverApp/task/login")
fun login(@Body info : LoginRequest) : Observable<BaseResponse<DriverInfo?>>
@POST("/driverApp/supplier/fastLogin")
fun fastLogin(@Body info : FastLoginRequest) : Observable<BaseResponse<DriverInfo?>>
//获取该手机号下面服务商列表
@POST("/driverApp/supplier/getDriverListInfo")
fun getDriverListInfo(@Body info : VerifyCodeRequest) : Observable<BaseResponse<List<AppNewDriverInfoDTO>>>
@ -271,4 +276,7 @@ interface ApiService {
@POST("driverApp/base/getVoiceUrl")
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 com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.NetworkUtils
import com.blankj.utilcode.util.ToastUtils
import com.google.gson.JsonParseException
import com.za.base.Const
@ -105,21 +104,6 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
GlobalData.clearUserCache()
ZdLocationManager.stopContinuousLocation()
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) {
LogUtil.print("handlerTokenExpired", e)
}

View File

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

View File

@ -11,7 +11,7 @@ import com.za.room.db.GlobalRoom
@SuppressLint("StaticFieldLeak")
object RoomHelper {
const val VERSION: Int = 37
const val VERSION: Int = 38
private lateinit var mContext: Context
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.OrderDao
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.LocalResourceDao
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,
exportSchema = false)
abstract class GlobalRoom : RoomDatabase() {
@ -43,7 +41,5 @@ abstract class GlobalRoom : RoomDatabase() {
abstract fun offlineTaskDao() : OfflineDao
abstract fun driverInfoDao() : DriverInfoDao
abstract fun localResourceDao() : LocalResourceDao
}

View File

@ -1,11 +1,10 @@
package com.za.room.db.user
import androidx.room.Entity
import androidx.room.PrimaryKey
import android.os.Parcel
import android.os.Parcelable
@Entity(tableName = "driver_info")
data class DriverInfoBean(
@PrimaryKey(autoGenerate = false) val userId : Int? = null, //用户id
val userId : Int? = null, //用户id
val userName : String? = null, //用户姓名
val userPhone : String? = null, //用户手机号
val userPortrait : String? = null, //用户头像
@ -23,4 +22,59 @@ data class DriverInfoBean(
val vehicleState : Int? = null, //车辆状态 0 空闲 1 忙碌
val plateNumber : 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

@ -103,6 +103,7 @@ class ZdCameraXActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_zd_camera_xactivity)
isBack = intent.getBooleanExtra("isBack", true)
initView()
initLocation()
startCamera()
@ -118,12 +119,15 @@ class ZdCameraXActivity : AppCompatActivity() {
in 45 .. 134 -> {
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
}
@ -149,7 +153,8 @@ class ZdCameraXActivity : AppCompatActivity() {
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?.playTogether(ObjectAnimator.ofFloat(ivCancel, "translationX", 0f, - 200f),
ObjectAnimator.ofFloat(ivConfirm, "translationX", 200f))
animatorSet?.setDuration(500)
}
@ -204,7 +209,8 @@ class ZdCameraXActivity : AppCompatActivity() {
if (exposureState != null) {
camera?.cameraControl?.setExposureCompensationIndex(Math.round(progress.toFloat()))
if (exposurePopupWindow != null) {
val textView = exposurePopupWindow?.contentView?.findViewWithTag<TextView>("tv_exposureView")
val textView =
exposurePopupWindow?.contentView?.findViewWithTag<TextView>("tv_exposureView")
textView?.text = progress.toString() + ""
}
}
@ -234,7 +240,8 @@ class ZdCameraXActivity : AppCompatActivity() {
}
override fun click(event : MotionEvent) {
val action = viewFinder?.meteringPointFactory?.createPoint(event.x, event.y)?.let { FocusMeteringAction.Builder(it).build() }
val action = viewFinder?.meteringPointFactory?.createPoint(event.x, event.y)
?.let { FocusMeteringAction.Builder(it).build() }
rootView?.post { showTapView(event.rawX.toInt(), event.rawY.toInt()) }
action?.let {
camera?.cameraControl?.startFocusAndMetering(action)
@ -261,9 +268,11 @@ class ZdCameraXActivity : AppCompatActivity() {
private fun takePhoto() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ThreadUtils.runOnUiThread {
PermissionX.init(this@ZdCameraXActivity).permissions(Manifest.permission.ACCESS_MEDIA_LOCATION)
PermissionX.init(this@ZdCameraXActivity)
.permissions(Manifest.permission.ACCESS_MEDIA_LOCATION)
.request { allGranted : Boolean, grantedList : List<String?>?, deniedList : List<String?>? ->
print("ZDCamerax ACCESS_MEDIA_LOCATION 权限请求结果", "allGranted==$allGranted")
print("ZDCamerax ACCESS_MEDIA_LOCATION 权限请求结果",
"allGranted==$allGranted")
if (! allGranted) {
ToastUtils.showLong("权限获取失败")
finish()
@ -274,8 +283,7 @@ class ZdCameraXActivity : AppCompatActivity() {
if (imageCapture == null) {
ToastUtils.showShort("相机打开失败")
finish()
}
// val filename = TimeUtils.date2String(Date(), "yyyyMMddHHmmss")
} // 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")
@ -291,10 +299,9 @@ class ZdCameraXActivity : AppCompatActivity() {
// 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 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")
@ -304,25 +311,28 @@ class ZdCameraXActivity : AppCompatActivity() {
}
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions
.Builder(contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
.build()
val outputOptions = ImageCapture.OutputFileOptions.Builder(contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues).build()
takePhoto?.startAnimation()
imageCapture?.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
imageCapture?.takePicture(outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults : ImageCapture.OutputFileResults) {
this@ZdCameraXActivity.uri = outputFileResults.savedUri
//保存照片信息
this@ZdCameraXActivity.uri = outputFileResults.savedUri //保存照片信息
val exifInterface : ExifInterface
try {
exifInterface = ExifInterface(UriUtils.uri2File(outputFileResults.savedUri).absolutePath)
exifInterface =
ExifInterface(UriUtils.uri2File(outputFileResults.savedUri).absolutePath)
exifInterface.setAttribute(ExifInterface.TAG_DATETIME, filename)
if (location != null) {
exifInterface.setGpsInfo(location)
if (location?.latitude !! > 0f && location?.longitude != null && location?.longitude !! > 0f) {
exifInterface.setLatLong(location?.latitude!!, location?.longitude!!)
exifInterface.setLatLong(location?.latitude !!,
location?.longitude !!)
}
}
@ -330,7 +340,8 @@ class ZdCameraXActivity : AppCompatActivity() {
takePhoto?.cancelAnimation()
groupPreview?.visibility = View.VISIBLE
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()
@ -350,15 +361,18 @@ class ZdCameraXActivity : AppCompatActivity() {
}
private fun startCamera() {
imageCapture = ImageCapture.Builder().setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY).build()
imageCapture =
ImageCapture.Builder().setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
.build()
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
try {
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3).build()
preview.setSurfaceProvider(viewFinder?.surfaceProvider)
val cameraSelector = if (isBack) CameraSelector.DEFAULT_BACK_CAMERA else CameraSelector.DEFAULT_FRONT_CAMERA
preview.surfaceProvider = viewFinder?.surfaceProvider
val cameraSelector =
if (isBack) CameraSelector.DEFAULT_BACK_CAMERA else CameraSelector.DEFAULT_FRONT_CAMERA
preview.targetRotation = Surface.ROTATION_0
cameraProvider.unbindAll()
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
@ -386,8 +400,21 @@ class ZdCameraXActivity : AppCompatActivity() {
private fun initLocation() {
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) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA), 102)
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
) {
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)
@ -401,7 +428,8 @@ class ZdCameraXActivity : AppCompatActivity() {
return
}
try {
val popupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val popupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
popupWindow.width = 200
popupWindow.height = 200
val imageView = ImageView(this)
@ -421,7 +449,8 @@ class ZdCameraXActivity : AppCompatActivity() {
if (takePhoto?.visibility == View.GONE) {
return
}
exposurePopupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
exposurePopupWindow =
PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
exposurePopupWindow?.width = 300
exposurePopupWindow?.height = 200
val textView = TextView(this)
@ -439,8 +468,10 @@ class ZdCameraXActivity : AppCompatActivity() {
if (flashPopWindow != null) {
flashPopWindow = null
}
flashPopWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
@SuppressLint("InflateParams") val view = LayoutInflater.from(this).inflate(R.layout.item_flash_mode, null)
flashPopWindow =
PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
@SuppressLint("InflateParams") val view =
LayoutInflater.from(this).inflate(R.layout.item_flash_mode, null)
val tvFlashAuto = view.findViewById<TextView>(R.id.tvFlashAuto)
val tvFlashOpen = view.findViewById<TextView>(R.id.tvFlashOpen)
val tvFlashLight = view.findViewById<TextView>(R.id.tvFlashLight)
@ -491,8 +522,10 @@ class ZdCameraXActivity : AppCompatActivity() {
}
flashPopWindow?.contentView = view
flashPopWindow?.showAtLocation(ivFlash, Gravity.LEFT or Gravity.TOP, 0, ivFlash?.y?.toInt()?.minus(view.height)
?: 0)
flashPopWindow?.showAtLocation(ivFlash,
Gravity.LEFT or Gravity.TOP,
0,
ivFlash?.y?.toInt()?.minus(view.height) ?: 0)
}
override fun onPause() {

View File

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

View File

@ -2,22 +2,65 @@ package com.za.ui.main
import android.content.Context
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.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.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.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.viewmodel.compose.viewModel
import com.blankj.utilcode.util.ActivityUtils
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.util.DeviceUtil
import com.za.ext.convertToFlowName
import com.za.ext.copy
import com.za.ext.finish
import com.za.ext.goStatusPage
import com.za.service.ServiceManager
import com.za.service.location.ZdLocationManager
import com.za.servicing.R
import kotlinx.coroutines.launch
class ServicingMainActivity : BaseActivity() {
@ -59,8 +102,8 @@ private fun ServicingMainScreen(jobCode : String? = null,
rescueVehicle : String? = null,
vm : ServicingMainVm = viewModel()) {
val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
val scope = rememberCoroutineScope()
val context = LocalContext.current
LaunchedEffect(Unit) {
vm.dispatch(ServicingMainVm.Action.Init(jobCode,
@ -75,12 +118,200 @@ private fun ServicingMainScreen(jobCode : String? = null,
}
}
if (uiState.value.state == 2) {
Box {
Text(text = "加载失败")
}
} else {
Scaffold(topBar = { HeadView("订单信息确认", onBack = { context.finish() }) }, bottomBar = {
if (uiState.value.state == 1) {
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)
}
}
}
}
}
@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()
GlobalData.currentOrder = orderInfo
updateState(uiState.value.copy(state = 1))
if (orderInfos.isNullOrEmpty()) {
ToastUtils.showShort("未查询到订单")
}
},
failed = {
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.maps.CameraUpdateFactory
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.LatLngBounds
import com.amap.api.maps.model.MarkerOptions

View File

@ -51,7 +51,9 @@ class OrderGiveUpActivity : BaseActivity() {
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 {
@ -60,11 +62,13 @@ class OrderGiveUpActivity : BaseActivity() {
fun goOrderGiveUpActivity(context : Context,
orderInfo : OrderInfo? = null,
taskId : Int? = null,
userOrderId : Int? = null,
giveUpType : Int) {
val intent = Intent(context, OrderGiveUpActivity::class.java)
intent.putExtra("giveUpType", giveUpType)
intent.putExtra("orderInfo", orderInfo)
intent.putExtra("taskId", taskId)
intent.putExtra("userOrderId", userOrderId)
context.startActivity(intent)
}
}
@ -72,12 +76,17 @@ class OrderGiveUpActivity : BaseActivity() {
@Composable
fun OrderGiveUpScreen(vm: OrderGiveUpVm = viewModel(), orderInfo: OrderInfo?, taskId: Int, giveUpType: Int) {
fun OrderGiveUpScreen(vm : OrderGiveUpVm = viewModel(),
orderInfo : OrderInfo?,
taskId : Int,
giveUpType : Int) {
val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
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,
taskId = taskId,
giveUpType = giveUpType)))
vm.dispatch(OrderGiveUpVm.Action.Init)
}
@ -86,11 +95,17 @@ fun OrderGiveUpScreen(vm: OrderGiveUpVm = viewModel(), orderInfo: OrderInfo?, ta
}
if (uiState.value.isGoNextPageDialog == true) {
CommonDialog(cancelText = "取消", confirmText = "是否确认放弃订单", title = "放弃订单", cancelEnable = true, cancel = {
CommonDialog(cancelText = "取消",
confirmText = "是否确认放弃订单",
title = "放弃订单",
cancelEnable = true,
cancel = {
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
}, dismiss = {
},
dismiss = {
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
}, confirm = {
},
confirm = {
vm.dispatch(OrderGiveUpVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
vm.dispatch(OrderGiveUpVm.Action.UpdateTask)
})
@ -105,10 +120,11 @@ fun OrderGiveUpScreen(vm: OrderGiveUpVm = viewModel(), orderInfo: OrderInfo?, ta
}) { it ->
LazyColumn(modifier = Modifier
.fillMaxSize()
.padding(it), contentPadding = PaddingValues(10.dp)) {
.padding(it),
contentPadding = PaddingValues(10.dp)) {
itemsIndexed(items = uiState.value.photoTemplateList
?: arrayListOf(), key = { _, item -> item.hashCode() }) { index: Int, item: PhotoTemplateInfo ->
itemsIndexed(items = uiState.value.photoTemplateList ?: arrayListOf(),
key = { _, item -> item.hashCode() }) { index : Int, item : PhotoTemplateInfo ->
InServicingPhotoView(photoTemplateInfo = item, index = index + 1, success = {
vm.dispatch(OrderGiveUpVm.Action.UpdatePhotoTemplate(it))
})
@ -124,10 +140,16 @@ fun OrderGiveUpScreen(vm: OrderGiveUpVm = viewModel(), orderInfo: OrderInfo?, ta
.background(color = Color.White, shape = RoundedCornerShape(6.dp))
.padding(10.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(text = "司机签名", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "司机签名",
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))
AsyncImage(model = R.drawable.sv_sign_new,
contentDescription = "",
modifier = Modifier.size(20.dp))
}
Spacer(modifier = Modifier.height(10.dp))

View File

@ -46,19 +46,22 @@ import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: OrderInfo?, isCanBack: Boolean = true) {
fun InServicingHeadView(title : String,
onBack : () -> Unit = {},
orderInfo : OrderInfo?,
isCanBack : Boolean = true) {
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) {
StartNavigationView(orderInfo = orderInfo, dismiss = { showNavigationDialog.value = false })
}
if (showCallPhoneDialog.value) {
CommonDialog(
cancelText = "取消",
CommonDialog(cancelText = "取消",
confirmText = "确定",
title = "是否联系客户?",
cancelEnable = true,
@ -73,6 +76,22 @@ fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: Order
})
}
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) {
ModalBottomSheet(containerColor = Color.White,
@ -86,9 +105,14 @@ fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: Order
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ON_SITE_PHOTO)
OrderRequirementsActivity.goOrderRequirementsActivity(context,
orderInfo,
type = Const.InServiceSettingType.ON_SITE_PHOTO)
}, contentAlignment = Alignment.Center) {
Text(text = "现场照片", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black)
Text(text = "现场照片",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
}
@ -99,7 +123,10 @@ fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: Order
showNavigationDialog.value = true
showBottomSheetDialog.value = false
}, contentAlignment = Alignment.Center) {
Text(text = "开启外部导航", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black)
Text(text = "开启外部导航",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
@ -107,9 +134,14 @@ fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: Order
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ORDER_REQUIREMENTS)
OrderRequirementsActivity.goOrderRequirementsActivity(context,
orderInfo,
type = Const.InServiceSettingType.ORDER_REQUIREMENTS)
}, contentAlignment = Alignment.Center) {
Text(text = "案件要求", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black)
Text(text = "案件要求",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
@ -117,9 +149,14 @@ fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: Order
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ORDER_DETAIL)
OrderRequirementsActivity.goOrderRequirementsActivity(context,
orderInfo,
type = Const.InServiceSettingType.ORDER_DETAIL)
}, contentAlignment = Alignment.Center) {
Text(text = "案件详情", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black)
Text(text = "案件详情",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
@ -127,9 +164,15 @@ fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: Order
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderGiveUpActivity.goOrderGiveUpActivity(context, orderInfo = orderInfo, giveUpType = 0)
OrderGiveUpActivity.goOrderGiveUpActivity(context,
orderInfo = orderInfo,
userOrderId = orderInfo?.userOrderId,
giveUpType = 0)
}, contentAlignment = Alignment.Center) {
Text(text = "订单放弃", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Red)
Text(text = "订单放弃",
fontSize = 15.sp,
fontWeight = FontWeight.Medium,
color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
@ -139,8 +182,26 @@ fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: Order
showBottomSheetDialog.value = false
showCallPhoneDialog.value = true
}, contentAlignment = Alignment.Center) {
Text(text = "拨打电话", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black)
Text(text = "拨打客户电话",
fontSize = 15.sp,
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))
}
@ -152,21 +213,27 @@ fun InServicingHeadView(title: String, onBack: () -> Unit = {}, orderInfo: Order
.fillMaxWidth()
.background(color = headBgColor)
.padding(top = headPadding),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors().copy(containerColor = headBgColor, titleContentColor = Color.White),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
.copy(containerColor = headBgColor, titleContentColor = Color.White),
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
navigationIcon = {
if (isCanBack) {
AsyncImage(model = R.drawable.sv_back, contentDescription = "", modifier = Modifier
AsyncImage(model = R.drawable.sv_back,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clickable { onBack() }
.padding(10.dp))
}
}, actions = {
},
actions = {
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())
AsyncImage(model = R.drawable.sv_setting,
contentDescription = "",
modifier = Modifier.fillMaxSize())
}
})
}
@ -180,50 +247,84 @@ fun StartNavigationView(orderInfo: OrderInfo?, dismiss: () -> Unit) {
.fillMaxWidth()
.height(180.dp)
.background(color = Color.White, shape = RoundedCornerShape(8.dp))
.padding(10.dp), verticalAlignment = Alignment.CenterVertically,
.padding(10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround) {
if (MapUtil.isGdMapInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationGd(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress)
MapUtil.startNavigationGd(context,
orderInfo.distLat,
lng = orderInfo.distLng,
address = orderInfo.distAddress)
} else {
MapUtil.startNavigationGd(context, orderInfo?.lat, lng = orderInfo?.lng, address = orderInfo?.address)
MapUtil.startNavigationGd(context,
orderInfo?.lat,
lng = orderInfo?.lng,
address = orderInfo?.address)
}
dismiss()
}) {
AsyncImage(model = R.drawable.sv_amap_icon, contentDescription = "", modifier = Modifier.size(60.dp))
AsyncImage(model = R.drawable.sv_amap_icon,
contentDescription = "",
modifier = Modifier.size(60.dp))
Spacer(modifier = Modifier.height(5.dp))
Text(text = "高德地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
Text(text = "高德地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
}
}
if (MapUtil.isBaiduMapInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationBd(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress)
MapUtil.startNavigationBd(context,
orderInfo.distLat,
lng = orderInfo.distLng,
address = orderInfo.distAddress)
} else {
MapUtil.startNavigationBd(context, orderInfo?.lat, lng = orderInfo?.lng, address = orderInfo?.address)
MapUtil.startNavigationBd(context,
orderInfo?.lat,
lng = orderInfo?.lng,
address = orderInfo?.address)
}
dismiss()
}) {
AsyncImage(model = R.drawable.sv_baidu_icon, contentDescription = "", modifier = Modifier.size(60.dp))
AsyncImage(model = R.drawable.sv_baidu_icon,
contentDescription = "",
modifier = Modifier.size(60.dp))
Spacer(modifier = Modifier.height(5.dp))
Text(text = "百度地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
Text(text = "百度地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
}
}
if (MapUtil.isTencentInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationTencent(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress)
MapUtil.startNavigationTencent(context,
orderInfo.distLat,
lng = orderInfo.distLng,
address = orderInfo.distAddress)
} else {
MapUtil.startNavigationTencent(context, orderInfo?.lat, lng = orderInfo?.lng, address = orderInfo?.address)
MapUtil.startNavigationTencent(context,
orderInfo?.lat,
lng = orderInfo?.lng,
address = orderInfo?.address)
}
dismiss()
}) {
AsyncImage(model = R.drawable.sv_tencent_icon, contentDescription = "", modifier = Modifier.size(60.dp))
AsyncImage(model = R.drawable.sv_tencent_icon,
contentDescription = "",
modifier = Modifier.size(60.dp))
Spacer(modifier = Modifier.height(5.dp))
Text(text = "腾讯地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
Text(text = "腾讯地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
}
}
}

View File

@ -1,8 +1,12 @@
package com.za.ui.servicing.wait_to_start
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@ -55,16 +59,19 @@ import com.amap.api.maps.model.LatLng
import com.amap.api.maps.model.LatLngBounds
import com.amap.api.maps.model.MarkerOptions
import com.amap.api.maps.model.PolylineOptions
import com.blankj.utilcode.util.ToastUtils
import com.za.base.BaseActivity
import com.za.base.theme.headBgColor
import com.za.base.view.CommonDialog
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.util.ImageUtil
import com.za.common.util.ServicingSpeechManager
import com.za.ext.copy
import com.za.ext.finish
import com.za.ext.goNextPage
import com.za.servicing.R
import com.za.ui.camera.ZdCameraXActivity
import com.za.ui.servicing.view.InServicingHeadView
@ -83,13 +90,22 @@ fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) {
val lifecycleOwner = LocalLifecycleOwner.current
val mapView = remember { MapView(context) }
val getResult =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { it ->
if (it.resultCode == Activity.RESULT_OK) {
val value = it.data?.getStringExtra("path")
LogUtil.print("takePhoto", "path==$value")
if (value.isNullOrBlank()) {
ToastUtils.showLong("照片路径为空,请重新拍摄!")
return@rememberLauncherForActivityResult
}
vm.dispatch(WaitToStartVm.Action.CompareServicePeople(value))
}
}
// 添加 BottomSheet 状态
val bottomSheetState = rememberStandardBottomSheetState(
initialValue = SheetValue.Expanded
)
val scaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = bottomSheetState
)
val bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.Expanded)
val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
@ -115,245 +131,171 @@ fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) {
if (uiState.value.goNextPage != null) {
goNextPage(uiState.value.goNextPage?.nextState, context)
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(goNextPage = null)))
}
if (uiState.value.isGoNextPageDialog == true) {
CommonDialog(
cancelText = "取消",
confirmText = "前往下一步",
title = "是否前往下一步?",
if (uiState.value.showServicePeopleConfirmDialog == true) {
CommonDialog(cancelText = "取消",
confirmText = "去拍照",
title = "服务人员确认",
cancelEnable = true,
cancel = {
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = false)))
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
showServicePeopleConfirmDialog = false)))
},
dismiss = {
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
showServicePeopleConfirmDialog = 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)
}
)
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
showServicePeopleConfirmDialog = false)))
val intent = Intent(context, ZdCameraXActivity::class.java)
intent.putExtra("isBack", false)
getResult.launch(intent)
})
}
BottomSheetScaffold(
scaffoldState = scaffoldState,
BottomSheetScaffold(scaffoldState = scaffoldState,
topBar = {
InServicingHeadView(
title = "调度成功,等待发车",
InServicingHeadView(title = "调度成功,等待发车",
onBack = { context.finish() },
orderInfo = uiState.value.orderInfo
)
orderInfo = uiState.value.orderInfo)
},
sheetContent = {
Column(
modifier = Modifier
Column(modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState())
) {
// 滑动指示器
Box(
modifier = Modifier
.verticalScroll(rememberScrollState())) { // 滑动指示器
Box(modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
contentAlignment = Alignment.Center) {
Box(modifier = Modifier
.width(32.dp)
.height(4.dp)
.background(
color = Color(0xFFE0E0E0),
shape = RoundedCornerShape(2.dp)
)
)
.background(color = Color(0xFFE0E0E0), shape = RoundedCornerShape(2.dp)))
}
// 距离和时间信息
Row(
modifier = Modifier
Row(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (uiState.value.estimatedArrivalTime.isNotEmpty())
"预计到达: ${uiState.value.estimatedArrivalTime}"
else
"计算中...",
color = Color(0xFF666666),
fontSize = 14.sp
)
verticalAlignment = Alignment.CenterVertically) {
Text(text = if (uiState.value.estimatedArrivalTime.isNotEmpty()) "预计到达: ${uiState.value.estimatedArrivalTime}"
else "计算中...", color = Color(0xFF666666), fontSize = 14.sp)
Text(
text = if (uiState.value.remainingDistance > 0)
"距离救援地: %.1fkm".format(uiState.value.remainingDistance / 1000f)
else
"计算中...",
color = Color(0xFFFF4D4F),
fontSize = 14.sp
)
Text(text = if (uiState.value.remainingDistance > 0) "距离救援地: %.1fkm".format(
uiState.value.remainingDistance / 1000f)
else "计算中...", color = Color(0xFFFF4D4F), fontSize = 14.sp)
}
HorizontalDivider(
modifier = Modifier
HorizontalDivider(modifier = Modifier
.fillMaxWidth()
.alpha(0.1f)
)
.alpha(0.1f))
// 订单信息
Column(
modifier = Modifier
Column(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)
) {
// 订单类型
Text(
text = uiState.value.orderInfo?.serviceTypeName ?: "",
.padding(horizontal = 16.dp, vertical = 12.dp)) { // 订单类型
Text(text = uiState.value.orderInfo?.serviceTypeName ?: "",
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black
)
color = Color.Black)
Spacer(modifier = Modifier.height(8.dp))
// 订单标签
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Box(
modifier = Modifier
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
)
.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 ?: "",
Text(text = uiState.value.orderInfo?.orderSource ?: "",
color = Color.Black,
fontSize = 12.sp
)
fontSize = 12.sp)
Text(
text = uiState.value.orderInfo?.addressProperty ?: "",
Text(text = uiState.value.orderInfo?.addressProperty ?: "",
color = Color(0xFFFD8205),
fontSize = 12.sp,
fontWeight = FontWeight.Medium
)
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
)
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))
Text(
text = uiState.value.orderInfo?.taskCode ?: "",
Text(text = uiState.value.orderInfo?.taskCode ?: "",
color = Color(0xFF666666),
fontSize = 14.sp
)
fontSize = 14.sp)
Spacer(modifier = Modifier.width(8.dp))
AsyncImage(
model = R.drawable.sv_copy,
AsyncImage(model = R.drawable.sv_copy,
contentDescription = "copy",
modifier = Modifier.size(16.dp)
)
modifier = Modifier.size(16.dp))
}
Spacer(modifier = Modifier.height(16.dp))
// 地址信息
Column(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// 救援地
Row(
verticalAlignment = Alignment.Top,
modifier = Modifier.clickable {
// 点击救援地时移动地图到救援位置
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(
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
)
)
16f))
}
}
}
) {
AsyncImage(
model = R.drawable.sv_rescuing,
}) {
AsyncImage(model = R.drawable.sv_rescuing,
contentDescription = "rescue",
modifier = Modifier.size(16.dp)
)
modifier = Modifier.size(16.dp))
Spacer(modifier = Modifier.width(8.dp))
Text(
text = uiState.value.orderInfo?.address ?: "",
Text(text = uiState.value.orderInfo?.address ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium
)
fontWeight = FontWeight.Medium)
}
// 目的地
if (! uiState.value.orderInfo?.distAddress.isNullOrBlank()) {
Row(
verticalAlignment = Alignment.Top,
modifier = Modifier.clickable {
// 点击目的地时移动地图到目的地位置
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(
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
)
)
16f))
}
}
}
) {
AsyncImage(
model = R.drawable.sv_dist,
}) {
AsyncImage(model = R.drawable.sv_dist,
contentDescription = "destination",
modifier = Modifier.size(16.dp)
)
modifier = Modifier.size(16.dp))
Spacer(modifier = Modifier.width(8.dp))
Text(
text = uiState.value.orderInfo?.distAddress ?: "",
Text(text = uiState.value.orderInfo?.distAddress ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium
)
fontWeight = FontWeight.Medium)
}
}
}
@ -361,23 +303,20 @@ fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) {
Spacer(modifier = Modifier.height(16.dp))
// 发车按钮
Button(
onClick = { vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(isGoNextPageDialog = true))) },
Button(onClick = {
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
showServicePeopleConfirmDialog = true)))
},
modifier = Modifier
.fillMaxWidth()
.height(44.dp)
.padding(horizontal = 16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = headBgColor
),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = "发车",
colors = ButtonDefaults.buttonColors(containerColor = headBgColor),
shape = RoundedCornerShape(8.dp)) {
Text(text = "发车",
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
)
fontWeight = FontWeight.Medium)
}
Spacer(modifier = Modifier.height(16.dp))
@ -388,16 +327,11 @@ fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) {
sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp),
sheetContainerColor = Color.White,
sheetDragHandle = null,
sheetSwipeEnabled = true
) { paddingValues ->
Box(
modifier = Modifier
sheetSwipeEnabled = true) { paddingValues ->
Box(modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = {
.padding(paddingValues)) {
AndroidView(modifier = Modifier.fillMaxSize(), factory = {
AMapLocationClient.updatePrivacyShow(context, true, true)
AMapLocationClient.updatePrivacyAgree(context, true)
mapView.apply {
@ -410,8 +344,7 @@ fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) {
// 修改标记点点击事件
setOnMarkerClickListener { marker ->
marker.showInfoWindow()
// 800ms后隐藏信息窗口
marker.showInfoWindow() // 800ms后隐藏信息窗口
Handler(Looper.getMainLooper()).postDelayed({
marker.hideInfoWindow()
}, 800)
@ -419,88 +352,58 @@ fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) {
}
}
}
},
update = {
// 清除旧标记和路线
}, update = { // 清除旧标记和路线
mapView.map.clear()
// 先绘制路线
uiState.value.routePoints?.let { points ->
mapView.map.addPolyline(
PolylineOptions()
.addAll(points)
.width(15f)
mapView.map.addPolyline(PolylineOptions().addAll(points).width(15f)
.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.icon_road_green_arrow))
.zIndex(1f)
)
.zIndex(1f))
}
// 再添加标记点,确保标记点在路线上层
// 添加当前位置标记
if (GlobalData.currentLocation != null) {
mapView.map.addMarker(
MarkerOptions()
.position(LatLng(
GlobalData.currentLocation?.latitude!!,
GlobalData.currentLocation?.longitude!!
))
.title("当前位置")
mapView.map.addMarker(MarkerOptions().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)
)
.anchor(0.5f, 0.5f).visible(true))
}
// 添加救援地标记
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 &&
order.lng != null && order.lng != 0.0 &&
GlobalData.currentLocation != null
) {
mapView.map.addMarker(
MarkerOptions()
.position(LatLng(order.lat!!, order.lng!!))
.title("救援地点")
if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0 && GlobalData.currentLocation != null) {
mapView.map.addMarker(MarkerOptions().position(LatLng(order.lat !!,
order.lng !!)).title("救援地点")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.anchor(0.5f, 0.5f)
)
.anchor(0.5f, 0.5f))
}
// 添加目的地标记
if (order.distLat != null && order.distLat != 0.0 &&
order.distLng != null && order.distLng != 0.0 &&
GlobalData.currentLocation != null
) {
mapView.map.addMarker(
MarkerOptions()
.position(LatLng(order.distLat!!, order.distLng!!))
.title("目的地")
if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0 && GlobalData.currentLocation != null) {
mapView.map.addMarker(MarkerOptions().position(LatLng(order.distLat !!,
order.distLng !!)).title("目的地")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_dist_map))
.anchor(0.5f, 0.5f)
)
.anchor(0.5f, 0.5f))
}
}
// 最后调整地图显示范围
// 计算地图显示范围
val bounds = LatLngBounds.Builder().apply {
// 添加当前位置
val bounds = LatLngBounds.Builder().apply { // 添加当前位置
GlobalData.currentLocation?.let {
include(LatLng(it.latitude, it.longitude))
}
// 添加救援地点
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 &&
order.lng != null && order.lng != 0.0
) {
if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0) {
include(LatLng(order.lat !!, order.lng !!))
}
// 添加目的地
if (order.distLat != null && order.distLat != 0.0 &&
order.distLng != null && order.distLng != 0.0
) {
if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0) {
include(LatLng(order.distLat !!, order.distLng !!))
}
}
@ -508,22 +411,14 @@ fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) {
// 调整地图显示范围,确保所有点都可见
try {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngBounds(bounds, 100)
)
} catch (e: Exception) {
// 如果计算边界失败,则使用默认缩放级别
mapView.map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100))
} catch (e : Exception) { // 如果计算边界失败,则使用默认缩放级别
GlobalData.currentLocation?.let {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(it.latitude, it.longitude),
15f
)
)
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.view.LoadingManager
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.UpdateTaskRequest
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.ext.toJson
import com.za.net.BaseObserver
import com.za.net.CommonMethod
import com.za.net.RetrofitHelper
import com.za.service.location.ZdLocationManager
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.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
@ -47,6 +54,7 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
is Action.UpdateState -> updateState(action.uiState)
is Action.StartTimer -> startTimer()
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 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() {
super.onCleared()
timerJob?.cancel()
@ -227,14 +281,15 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
data class UpdateState(val uiState : UiState) : Action()
data object StartTimer : Action()
data object UpdateTimer : Action()
data class CompareServicePeople(val localPath : String) : Action()
}
data class UiState(val orderInfo : OrderInfo? = null,
val showCallPhoneDialog : Boolean? = false,
val markers : ArrayList<MarkerOptions>? = null,
val goNextPage : UpdateTaskBean? = null,
val isGoNextPageDialog : Boolean? = null,
val routePoints : List<LatLng>? = null,
val remainingDistance : Float = 0f,
val showServicePeopleConfirmDialog : Boolean? = false,
val estimatedArrivalTime : String = "")
}

View File

@ -1,14 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="11dp"
android:height="11dp"
android:viewportWidth="11"
android:viewportHeight="11">
android:width="128dp"
android:height="128dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<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:strokeAlpha="0.8312872"
android:strokeWidth="1"
android:fillColor="#9197AB"
android:fillType="nonZero"
android:strokeColor="#00000000"
android:fillAlpha="0.8312872"/>
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.3"
android:fillColor="#1A73E8"
android:fillAlpha="0.3"/>
<path
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:fillColor="#1A73E8"/>
</vector>

View File

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

View File

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

BIN
zd_sdk_demo.jks Normal file

Binary file not shown.