Files
zd_servicing/servicing/src/main/java/com/za/offline/OfflineManager.kt
songzhiling 91305ab9d1 feat: 初始化项目结构和基本功能
- 创建项目根目录和主要子模块
- 添加基本的 Activity 和布局文件
- 实现简单的导航和电话拨打功能
- 添加相机和图像处理相关代码
- 创建网络请求和数据加密工具类
- 设置 AndroidManifest 文件和权限
2025-04-11 11:52:07 +08:00

514 lines
20 KiB
Kotlin

package com.za.offline
import android.content.Intent
import android.provider.Settings
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import com.alibaba.fastjson.JSONObject
import com.amap.api.services.core.LatLonPoint
import com.amap.api.services.geocoder.GeocodeResult
import com.amap.api.services.geocoder.GeocodeSearch
import com.amap.api.services.geocoder.GeocodeSearch.OnGeocodeSearchListener
import com.amap.api.services.geocoder.RegeocodeQuery
import com.amap.api.services.geocoder.RegeocodeResult
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.ToastUtils
import com.za.bean.request.SaveEleOrderRequest
import com.za.bean.request.TaskFinishRequest
import com.za.bean.request.TaskFinishResponse
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.room.RoomHelper
import com.za.water_marker.PhotoMarkerManager
import com.za.water_marker.bean.PhotoMarkerInfo
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import java.io.File
interface IOfflineListener {
fun start()
fun stop()
fun uploadSuccess()
fun uploadFailure(msg : String?)
fun currentTask(offlineUpdateTaskBean : OfflineUpdateTaskBean)
}
object OfflineManager {
private var isUploading = false
private var uploadDispose : Disposable? = null
private var offlineListener : IOfflineListener? = null
private var currentTaskId : Int? = null
fun addOfflineListener(offlineListener : IOfflineListener) {
this.offlineListener = offlineListener
}
fun start(taskId : Int?) {
if (checkOfflineIsComplete(taskId)) {
return
}
if (currentTaskId != null && taskId == currentTaskId && isUploading) {
return
}
if (! Settings.canDrawOverlays(GlobalData.application)) {
openSystemAlertPermissionDialog()
return
}
stop()
OfflineService.addListener()
offlineListener?.start()
uploadDispose = Observable.interval(0, 5, java.util.concurrent.TimeUnit.SECONDS)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe {
if (! isUploading) {
val list =
RoomHelper.db?.offlineTaskDao()?.getOfflineTaskFromTaskId(taskId ?: 0)
if (list.isNullOrEmpty()) {
LogUtil.print("离线任务", "离线任务为空!!")
stop()
ToastUtils.showLong("离线任务上传完成")
return@subscribe
}
doUpload(list[0])
}
}
}
private fun stop() {
uploadDispose?.dispose()
uploadDispose = null
isUploading = false
currentTaskId = null
offlineListener?.stop()
offlineListener = null
}
private fun checkOfflineIsComplete(taskId : Int?) : Boolean {
val list = RoomHelper.db?.offlineTaskDao()?.getOfflineTaskFromTaskId(taskId ?: 0)
LogUtil.print("离线任务列表", "${list.toJson()}")
return list.isNullOrEmpty()
}
private fun doUpload(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
offlineListener?.currentTask(offlineUpdateTaskBean)
currentTaskId = offlineUpdateTaskBean.taskId
when (offlineUpdateTaskBean.offlineType) {
1 -> uploadTask(offlineUpdateTaskBean)
2 -> uploadOrderImage(offlineUpdateTaskBean)
3 -> uploadEleWorkOrder(offlineUpdateTaskBean)
4 -> uploadEleDamageImage(offlineUpdateTaskBean)
5 -> uploadCustomerSignImage(offlineUpdateTaskBean)
6 -> uploadAcceptPeopleSignImage(offlineUpdateTaskBean)
7 -> uploadServicePeopleSignImage(offlineUpdateTaskBean)
8 -> taskFinish(offlineUpdateTaskBean)
}
}
private fun uploadTask(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
isUploading = true
val updateTaskRequest = UpdateTaskRequest(type = offlineUpdateTaskBean.type,
taskId = offlineUpdateTaskBean.taskId,
userId = offlineUpdateTaskBean.userId,
vehicleId = offlineUpdateTaskBean.vehicleId,
currentState = offlineUpdateTaskBean.currentState,
operateTime = offlineUpdateTaskBean.operateTime,
lat = offlineUpdateTaskBean.updateTaskLat,
lng = offlineUpdateTaskBean.updateTaskLng,
address = offlineUpdateTaskBean.updateTaskAddress,
offlineMode = offlineUpdateTaskBean.offlineMode,
flowType = offlineUpdateTaskBean.flowType,
success = offlineUpdateTaskBean.success,
templatePhotoInfoList = offlineUpdateTaskBean.templatePhotoInfoList)
RetrofitHelper.getDefaultService().updateTask(updateTaskRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<UpdateTaskBean>() {
override fun doSuccess(it : UpdateTaskBean?) {
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务", "success=$it request=${updateTaskRequest.toJson()}")
}
override fun doFailure(code : Int, msg : String?) {
isUploading = false
offlineListener?.uploadFailure(msg)
LogUtil.print("离线任务",
"uploadTask failed==${offlineUpdateTaskBean.toJson()}")
}
})
}
private fun uploadOrderImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
isUploading = true
var file = File(offlineUpdateTaskBean.photoLocalWaterMarkerPath ?: "")
if (! file.exists()) {
offlineListener?.uploadFailure("图片未找到!")
LogUtil.print("离线任务 uploadOrderImage",
"uploadTask failed==${offlineUpdateTaskBean.toJson()}")
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
return
}
if (offlineUpdateTaskBean.imageAddress.isNullOrBlank() && (offlineUpdateTaskBean.imageLat != null && offlineUpdateTaskBean.imageLat != 0f && offlineUpdateTaskBean.imageLng != null && offlineUpdateTaskBean.imageLng != 0f)) {
geoCoder(offlineUpdateTaskBean.imageLat.toDouble(),
offlineUpdateTaskBean.imageLng.toDouble(),
success = { it ->
val photoMarkerInfo = PhotoMarkerInfo(offlineUpdateTaskBean.needWater,
needShowPhoneBrand = offlineUpdateTaskBean.needPhoneBrand,
path = offlineUpdateTaskBean.photoLocalWaterMarkerPath,
from = "(离线)",
lat = offlineUpdateTaskBean.imageLat.toDouble(),
lng = offlineUpdateTaskBean.imageLng.toDouble(),
address = it,
time = offlineUpdateTaskBean.time,
driverName = GlobalData.driverInfo?.userName,
taskCode = offlineUpdateTaskBean.taskCode)
val offlineTemp = offlineUpdateTaskBean.copy(imageAddress = it)
file = File(PhotoMarkerManager.addPhotoMarker(ActivityUtils.getTopActivity(),
photoMarkerInfo))
CommonMethod.uploadImage(file = file, success = {
val item = RoomHelper.db?.offlineTaskDao()
?.getRecentOfflineTask(offlineTemp.taskId ?: 0)?.get(0)
if (item == null) {
offlineListener?.uploadFailure(it)
isUploading = false
return@uploadImage
}
LogUtil.print("离线任务 getRecentOfflineTask", "success=${item.toJson()}")
val list = item.templatePhotoInfoList?.toMutableList()
val jsonObject = JSONObject()
jsonObject["realTakePhotoTime"] = offlineTemp.realTakePhotoTime ?: ""
jsonObject["photoSource"] = offlineTemp.photoSource
jsonObject["path"] = it
jsonObject["time"] = offlineTemp.time
jsonObject["lat"] = offlineTemp.imageLat
jsonObject["lng"] = offlineTemp.imageLng
jsonObject["address"] = offlineTemp.imageAddress
list?.set(offlineTemp.imageIndex ?: 0, jsonObject.toJSONString())
val temp = item.copy(templatePhotoInfoList = list?.toList())
RoomHelper.db?.offlineTaskDao()?.update(temp)
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineTemp.primaryId ?: 0)
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务 逆地理 uploadOrderImage",
"success=$it request=${offlineUpdateTaskBean.toJson()}")
}, failed = {
RoomHelper.db?.offlineTaskDao()?.update(offlineTemp)
offlineListener?.uploadFailure(it)
isUploading = false
})
},
failed = {
offlineListener?.uploadFailure(it)
isUploading = false
})
} else {
CommonMethod.uploadImage(file = file, success = {
val item = RoomHelper.db?.offlineTaskDao()
?.getRecentOfflineTask(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
if (item == null) {
offlineListener?.uploadFailure(it)
isUploading = false
return@uploadImage
}
LogUtil.print("离线任务 getRecentOfflineTask", "success=${item.toJson()}")
val list = item.templatePhotoInfoList?.toMutableList()
val jsonObject = JSONObject()
jsonObject["realTakePhotoTime"] = offlineUpdateTaskBean.realTakePhotoTime ?: ""
jsonObject["photoSource"] = offlineUpdateTaskBean.photoSource
jsonObject["path"] = it
jsonObject["time"] = offlineUpdateTaskBean.time
jsonObject["lat"] = offlineUpdateTaskBean.imageLat
jsonObject["lng"] = offlineUpdateTaskBean.imageLng
jsonObject["address"] = offlineUpdateTaskBean.imageAddress
list?.set(offlineUpdateTaskBean.imageIndex ?: 0, jsonObject.toJSONString())
val temp = item.copy(templatePhotoInfoList = list?.toList())
RoomHelper.db?.offlineTaskDao()?.update(temp)
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务 uploadOrderImage",
"success=$it request=${offlineUpdateTaskBean.toJson()}")
}, failed = {
offlineListener?.uploadFailure(it)
isUploading = false
})
}
}
private fun uploadEleDamageImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
isUploading = true
val file = File(offlineUpdateTaskBean.imageLocalPath ?: "")
if (! file.exists()) {
offlineListener?.uploadFailure("图片未找到!")
LogUtil.print("离线任务 uploadEleDamageImage",
"failed==${offlineUpdateTaskBean.toJson()}")
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
return
}
CommonMethod.uploadImage(file = file, success = {
val item = RoomHelper.db?.offlineTaskDao()
?.getRecentOfflineEle(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
if (item == null) {
offlineListener?.uploadFailure(it)
isUploading = false
return@uploadImage
}
LogUtil.print("离线任务 getRecentOfflineEle", "success=${item.toJson()}")
val list = item.damageFileList?.toMutableList()
list?.set(offlineUpdateTaskBean.imageIndex ?: 0, it)
val temp = item.copy(templatePhotoInfoList = list?.toList())
RoomHelper.db?.offlineTaskDao()?.update(temp)
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务 uploadEleDamageImage",
"success=$it request=${offlineUpdateTaskBean.toJson()}")
}, failed = {
offlineListener?.uploadFailure(it)
isUploading = false
})
}
private fun uploadEleWorkOrder(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
val saveEleOrderRequest = SaveEleOrderRequest(state = offlineUpdateTaskBean.eleState,
userOrderId = offlineUpdateTaskBean.userOrderId,
taskOrderId = offlineUpdateTaskBean.taskId,
damageFileList = offlineUpdateTaskBean.damageFileList,
hasDamage = offlineUpdateTaskBean.hasDamage,
hasSuccess = offlineUpdateTaskBean.hasSuccess,
customerSignPath = offlineUpdateTaskBean.customerSignPath,
recipientSignPath = offlineUpdateTaskBean.recipientSignPath,
waitstaffSignPath = offlineUpdateTaskBean.waitstaffSignPath,
offlineMode = 1,
userOrderCode = offlineUpdateTaskBean.userOrderCode,
lat = offlineUpdateTaskBean.eleLat,
lng = offlineUpdateTaskBean.eleLng,
tyreNumber = offlineUpdateTaskBean.tyreNumber,
isFinish = offlineUpdateTaskBean.isFinish)
LogUtil.print("离线数据 eleSign_check request", saveEleOrderRequest.toJson() ?: "")
RetrofitHelper.getDefaultService().saveElectronOrder(saveEleOrderRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : String?) {
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务 uploadEleWorkOrder",
"success=$it request=${offlineUpdateTaskBean.toJson()}")
}
override fun doFailure(code : Int, msg : String?) {
offlineListener?.uploadFailure(msg)
isUploading = false
}
})
}
private fun uploadCustomerSignImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
isUploading = true
val file = File(offlineUpdateTaskBean.imageLocalPath ?: "")
if (! file.exists()) {
offlineListener?.uploadFailure("图片未找到!")
LogUtil.print("离线任务 uploadCustomerSignImage",
"failed==${offlineUpdateTaskBean.toJson()}")
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
return
}
CommonMethod.uploadImage(file = file, success = { it ->
val item = RoomHelper.db?.offlineTaskDao()
?.getRecentOfflineEle(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
if (item == null) {
offlineListener?.uploadFailure(it)
isUploading = false
return@uploadImage
}
LogUtil.print("离线任务 uploadCustomerSignImage getRecentOfflineEle",
"success=${item.toJson()}")
RoomHelper.db?.offlineTaskDao()?.update(item.copy(customerSignPath = it))
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(offlineUpdateTaskBean.taskId ?: 0)
?.copy(serverCustomSignPath = it)?.let {
RoomHelper.db?.eleWorkOrderDao()?.update(it)
}
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务 uploadCustomerSignImage",
"success=$it request=${offlineUpdateTaskBean.toJson()}")
}, failed = {
offlineListener?.uploadFailure(it)
isUploading = false
})
}
private fun uploadAcceptPeopleSignImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
isUploading = true
val file = File(offlineUpdateTaskBean.imageLocalPath ?: "")
if (! file.exists()) {
offlineListener?.uploadFailure("图片未找到!")
LogUtil.print("离线任务 uploadCustomerSignImage",
"failed==${offlineUpdateTaskBean.toJson()}")
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
return
}
CommonMethod.uploadImage(file = file, success = { it ->
val item = RoomHelper.db?.offlineTaskDao()
?.getRecentOfflineEle(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
if (item == null) {
offlineListener?.uploadFailure(it)
isUploading = false
return@uploadImage
}
LogUtil.print("离线任务 uploadAcceptPeopleSignImage getRecentOfflineEle",
"success=${item.toJson()}")
RoomHelper.db?.offlineTaskDao()?.update(item.copy(recipientSignPath = it))
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(offlineUpdateTaskBean.taskId ?: 0)
?.copy(serverAcceptCarSignPath = it)?.let {
RoomHelper.db?.eleWorkOrderDao()?.update(it)
}
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务 uploadAcceptPeopleSignImage",
"success=$it request=${offlineUpdateTaskBean.toJson()}")
}, failed = {
offlineListener?.uploadFailure(it)
isUploading = false
})
}
private fun uploadServicePeopleSignImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
isUploading = true
val file = File(offlineUpdateTaskBean.imageLocalPath ?: "")
if (! file.exists()) {
offlineListener?.uploadFailure("图片未找到!")
LogUtil.print("离线任务 uploadCustomerSignImage",
"failed==${offlineUpdateTaskBean.toJson()}")
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
return
}
CommonMethod.uploadImage(file = file, success = { it ->
val item = RoomHelper.db?.offlineTaskDao()
?.getRecentOfflineEle(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
if (item == null) {
offlineListener?.uploadFailure(it)
isUploading = false
return@uploadImage
}
LogUtil.print("离线任务 uploadCustomerSignImage getRecentOfflineEle",
"success=${item.toJson()}")
RoomHelper.db?.offlineTaskDao()?.update(item.copy(waitstaffSignPath = it))
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(offlineUpdateTaskBean.taskId ?: 0)
?.copy(serverServicePeopleSignPath = it)?.let {
RoomHelper.db?.eleWorkOrderDao()?.update(it)
}
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务 uploadCustomerSignImage",
"success=$it request=${offlineUpdateTaskBean.toJson()}")
}, failed = {
offlineListener?.uploadFailure(it)
isUploading = false
})
}
private fun taskFinish(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
isUploading = true
val taskFinishRequest = TaskFinishRequest(
operateTime = offlineUpdateTaskBean.operateTime?.toLong(),
lat = offlineUpdateTaskBean.updateTaskLat,
userOrderId = offlineUpdateTaskBean.userOrderId,
lng = offlineUpdateTaskBean.updateTaskLng,
)
RetrofitHelper.getDefaultService().taskFinish(taskFinishRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<TaskFinishResponse>() {
override fun doSuccess(it : TaskFinishResponse?) {
RoomHelper.db?.offlineTaskDao()
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
isUploading = false
offlineListener?.uploadSuccess()
LogUtil.print("离线任务", "success=$it request=${taskFinishRequest.toJson()}")
}
override fun doFailure(code : Int, msg : String?) {
isUploading = false
offlineListener?.uploadFailure(msg)
LogUtil.print("离线任务",
"uploadTask failed==${offlineUpdateTaskBean.toJson()}")
}
})
}
}
//打开系统悬浮窗权限
private fun openSystemAlertPermissionDialog() {
val context = ActivityUtils.getTopActivity()
val alertdialog = AlertDialog.Builder(ActivityUtils.getTopActivity()).setTitle("提示")
.setMessage("当前应用缺少【悬浮窗】必要权限。\n\n请点击\"设置\"-\"权限\"-打开所需权限。")
.setCancelable(false).setPositiveButton("设置") { _, _ ->
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
"package:${context.packageName}".toUri())
intent.data = ("package:" + ActivityUtils.getTopActivity().packageName).toUri()
ActivityUtils.getTopActivity().startActivity(intent)
}
alertdialog.show()
}
//逆地址编码
private fun geoCoder(lat : Double,
lng : Double,
success : (String) -> Unit,
failed : (String) -> Unit = {}) {
val geocoderSearch = GeocodeSearch(ActivityUtils.getTopActivity().application)
val query = RegeocodeQuery(LatLonPoint(lat, lng), 200f, GeocodeSearch.AMAP)
geocoderSearch.getFromLocationAsyn(query)
geocoderSearch.setOnGeocodeSearchListener(object : OnGeocodeSearchListener {
override fun onRegeocodeSearched(regeocodeResult : RegeocodeResult, i : Int) {
if (i == 1000) {
success(regeocodeResult.regeocodeAddress.formatAddress)
LogUtil.print("离线任务 singleLocation 逆地理编码",
regeocodeResult.regeocodeAddress.formatAddress)
} else {
failed("地址获取失败")
LogUtil.print("singleLocation 逆地理失败 ", regeocodeResult.toString())
}
}
override fun onGeocodeSearched(geocodeResult : GeocodeResult, i : Int) {}
})
}