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

@ -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, //车牌号
@ -216,4 +217,4 @@ data class AppNewDriverInfoDTO(val userId : Int? = null,
return arrayOfNulls(size)
}
}
}
}

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,22 +133,46 @@ object CommonMethod {
LogUtil.print("getGenerateInfo", "获取车辆信息失败")
return
}
val driverInfoBean = GlobalData.driverInfoBean
GlobalData.driverInfoBean = 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,
supplierId = it.supplierId,
deviceId = it.deviceId,
supplierName = it.supplierName,
authStatus = it.authStatus,
serviceList = it.serviceList?.toString())
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,
supplierType = it.supplierType,
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",
"${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

@ -65,457 +65,490 @@ import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class ZdCameraXActivity : AppCompatActivity() {
private var exposurePopupWindow: PopupWindow? = null
private var exposurePopupWindow : PopupWindow? = null
internal enum class FlashMode {
auto, open, light, close
}
internal enum class FlashMode {
auto, open, light, close
}
private var rootView: ConstraintLayout? = null
private var imageCapture: ImageCapture? = null
private var cameraInfo: CameraInfo? = null
private var rootView : ConstraintLayout? = null
private var imageCapture : ImageCapture? = null
private var cameraInfo : CameraInfo? = null
private var exposureState: ExposureState? = null
private var cameraExecutor: ExecutorService? = null
private var zoomState: LiveData<ZoomState>? = null
private var viewFinder: PreviewView? = null
private var takePhoto: TakePhotoButtonView? = null
private var ivFlash: ImageView? = null
private var ivChangeCamera: ImageView? = null
private var ivConfirm: ImageView? = null
private var ivCancel: ImageView? = null
private lateinit var ivPreview: ImageFilterView
private var ivBack: ImageView? = null
private var sliderExposure: AppCompatSeekBar? = null
private var groupOperation: Group? = null
private var groupPreview: Group? = null
private var uri: Uri? = null
private var animatorSet: AnimatorSet? = null
private var location: Location? = null
private var camera: Camera? = null
private var isBack = true
private var exposureState : ExposureState? = null
private var cameraExecutor : ExecutorService? = null
private var zoomState : LiveData<ZoomState>? = null
private var viewFinder : PreviewView? = null
private var takePhoto : TakePhotoButtonView? = null
private var ivFlash : ImageView? = null
private var ivChangeCamera : ImageView? = null
private var ivConfirm : ImageView? = null
private var ivCancel : ImageView? = null
private lateinit var ivPreview : ImageFilterView
private var ivBack : ImageView? = null
private var sliderExposure : AppCompatSeekBar? = null
private var groupOperation : Group? = null
private var groupPreview : Group? = null
private var uri : Uri? = null
private var animatorSet : AnimatorSet? = null
private var location : Location? = null
private var camera : Camera? = null
private var isBack = true
private var orientationEventListener: OrientationEventListener? = null
private var flashPopWindow: PopupWindow? = null
private var flashMode = FlashMode.close
private var orientationEventListener : OrientationEventListener? = null
private var flashPopWindow : PopupWindow? = null
private var flashMode = FlashMode.close
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_zd_camera_xactivity)
initView()
initLocation()
startCamera()
setOnClick()
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_zd_camera_xactivity)
isBack = intent.getBooleanExtra("isBack", true)
initView()
initLocation()
startCamera()
setOnClick()
orientationEventListener = object : OrientationEventListener(this) {
override fun onOrientationChanged(orientation: Int) {
if (orientation == ORIENTATION_UNKNOWN || imageCapture == null) {
print("zdCamerax orientation", "orientation==$orientation")
return
}
when (orientation) {
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
}
}
}
}
}
orientationEventListener = object : OrientationEventListener(this) {
override fun onOrientationChanged(orientation : Int) {
if (orientation == ORIENTATION_UNKNOWN || imageCapture == null) {
print("zdCamerax orientation", "orientation==$orientation")
return
}
when (orientation) {
in 45 .. 134 -> {
imageCapture?.targetRotation = Surface.ROTATION_270
}
@SuppressLint("ObjectAnimatorBinding")
private fun initView() {
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))
in 135 .. 224 -> {
imageCapture?.targetRotation = Surface.ROTATION_180
}
animatorSet?.setDuration(500)
}
in 225 .. 314 -> {
imageCapture?.targetRotation = Surface.ROTATION_90
}
@SuppressLint("ClickableViewAccessibility")
private fun setOnClick() {
takePhoto?.setOnClickListener(ClickProxy { v: View? -> takePhoto() })
else -> {
imageCapture?.targetRotation = Surface.ROTATION_0
}
}
}
}
}
ivChangeCamera?.setOnClickListener(ClickProxy { v: View? ->
isBack = !isBack
if (!isBack) {
if (flashPopWindow != null && flashPopWindow?.isShowing == true) {
flashPopWindow?.dismiss()
}
flashMode = FlashMode.close
ivFlash?.setImageResource(R.drawable.picture_ic_flash_off)
}
startCamera()
})
@SuppressLint("ObjectAnimatorBinding")
private fun initView() {
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))
ivFlash?.setOnClickListener {
if (!isBack) {
ToastUtils.showShort("前置模式下无法开启!")
return@setOnClickListener
}
if (flashPopWindow != null && flashPopWindow?.isShowing==true) {
flashPopWindow?.dismiss()
} else {
showFlashView()
}
}
animatorSet?.setDuration(500)
}
ivCancel?.setOnClickListener {
groupPreview?.visibility = View.GONE
groupOperation?.visibility = View.VISIBLE
}
@SuppressLint("ClickableViewAccessibility")
private fun setOnClick() {
takePhoto?.setOnClickListener(ClickProxy { v : View? -> takePhoto() })
ivConfirm?.setOnClickListener {
val intent = Intent()
if (uri != null) {
intent.putExtra("path", UriUtils.uri2File(uri).absolutePath)
}
setResult(RESULT_OK, intent)
finish()
}
ivChangeCamera?.setOnClickListener(ClickProxy { v : View? ->
isBack = ! isBack
if (! isBack) {
if (flashPopWindow != null && flashPopWindow?.isShowing == true) {
flashPopWindow?.dismiss()
}
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 {
@SuppressLint("SetTextI18n")
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
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() + ""
}
}
}
ivCancel?.setOnClickListener {
groupPreview?.visibility = View.GONE
groupOperation?.visibility = View.VISIBLE
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
showExposureView()
}
ivConfirm?.setOnClickListener {
val intent = Intent()
if (uri != null) {
intent.putExtra("path", UriUtils.uri2File(uri).absolutePath)
}
setResult(RESULT_OK, intent)
finish()
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
viewFinder?.postDelayed({ exposurePopupWindow?.dismiss() }, 1000)
}
})
ivBack?.setOnClickListener { finish() }
val cameraXPreviewViewTouchListener = CameraXPreviewViewTouchListener(this)
viewFinder?.setOnTouchListener(cameraXPreviewViewTouchListener)
cameraXPreviewViewTouchListener.setCustomTouchListener(object : CustomTouchListener {
override fun zoom(delta: Float) {
if (zoomState == null) {
return
}
val currentZoomRatio = zoomState?.value?.zoomRatio
currentZoomRatio?.let {
camera?.cameraControl?.setZoomRatio(it.times(delta))
}
sliderExposure?.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
@SuppressLint("SetTextI18n")
override fun onProgressChanged(seekBar : SeekBar, progress : Int, fromUser : Boolean) {
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) {
showExposureView()
}
override fun click(event: MotionEvent) {
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)
}
}
override fun onStopTrackingTouch(seekBar : SeekBar) {
viewFinder?.postDelayed({ exposurePopupWindow?.dismiss() }, 1000)
}
})
override fun doubleClick(x: Float, y: Float) {
if (zoomState == null) {
return
}
val currentZoomRatio = zoomState?.value?.zoomRatio
zoomState?.value?.minZoomRatio?.let {
if (currentZoomRatio!! > it) {
camera?.cameraControl?.setLinearZoom(0f)
} else {
camera?.cameraControl?.setLinearZoom(0.5f)
}
}
val cameraXPreviewViewTouchListener = CameraXPreviewViewTouchListener(this)
viewFinder?.setOnTouchListener(cameraXPreviewViewTouchListener)
cameraXPreviewViewTouchListener.setCustomTouchListener(object : CustomTouchListener {
override fun zoom(delta : Float) {
if (zoomState == null) {
return
}
val currentZoomRatio = zoomState?.value?.zoomRatio
currentZoomRatio?.let {
camera?.cameraControl?.setZoomRatio(it.times(delta))
}
}
})
}
}
private fun takePhoto() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ThreadUtils.runOnUiThread {
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")
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()
override fun click(event : MotionEvent) {
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)
}
}
override fun doubleClick(x : Float, y : Float) {
if (zoomState == null) {
return
}
val currentZoomRatio = zoomState?.value?.zoomRatio
zoomState?.value?.minZoomRatio?.let {
if (currentZoomRatio !! > it) {
camera?.cameraControl?.setLinearZoom(0f)
} else {
camera?.cameraControl?.setLinearZoom(0.5f)
}
}
}
})
}
private fun takePhoto() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ThreadUtils.runOnUiThread {
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")
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.
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 output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions
.Builder(contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
.build()
// 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 {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
this@ZdCameraXActivity.uri = outputFileResults.savedUri
//保存照片信息
val exifInterface: ExifInterface
try {
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!!)
}
}
imageCapture?.takePicture(outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults : ImageCapture.OutputFileResults) {
this@ZdCameraXActivity.uri = outputFileResults.savedUri //保存照片信息
val exifInterface : ExifInterface
try {
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.saveAttributes()
takePhoto?.cancelAnimation()
groupPreview?.visibility = View.VISIBLE
if (!this@ZdCameraXActivity.isFinishing) {
Glide.with(this@ZdCameraXActivity).load(outputFileResults.savedUri).into(ivPreview)
}
groupOperation?.visibility = View.GONE
animatorSet?.start()
} catch (e: IOException) {
takePhoto?.cancelAnimation()
ToastUtils.showShort("照片保存失败" + e.message)
print("照片保存失败", e)
}
}
exifInterface.saveAttributes()
takePhoto?.cancelAnimation()
groupPreview?.visibility = View.VISIBLE
if (! this@ZdCameraXActivity.isFinishing) {
Glide.with(this@ZdCameraXActivity).load(outputFileResults.savedUri)
.into(ivPreview)
}
groupOperation?.visibility = View.GONE
animatorSet?.start()
} catch (e : IOException) {
takePhoto?.cancelAnimation()
ToastUtils.showShort("照片保存失败" + e.message)
print("照片保存失败", e)
}
}
override fun onError(exception: ImageCaptureException) {
takePhoto?.cancelAnimation()
ToastUtils.showShort("Photo capture failed" + exception.message)
print("onCameraError", exception)
}
})
}
override fun onError(exception : ImageCaptureException) {
takePhoto?.cancelAnimation()
ToastUtils.showShort("Photo capture failed" + exception.message)
print("onCameraError", exception)
}
})
}
private fun startCamera() {
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()
private fun startCamera() {
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.targetRotation = Surface.ROTATION_0
cameraProvider.unbindAll()
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
cameraInfo = camera?.cameraInfo
zoomState = cameraInfo?.zoomState
exposureState = cameraInfo?.exposureState
if (exposureState?.isExposureCompensationSupported == true) {
sliderExposure?.visibility = View.VISIBLE
sliderExposure?.max = exposureState?.exposureCompensationRange?.upper ?: 1
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
sliderExposure?.min = exposureState?.exposureCompensationRange?.lower ?: 1
}
} else {
sliderExposure?.visibility = View.GONE
}
} catch (e: ExecutionException) {
finish()
print("相机初始化失败", e)
} catch (e: InterruptedException) {
finish()
print("相机初始化失败", e)
}
}, ContextCompat.getMainExecutor(this))
}
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)
cameraInfo = camera?.cameraInfo
zoomState = cameraInfo?.zoomState
exposureState = cameraInfo?.exposureState
if (exposureState?.isExposureCompensationSupported == true) {
sliderExposure?.visibility = View.VISIBLE
sliderExposure?.max = exposureState?.exposureCompensationRange?.upper ?: 1
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
sliderExposure?.min = exposureState?.exposureCompensationRange?.lower ?: 1
}
} else {
sliderExposure?.visibility = View.GONE
}
} catch (e : ExecutionException) {
finish()
print("相机初始化失败", e)
} catch (e : InterruptedException) {
finish()
print("相机初始化失败", e)
}
}, ContextCompat.getMainExecutor(this))
}
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)
} else {
if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
}
}
}
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)
} else {
if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
}
}
}
@SuppressLint("RtlHardcoded")
private fun showTapView(x: Int, y: Int) {
if (takePhoto?.visibility == View.GONE) {
return
}
try {
val popupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
popupWindow.width = 200
popupWindow.height = 200
val imageView = ImageView(this)
imageView.setImageResource(R.drawable.focus_focusing)
imageView.scaleType = ImageView.ScaleType.CENTER_CROP
popupWindow.contentView = imageView
popupWindow.showAtLocation(rootView, Gravity.LEFT or Gravity.TOP, x, y)
viewFinder?.postDelayed({ popupWindow.dismiss() }, 1000)
viewFinder?.playSoundEffect(SoundEffectConstants.CLICK)
} catch (e: Exception) {
print("zdCameraX showTapView", e)
}
}
@SuppressLint("RtlHardcoded")
private fun showTapView(x : Int, y : Int) {
if (takePhoto?.visibility == View.GONE) {
return
}
try {
val popupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
popupWindow.width = 200
popupWindow.height = 200
val imageView = ImageView(this)
imageView.setImageResource(R.drawable.focus_focusing)
imageView.scaleType = ImageView.ScaleType.CENTER_CROP
popupWindow.contentView = imageView
popupWindow.showAtLocation(rootView, Gravity.LEFT or Gravity.TOP, x, y)
viewFinder?.postDelayed({ popupWindow.dismiss() }, 1000)
viewFinder?.playSoundEffect(SoundEffectConstants.CLICK)
} catch (e : Exception) {
print("zdCameraX showTapView", e)
}
}
@SuppressLint("RtlHardcoded", "SetTextI18n")
private fun showExposureView() {
if (takePhoto?.visibility == View.GONE) {
return
}
exposurePopupWindow = PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
exposurePopupWindow?.width = 300
exposurePopupWindow?.height = 200
val textView = TextView(this)
textView.setTextColor(Color.YELLOW)
textView.textSize = 50f
textView.tag = "tv_exposureView"
textView.gravity = Gravity.CENTER
textView.text = exposureState?.exposureCompensationIndex.toString() + ""
exposurePopupWindow?.contentView = textView
exposurePopupWindow?.showAtLocation(rootView, Gravity.CENTER, 0, 0)
}
@SuppressLint("RtlHardcoded", "SetTextI18n")
private fun showExposureView() {
if (takePhoto?.visibility == View.GONE) {
return
}
exposurePopupWindow =
PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
exposurePopupWindow?.width = 300
exposurePopupWindow?.height = 200
val textView = TextView(this)
textView.setTextColor(Color.YELLOW)
textView.textSize = 50f
textView.tag = "tv_exposureView"
textView.gravity = Gravity.CENTER
textView.text = exposureState?.exposureCompensationIndex.toString() + ""
exposurePopupWindow?.contentView = textView
exposurePopupWindow?.showAtLocation(rootView, Gravity.CENTER, 0, 0)
}
@SuppressLint("RtlHardcoded")
private fun showFlashView() {
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)
val tvFlashAuto = view.findViewById<TextView>(R.id.tvFlashAuto)
val tvFlashOpen = view.findViewById<TextView>(R.id.tvFlashOpen)
val tvFlashLight = view.findViewById<TextView>(R.id.tvFlashLight)
val tvFlashClose = view.findViewById<TextView>(R.id.tvFlashClose)
@SuppressLint("RtlHardcoded")
private fun showFlashView() {
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)
val tvFlashAuto = view.findViewById<TextView>(R.id.tvFlashAuto)
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) {
FlashMode.auto -> tvFlashAuto.setTextColor(Color.YELLOW)
FlashMode.open -> tvFlashOpen.setTextColor(Color.YELLOW)
FlashMode.light -> tvFlashLight.setTextColor(Color.YELLOW)
FlashMode.close -> tvFlashClose.setTextColor(Color.YELLOW)
}
tvFlashAuto.setOnClickListener { v: View? ->
if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_AUTO
ivFlash?.setImageResource(R.drawable.picture_ic_flash_auto)
flashMode = FlashMode.auto
}
flashPopWindow?.dismiss()
}
tvFlashOpen.setOnClickListener { v: View? ->
if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_ON
ivFlash?.setImageResource(R.drawable.picture_ic_flash_on)
flashMode = FlashMode.open
}
flashPopWindow?.dismiss()
}
when (flashMode) {
FlashMode.auto -> tvFlashAuto.setTextColor(Color.YELLOW)
FlashMode.open -> tvFlashOpen.setTextColor(Color.YELLOW)
FlashMode.light -> tvFlashLight.setTextColor(Color.YELLOW)
FlashMode.close -> tvFlashClose.setTextColor(Color.YELLOW)
}
tvFlashAuto.setOnClickListener { v : View? ->
if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_AUTO
ivFlash?.setImageResource(R.drawable.picture_ic_flash_auto)
flashMode = FlashMode.auto
}
flashPopWindow?.dismiss()
}
tvFlashOpen.setOnClickListener { v : View? ->
if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_ON
ivFlash?.setImageResource(R.drawable.picture_ic_flash_on)
flashMode = FlashMode.open
}
flashPopWindow?.dismiss()
}
tvFlashLight.setOnClickListener { v: View? ->
if (camera != null) {
camera?.cameraControl?.enableTorch(true)
ivFlash?.setImageResource(R.drawable.ic_flash_light)
flashMode = FlashMode.light
}
flashPopWindow?.dismiss()
}
tvFlashLight.setOnClickListener { v : View? ->
if (camera != null) {
camera?.cameraControl?.enableTorch(true)
ivFlash?.setImageResource(R.drawable.ic_flash_light)
flashMode = FlashMode.light
}
flashPopWindow?.dismiss()
}
tvFlashClose.setOnClickListener { v: View? ->
if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF
ivFlash?.setImageResource(R.drawable.picture_ic_flash_off)
flashMode = FlashMode.close
}
flashPopWindow?.dismiss()
}
tvFlashClose.setOnClickListener { v : View? ->
if (imageCapture != null) {
camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF
ivFlash?.setImageResource(R.drawable.picture_ic_flash_off)
flashMode = FlashMode.close
}
flashPopWindow?.dismiss()
}
flashPopWindow?.contentView = view
flashPopWindow?.showAtLocation(ivFlash, Gravity.LEFT or Gravity.TOP, 0, ivFlash?.y?.toInt()?.minus(view.height)
?: 0)
}
flashPopWindow?.contentView = view
flashPopWindow?.showAtLocation(ivFlash,
Gravity.LEFT or Gravity.TOP,
0,
ivFlash?.y?.toInt()?.minus(view.height) ?: 0)
}
override fun onPause() {
super.onPause()
if (camera != null && imageCapture != null) {
camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF
ivFlash?.setImageResource(R.drawable.picture_ic_flash_off)
}
}
override fun onPause() {
super.onPause()
if (camera != null && imageCapture != null) {
camera?.cameraControl?.enableTorch(false)
imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF
ivFlash?.setImageResource(R.drawable.picture_ic_flash_off)
}
}
override fun onStart() {
super.onStart()
orientationEventListener?.enable()
}
override fun onStart() {
super.onStart()
orientationEventListener?.enable()
}
override fun onStop() {
super.onStop()
orientationEventListener?.disable()
}
override fun onStop() {
super.onStop()
orientationEventListener?.disable()
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor?.shutdown()
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor?.shutdown()
}
}

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 = "加载失败")
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)
}
}
}
} 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()
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

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

View File

@ -46,186 +46,287 @@ import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
@OptIn(ExperimentalMaterial3Api::class)
@Composable
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) }
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 (showNavigationDialog.value) {
StartNavigationView(orderInfo = orderInfo, dismiss = { showNavigationDialog.value = false })
}
if (showCallPhoneDialog.value) {
CommonDialog(
cancelText = "取消",
confirmText = "确定",
title = "是否联系客户?",
cancelEnable = true,
cancel = {
showCallPhoneDialog.value = false
},
dismiss = { showCallPhoneDialog.value = false },
confirm = {
showCallPhoneDialog.value = false
context.callPhone(orderInfo?.customerPhone)
context.finish()
})
}
if (showCallPhoneDialog.value) {
CommonDialog(cancelText = "取消",
confirmText = "确定",
title = "是否联系客户?",
cancelEnable = true,
cancel = {
showCallPhoneDialog.value = false
},
dismiss = { showCallPhoneDialog.value = false },
confirm = {
showCallPhoneDialog.value = false
context.callPhone(orderInfo?.customerPhone)
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) {
ModalBottomSheet(containerColor = Color.White,
onDismissRequest = { showBottomSheetDialog.value = false }) {
Column(modifier = Modifier
.fillMaxWidth()
.padding(10.dp)) {
if (showBottomSheetDialog.value) {
ModalBottomSheet(containerColor = Color.White,
onDismissRequest = { showBottomSheetDialog.value = false }) {
Column(modifier = Modifier
.fillMaxWidth()
.padding(10.dp)) {
if (!orderInfo?.customerReportImgs.isNullOrBlank()) {
Box(modifier = Modifier
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ON_SITE_PHOTO)
}, contentAlignment = Alignment.Center) {
Text(text = "现场照片", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
}
if (! orderInfo?.customerReportImgs.isNullOrBlank()) {
Box(modifier = Modifier
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context,
orderInfo,
type = Const.InServiceSettingType.ON_SITE_PHOTO)
}, 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
.fillMaxWidth()
.clickable {
showNavigationDialog.value = true
showBottomSheetDialog.value = false
}, 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
.fillMaxWidth()
.clickable {
showNavigationDialog.value = true
showBottomSheetDialog.value = false
}, 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
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ORDER_REQUIREMENTS)
}, 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
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context,
orderInfo,
type = Const.InServiceSettingType.ORDER_REQUIREMENTS)
}, 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
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context, orderInfo, type = Const.InServiceSettingType.ORDER_DETAIL)
}, 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
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderRequirementsActivity.goOrderRequirementsActivity(context,
orderInfo,
type = Const.InServiceSettingType.ORDER_DETAIL)
}, 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
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderGiveUpActivity.goOrderGiveUpActivity(context, orderInfo = orderInfo, giveUpType = 0)
}, contentAlignment = Alignment.Center) {
Text(text = "订单放弃", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Red)
}
HorizontalDivider(modifier = Modifier.padding(vertical = 10.dp), color = black5)
Box(modifier = Modifier
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
OrderGiveUpActivity.goOrderGiveUpActivity(context,
orderInfo = orderInfo,
userOrderId = orderInfo?.userOrderId,
giveUpType = 0)
}, 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
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
showCallPhoneDialog.value = true
}, contentAlignment = Alignment.Center) {
Text(text = "拨打电话", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Color.Black)
}
Spacer(modifier = Modifier.height(10.dp))
Box(modifier = Modifier
.fillMaxWidth()
.clickable {
showBottomSheetDialog.value = false
showCallPhoneDialog.value = true
}, 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
.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
.fillMaxWidth()
.background(color = headBgColor)
.padding(top = headPadding),
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
.size(40.dp)
.clickable { onBack() }
.padding(10.dp))
}
}, 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())
}
})
CenterAlignedTopAppBar(modifier = Modifier
.fillMaxWidth()
.background(color = headBgColor)
.padding(top = headPadding),
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
.size(40.dp)
.clickable { onBack() }
.padding(10.dp))
}
},
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())
}
})
}
@Composable
fun StartNavigationView(orderInfo: OrderInfo?, dismiss: () -> Unit) {
val context = LocalContext.current
fun StartNavigationView(orderInfo : OrderInfo?, dismiss : () -> Unit) {
val context = LocalContext.current
Dialog(onDismissRequest = { dismiss() }) {
Row(modifier = Modifier
.fillMaxWidth()
.height(180.dp)
.background(color = Color.White, shape = RoundedCornerShape(8.dp))
.padding(10.dp), verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround) {
if (MapUtil.isGdMapInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationGd(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress)
} else {
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))
Spacer(modifier = Modifier.height(5.dp))
Text(text = "高德地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
}
}
Dialog(onDismissRequest = { dismiss() }) {
Row(modifier = Modifier
.fillMaxWidth()
.height(180.dp)
.background(color = Color.White, shape = RoundedCornerShape(8.dp))
.padding(10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround) {
if (MapUtil.isGdMapInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationGd(context,
orderInfo.distLat,
lng = orderInfo.distLng,
address = orderInfo.distAddress)
} else {
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))
Spacer(modifier = Modifier.height(5.dp))
Text(text = "高德地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
}
}
if (MapUtil.isBaiduMapInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationBd(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress)
} else {
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))
Spacer(modifier = Modifier.height(5.dp))
Text(text = "百度地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
}
}
if (MapUtil.isBaiduMapInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationBd(context,
orderInfo.distLat,
lng = orderInfo.distLng,
address = orderInfo.distAddress)
} else {
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))
Spacer(modifier = Modifier.height(5.dp))
Text(text = "百度地图",
color = Color.Black,
fontWeight = FontWeight.Medium,
fontSize = 14.sp)
}
}
if (MapUtil.isTencentInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationTencent(context, orderInfo.distLat, lng = orderInfo.distLng, address = orderInfo.distAddress)
} else {
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))
Spacer(modifier = Modifier.height(5.dp))
Text(text = "腾讯地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
}
}
}
}
if (MapUtil.isTencentInstalled(context)) {
Column(modifier = Modifier.clickable {
if ("SENDTO" == orderInfo?.taskState) {
MapUtil.startNavigationTencent(context,
orderInfo.distLat,
lng = orderInfo.distLng,
address = orderInfo.distAddress)
} else {
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))
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
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,476 +59,367 @@ 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
class WaitToStartActivity : BaseActivity() {
@Composable
override fun ContentView() {
WaitToStartScreen()
}
@Composable
override fun ContentView() {
WaitToStartScreen()
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WaitToStartScreen(vm: WaitToStartVm = viewModel()) {
val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val mapView = remember { MapView(context) }
fun WaitToStartScreen(vm : WaitToStartVm = viewModel()) {
val uiState = vm.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val mapView = remember { MapView(context) }
// 添加 BottomSheet 状态
val bottomSheetState = rememberStandardBottomSheetState(
initialValue = SheetValue.Expanded
)
val scaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = bottomSheetState
)
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))
}
}
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_CREATE -> {
mapView.onCreate(Bundle())
vm.dispatch(WaitToStartVm.Action.Init)
ServicingSpeechManager.playStartTip(context)
}
// 添加 BottomSheet 状态
val bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.Expanded)
val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
Lifecycle.Event.ON_RESUME -> mapView.onResume()
Lifecycle.Event.ON_PAUSE -> mapView.onPause()
Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
else -> {}
}
}
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_CREATE -> {
mapView.onCreate(Bundle())
vm.dispatch(WaitToStartVm.Action.Init)
ServicingSpeechManager.playStartTip(context)
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
Lifecycle.Event.ON_RESUME -> mapView.onResume()
Lifecycle.Event.ON_PAUSE -> mapView.onPause()
Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
else -> {}
}
}
if (uiState.value.goNextPage != null) {
goNextPage(uiState.value.goNextPage?.nextState, context)
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
if (uiState.value.isGoNextPageDialog == true) {
CommonDialog(
cancelText = "取消",
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)
}
)
}
if (uiState.value.goNextPage != null) {
goNextPage(uiState.value.goNextPage?.nextState, context)
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(goNextPage = null)))
}
BottomSheetScaffold(
scaffoldState = scaffoldState,
topBar = {
InServicingHeadView(
title = "调度成功,等待发车",
onBack = { context.finish() },
orderInfo = uiState.value.orderInfo
)
},
sheetContent = {
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState())
) {
// 滑动指示器
Box(
modifier = Modifier
.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)
)
)
}
if (uiState.value.showServicePeopleConfirmDialog == true) {
CommonDialog(cancelText = "取消",
confirmText = "去拍照",
title = "服务人员确认",
cancelEnable = true,
cancel = {
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
showServicePeopleConfirmDialog = false)))
},
dismiss = {
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
showServicePeopleConfirmDialog = false)))
},
confirm = {
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
showServicePeopleConfirmDialog = false)))
val intent = Intent(context, ZdCameraXActivity::class.java)
intent.putExtra("isBack", false)
getResult.launch(intent)
})
}
// 距离和时间信息
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
)
BottomSheetScaffold(scaffoldState = scaffoldState,
topBar = {
InServicingHeadView(title = "调度成功,等待发车",
onBack = { context.finish() },
orderInfo = uiState.value.orderInfo)
},
sheetContent = {
Column(modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState())) { // 滑动指示器
Box(modifier = Modifier
.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)))
}
Text(
text = if (uiState.value.remainingDistance > 0)
"距离救援地: %.1fkm".format(uiState.value.remainingDistance / 1000f)
else
"计算中...",
color = Color(0xFFFF4D4F),
fontSize = 14.sp
)
}
// 距离和时间信息
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)
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.alpha(0.1f)
)
Text(text = if (uiState.value.remainingDistance > 0) "距离救援地: %.1fkm".format(
uiState.value.remainingDistance / 1000f)
else "计算中...", color = Color(0xFFFF4D4F), fontSize = 14.sp)
}
// 订单信息
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
)
HorizontalDivider(modifier = Modifier
.fillMaxWidth()
.alpha(0.1f))
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)
// 订单标签
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
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = uiState.value.orderInfo?.orderSource ?: "",
color = Color.Black,
fontSize = 12.sp
)
// 订单标签
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?.addressProperty ?: "",
color = Color(0xFFFD8205),
fontSize = 12.sp,
fontWeight = FontWeight.Medium
)
}
Text(text = uiState.value.orderInfo?.orderSource ?: "",
color = Color.Black,
fontSize = 12.sp)
Spacer(modifier = Modifier.height(12.dp))
Text(text = uiState.value.orderInfo?.addressProperty ?: "",
color = Color(0xFFFD8205),
fontSize = 12.sp,
fontWeight = FontWeight.Medium)
}
// 订单号
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { uiState.value.orderInfo?.taskCode?.copy(context) }
) {
Text(
text = "单号",
color = Color(0xFF999999),
fontSize = 13.sp
)
Spacer(modifier = Modifier.height(12.dp))
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(
text = uiState.value.orderInfo?.taskCode ?: "",
color = Color(0xFF666666),
fontSize = 14.sp
)
Spacer(modifier = Modifier.width(8.dp))
Spacer(modifier = Modifier.width(8.dp))
Text(text = uiState.value.orderInfo?.taskCode ?: "",
color = Color(0xFF666666),
fontSize = 14.sp)
AsyncImage(
model = R.drawable.sv_copy,
contentDescription = "copy",
modifier = Modifier.size(16.dp)
)
}
Spacer(modifier = Modifier.width(8.dp))
Spacer(modifier = Modifier.height(16.dp))
AsyncImage(model = R.drawable.sv_copy,
contentDescription = "copy",
modifier = Modifier.size(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.height(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(
text = uiState.value.orderInfo?.address ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium
)
}
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(text = uiState.value.orderInfo?.address ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
}
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(
text = uiState.value.orderInfo?.distAddress ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium
)
}
}
}
Spacer(modifier = Modifier.width(8.dp))
Spacer(modifier = Modifier.height(16.dp))
Text(text = uiState.value.orderInfo?.distAddress ?: "",
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Medium)
}
}
}
// 发车按钮
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))
Spacer(modifier = Modifier.height(16.dp))
}
}
},
sheetPeekHeight = 180.dp,
sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp),
sheetContainerColor = Color.White,
sheetDragHandle = null,
sheetSwipeEnabled = true
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
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
// 发车按钮
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 = "发车",
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Medium)
}
// 修改标记点点击事件
setOnMarkerClickListener { marker ->
marker.showInfoWindow()
// 800ms后隐藏信息窗口
Handler(Looper.getMainLooper()).postDelayed({
marker.hideInfoWindow()
}, 800)
true
}
}
}
},
update = {
// 清除旧标记和路线
mapView.map.clear()
Spacer(modifier = Modifier.height(16.dp))
}
}
},
sheetPeekHeight = 180.dp,
sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp),
sheetContainerColor = Color.White,
sheetDragHandle = null,
sheetSwipeEnabled = true) { paddingValues ->
Box(modifier = Modifier
.fillMaxSize()
.padding(paddingValues)) {
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
// 先绘制路线
uiState.value.routePoints?.let { points ->
mapView.map.addPolyline(
PolylineOptions()
.addAll(points)
.width(15f)
.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.icon_road_green_arrow))
.zIndex(1f)
)
}
// 修改标记点点击事件
setOnMarkerClickListener { marker ->
marker.showInfoWindow() // 800ms后隐藏信息窗口
Handler(Looper.getMainLooper()).postDelayed({
marker.hideInfoWindow()
}, 800)
true
}
}
}
}, update = { // 清除旧标记和路线
mapView.map.clear()
// 再添加标记点,确保标记点在路线上层
// 添加当前位置标记
if (GlobalData.currentLocation != null) {
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)
)
}
// 先绘制路线
uiState.value.routePoints?.let { points ->
mapView.map.addPolyline(PolylineOptions().addAll(points).width(15f)
.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.icon_road_green_arrow))
.zIndex(1f))
}
// 添加救援地标记
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("救援地点")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.anchor(0.5f, 0.5f)
)
}
// 添加标记点,确保标记点在路线上层
// 添加当前位置标记
if (GlobalData.currentLocation != null) {
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))
}
// 添加目的地标记
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)
)
}
}
// 添加救援地标记
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("救援地点")
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.sv_rescuing_map))
.anchor(0.5f, 0.5f))
}
// 最后调整地图显示范围
// 计算地图显示范围
val bounds = LatLngBounds.Builder().apply {
// 添加当前位置
GlobalData.currentLocation?.let {
include(LatLng(it.latitude, it.longitude))
}
// 添加目的地标记
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))
}
}
// 添加救援地点
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 &&
order.lng != null && order.lng != 0.0
) {
include(LatLng(order.lat!!, order.lng!!))
}
// 最后调整地图显示范围
// 计算地图显示范围
val bounds = LatLngBounds.Builder().apply { // 添加当前位置
GlobalData.currentLocation?.let {
include(LatLng(it.latitude, it.longitude))
}
// 添加目的地
if (order.distLat != null && order.distLat != 0.0 &&
order.distLng != null && order.distLng != 0.0
) {
include(LatLng(order.distLat!!, order.distLng!!))
}
}
}.build()
// 添加救援地点
uiState.value.orderInfo?.let { order ->
if (order.lat != null && order.lat != 0.0 && order.lng != null && order.lng != 0.0) {
include(LatLng(order.lat !!, order.lng !!))
}
// 调整地图显示范围,确保所有点都可见
try {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngBounds(bounds, 100)
)
} catch (e: Exception) {
// 如果计算边界失败,则使用默认缩放级别
GlobalData.currentLocation?.let {
mapView.map.animateCamera(
CameraUpdateFactory.newLatLngZoom(
LatLng(it.latitude, it.longitude),
15f
)
)
}
}
}
)
}
}
// 添加目的地
if (order.distLat != null && order.distLat != 0.0 && order.distLng != null && order.distLng != 0.0) {
include(LatLng(order.distLat !!, order.distLng !!))
}
}
}.build()
// 调整地图显示范围,确保所有点都可见
try {
mapView.map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100))
} catch (e : Exception) { // 如果计算边界失败,则使用默认缩放级别
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.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>