build(servicing): 更新构建配置并升级版本号

-禁用 release 和 debug 构建类型的代码压缩
- 添加 publishNonDefault 配置项
-将版本号从 1.0.1.9.9.126 升级到 1.0.1.9.9.127
This commit is contained in:
songzhiling
2025-07-11 17:48:46 +08:00
parent 2cb2425e8f
commit c1c070dfac
81 changed files with 2272 additions and 673 deletions

View File

@ -31,7 +31,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
publishNonDefault true
// publishNonDefault true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
@ -64,7 +64,7 @@ publishing {
release(MavenPublication) {
groupId = 'io.github.szl9'
artifactId = 'zd_servicing'
version = "1.0.1.9.9.127"
version = "1.0.1.9.9.138"
pom {
packaging = "aar"

View File

@ -1,3 +1,5 @@
-dontwarn java.lang.invoke.StringConcatFactory
# 保留行号用于调试
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
@ -8,19 +10,42 @@
-keepattributes Exceptions
-keepattributes InnerClasses
-dontwarn com.github.luben.zstd.BufferPool
-dontwarn com.github.luben.zstd.ZstdInputStream
-dontwarn com.github.luben.zstd.ZstdOutputStream
-dontwarn org.brotli.dec.BrotliInputStream
-dontwarn org.objectweb.asm.AnnotationVisitor
-dontwarn org.objectweb.asm.Attribute
-dontwarn org.objectweb.asm.ClassReader
-dontwarn org.objectweb.asm.ClassVisitor
-dontwarn org.objectweb.asm.FieldVisitor
-dontwarn org.objectweb.asm.Label
-dontwarn org.objectweb.asm.MethodVisitor
-dontwarn org.objectweb.asm.Type
# 保留R文件
-keepclassmembers class **.R$* {
public static <fields>;
}
# 保留servicing模块中的所有model类
-keep class com.za.servicing.model.** { *; }
-keep class com.za.bean.** { *; }
-keep class com.za.bean.db.** { *; }
-keep class com.za.bean.request.** { *; }
# 保留servicing模块中的所有接口
-keep interface com.za.servicing.** { *; }
-keep interface com.za.net.** { *; }
# 保留servicing模块中的所有枚举
-keepclassmembers enum com.za.servicing.** { *; }
-keepclassmembers enum com.za.** { *; }
# 保留Room数据库相关类
-keep class com.za.room.** { *; }
-keep class com.za.room.db.** { *; }
-keep class * extends androidx.room.RoomDatabase
-keep @androidx.room.Entity class *
-keep @androidx.room.Dao interface *
# 保留Parcelable实现类
-keep class * implements android.os.Parcelable {
@ -58,6 +83,10 @@
void set*(***); *** get*();
}
# CameraX相关
-keep class androidx.camera.** { *; }
-dontwarn androidx.camera.**
# Coil图片加载库
-keep class coil.** { *; }
-dontwarn coil.**
@ -75,7 +104,6 @@
# JPush相关
-dontoptimize
-dontpreverify
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-dontwarn cn.jcore.**
@ -105,13 +133,12 @@
long consumerIndex;
}
#blankJ
# BlankJ工具库
-dontwarn com.blankj.utilcode.**
-keep class com.blankj.utilcode.** { *; }
-keepclassmembers class * {
@com.blankj.utilcode.util.BusUtils$Bus <methods>;
}
-keep public class * extends com.blankj.utilcode.util.ApiUtils$BaseApi
-keep,allowobfuscation @interface com.blankj.utilcode.util.ApiUtils$Api
-keep @com.blankj.utilcode.util.ApiUtils$Api class *
@ -122,41 +149,27 @@
-keep public class * {
public protected *;
}
# Preserve all .class method names.
# 保留所有类的方法名
-keepclassmembernames class * {
java.lang.Class class$(java.lang.String);
java.lang.Class class$(java.lang.String, boolean);
}
# Preserve all native method names and the names of their classes.
-keepclasseswithmembernames class * {
native <methods>;
}
# Preserve the special static methods that are required in all enumeration
# classes.
# 保留枚举类中的特殊静态方法
-keepclassmembers class * extends java.lang.Enum {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# MQTT相关
-keep class org.eclipse.paho.** { *; }
-dontwarn org.eclipse.paho.**
# Gson
-keep class com.google.gson.** { *; }
-keepattributes Signature
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
@ -192,7 +205,7 @@
public ** component*();
public ** copy(...);
}
#
# 保留Kotlin数据类的toString/hashCode/equals方法
-keepclassmembers class ** {
public java.lang.String toString();
@ -200,4 +213,45 @@
public boolean equals(java.lang.Object);
}
# 保留Compose相关
-keep class androidx.compose.** { *; }
-dontwarn androidx.compose.**
# 保留自定义签名相关类
-keep class com.za.signature.** { *; }
# 保留Face Detection相关
-keep class com.google.mlkit.** { *; }
-dontwarn com.google.mlkit.**
# 保留ZXing相关
-keep class com.google.zxing.** { *; }
-dontwarn com.google.zxing.**
# 保留Glide相关
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class * extends com.bumptech.glide.module.AppGlideModule {
<init>(...);
}
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
*** rewind();
}
# 保留FastJSON相关
-keep class com.alibaba.fastjson.** { *; }
-dontwarn com.alibaba.fastjson.**
# 保留Bugly相关
-keep class com.tencent.bugly.** { *; }
-dontwarn com.tencent.bugly.**
#
# 保留自定义UI组件
-keep class com.za.ui.** { *; }
-keep class com.za.base.** { *; }
# 保留Java 8 Lambda表达式
-dontwarn java.lang.invoke.StringConcatFactory

View File

@ -61,6 +61,7 @@ object Const {
const val RE_DISPATCH = "reDispatch"
const val MODIFY = "modify"
const val REVOKE = "revoke"
const val REPORT_HANDLE = "report_handle"
const val GO_MAIN_PAGE = "goMainPage"
}
}

View File

@ -40,9 +40,7 @@ abstract class IServicingVm<T, U> : BaseVm<T, U>() {
getCurrentOrder()?.getTaskNode() ?: 0,
getCurrentOrder()?.userOrderId ?: 0)
success(data)
}, failed = {
failure(it)
})
}, failed = {})
} else {
success(photoTemplateList)
}

View File

@ -39,6 +39,12 @@ open class PushMessageActivity : AppCompatActivity() {
context = this@PushMessageActivity)
}
override fun reportHandle(jpushBean : JpushBean) {
sendMessageToMainProcess(type = Const.PushMessageType.REPORT_HANDLE,
message = Gson().toJson(jpushBean),
context = this@PushMessageActivity)
}
override fun newOrderMsg(jpushBean : JpushBean) {
sendMessageToMainProcess(type = Const.PushMessageType.NEW_ORDER,
message = Gson().toJson(jpushBean),

View File

@ -15,7 +15,10 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.Density
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
@ -61,12 +64,17 @@ fun DealerTheme(darkTheme : Boolean = isSystemInDarkTheme(), content : @Composab
}
}
}
val fontScale = LocalDensity.current.fontScale
val displayMetrics = LocalContext.current.resources.displayMetrics
val widthPixels = displayMetrics.widthPixels
MaterialTheme(colorScheme = colorScheme) {
CompositionLocalProvider(LocalRippleConfiguration provides RippleConfiguration(rippleAlpha = RippleAlpha(
0f,
0f,
0f,
0f))) {
0f)),
LocalDensity provides Density(fontScale = 1f, density = widthPixels / 360f)) {
ProvideTextStyle(value = MaterialTheme.typography.bodyLarge, content = content)
}
}

View File

@ -174,9 +174,6 @@ data class HistoryPhotoTemplateItem(
//获取拍照时间
fun getPhotoTakeTime(taskState : String, historyTaskBean : HistoryTaskBean?) : String? {
if (! takePhotoTime.isNullOrBlank()) {
return takePhotoTime
}
return when (taskState) {
"13001", "15001" -> historyTaskBean?.arriveTime
"17001" -> {
@ -197,9 +194,6 @@ data class HistoryPhotoTemplateItem(
}
fun getPhotoLat(taskState : String, historyTaskBean : HistoryTaskBean?) : Double? {
if (! lat.isNullOrBlank()) {
return lat.toDouble()
}
return when (taskState) {
"13001", "15001" -> historyTaskBean?.lat
"17001", "18001" -> {
@ -216,9 +210,6 @@ data class HistoryPhotoTemplateItem(
}
fun getPhotoLng(taskState : String, historyTaskBean : HistoryTaskBean?) : Double? {
if (! lon.isNullOrBlank()) {
return lon.toDouble()
}
return when (taskState) {
"13001", "15001" -> historyTaskBean?.lng
"17001", "18001" -> {
@ -235,9 +226,6 @@ data class HistoryPhotoTemplateItem(
}
fun getPhotoAddress(taskState : String, historyTaskBean : HistoryTaskBean?) : String? {
if (! takeAddress?.replace("[", "")?.replace("]", "").isNullOrBlank()) {
return takeAddress
}
return when (taskState) {
"13001", "15001" -> historyTaskBean?.address
"17001", "18001" -> {

View File

@ -31,4 +31,14 @@ data class TaskNotesBean(val taskNotes : String? = null, //救援要求
val customerNotes : String? = null, //客户提醒
val otherNotes : String? = null, //特殊提醒
val modelVinNo : String? = null, //车型
val contract : String? = null) //车型
val contract : String? = null) //车型
data class UnifiedOCRWithCompressRequest(
val ocrType : Int? = null, // 4为车架号识别 5为车牌号识别
val imageUrl : String? = null, // 图片地址
val cardSide : String? = null, // FRONT:正面 BACK:反面 默认FRONT
)
data class SaveSignatureRequest(val signatureUrl : String? = null, val driverId : Int? = null)

View File

@ -22,6 +22,7 @@ data class PhotoTemplateInfo(
val numbering : String? = null, // 图片编号
val recognizeType : Int? = null, //orc 识别类型 0 无 1 车牌号 2 车架号
//以下属性非后台返回属性
val serviceTypeName : String? = null, //服务类型名称
val userOrderId : Int? = null,
val taskCode : String? = null,
val taskId : Int? = null,

View File

@ -7,6 +7,7 @@ import com.tencent.mmkv.MMKV
import com.za.base.AppConfig
import com.za.base.Const
import com.za.bean.db.order.OrderInfo
import com.za.common.log.LogUtil
import com.za.room.RoomHelper
import com.za.room.db.user.DriverInfoBean
import com.za.service.location.ZdLocationManager
@ -49,14 +50,26 @@ object GlobalData : GlobalLocalData() {
var driverInfoBean : DriverInfoBean? = null
get() {
if (field == null) {
field = localDriverInfoBean
return try {
val driverInfo = MMKV.defaultMMKV()
.decodeParcelable("driverInfoBean", DriverInfoBean::class.java)
field = driverInfo
field
} catch (e : Exception) {
LogUtil.print("local_driverInfoBean", "获取司机信息失败: ${e.message}")
null
}
return field
}
set(value) {
localDriverInfoBean = value
field = value
try {
MMKV.defaultMMKV().encode("driverInfoBean", value)
if (value != null) {
lastLoginBean = value
}
field = value
} catch (e : Exception) {
LogUtil.print("set local_driverInfoBean", "保存司机信息失败: ${e.message}")
}
}
@ -139,6 +152,45 @@ object GlobalData : GlobalLocalData() {
mmkv.encode("isShowKeepAlive", value)
}
var lastUploadLogTime : Long
get() {
return mmkv.decodeLong("lastUploadLogTime", 0)
}
set(value) {
mmkv.encode("lastUploadLogTime", value)
}
var vehicleId : Int? = null
get() {
field = mmkv.decodeInt("vehicleId")
return field
}
set(value) {
mmkv.encode("vehicleId", value ?: 0)
field = value
}
var driverId : Int? = null
get() {
field = mmkv.decodeInt("driverId")
return field
}
set(value) {
mmkv.encode("driverId", value ?: 0)
field = value
}
var deviceId : String? = null
get() {
field = mmkv.decodeString("deviceId")
return field
}
set(value) {
mmkv.encode("deviceId", value)
field = value
}
fun clearUserCache() {
token = null
aesKey = null
@ -147,6 +199,10 @@ object GlobalData : GlobalLocalData() {
loginTime = null
isLoginRecognition = null
isHasShowKeepAlive = false
lastUploadLogTime = 0
deviceId = null
vehicleId = null
driverId = null
if (AppConfig.isRelease) {
networkEnv = if (AppConfig.isRelease) {

View File

@ -2,69 +2,72 @@ package com.za.common.log
import android.app.Application
import android.content.Context
import android.os.Build
import android.util.Log
import androidx.work.Configuration
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.blankj.utilcode.constant.MemoryConstants
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.DeviceUtils
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.TimeUtils
import com.za.common.GlobalData
import com.za.common.util.AppFileManager
import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream
import java.io.BufferedWriter
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.FileWriter
import java.io.IOException
import java.io.PrintWriter
import java.io.StringWriter
import java.nio.file.Files
import java.nio.file.Paths
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.concurrent.thread
object LogUtil {
private var context : Application? = null
private var logDestinationPath : String? = null
private var orderLogDirPath : String? = null
private var executors : ExecutorService? = null
private var normalLogDirPath : String? = null
private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val logBuffer = StringBuilder()
private val isWriting = AtomicBoolean(false)
//日志上报时间间隔
private const val uploadTimeInterval = (30 * 60 * 1000).toLong()
fun init(context : Application) {
this.context = context
logDestinationPath = AppFileManager.getLogPath(context).also { path ->
createDirectoryIfNotExists(path)
orderLogDirPath =
"$path${File.separator}order_log".also { createDirectoryIfNotExists(it) }
normalLogDirPath =
"$path${File.separator}normal_log".also { createDirectoryIfNotExists(it) }
executors = Executors.newSingleThreadExecutor()
val logDestinationPath : String = context.filesDir.absolutePath + File.separator + "Log"
val logDesFile = File(logDestinationPath)
if (! logDesFile.exists()) {
logDesFile.mkdir()
}
val orderLogDirPath = logDestinationPath + File.separator + "order_log"
val orderLogFile = File(orderLogDirPath)
if (! orderLogFile.exists()) {
orderLogFile.mkdir()
}
normalLogDirPath = logDestinationPath + File.separator + "normal_log"
val normalLogFile = File(normalLogDirPath)
LogUtils.getConfig().setDir(normalLogDirPath)
LogUtils.getConfig().setBorderSwitch(false)
LogUtils.getConfig().setLog2FileSwitch(true)
LogUtils.getConfig().setStackDeep(1)
LogUtils.getConfig().setLogHeadSwitch(false)
if (! normalLogFile.exists()) {
normalLogFile.mkdir()
}
initializeWorkManager(context)
}
private fun createDirectoryIfNotExists(path : String) {
File(path).apply { if (! exists()) mkdir() }
}
private fun initializeWorkManager(context : Application) {
if (! WorkManager.isInitialized()) {
WorkManager.initialize(context,
@ -73,183 +76,157 @@ object LogUtil {
WorkManager.getInstance(context).apply {
cancelAllWorkByTag("logWorkRequest")
enqueue(PeriodicWorkRequest.Builder(LogTask::class.java, 20, TimeUnit.MINUTES)
enqueue(PeriodicWorkRequest.Builder(LogTask::class.java, 80, TimeUnit.MINUTES)
.addTag("logWorkRequest").build())
}
}
fun print(tag : String, content : String) {
val time = getCurrentTime()
val logEntry = "$time---$tag---$content\n"
Log.e("normal", "$tag---$content")
synchronized(logBuffer) {
logBuffer.append(logEntry)
fun print(tag : String?, throwable : Throwable?) {
if (throwable == null) {
return
}
if (logBuffer.length > 4096) {
coroutineScope.launch {
flushBuffer()
}
}
}
fun print(tag : String, throwable : Throwable) {
val content = StringWriter()
val printWriter = PrintWriter(content)
throwable.printStackTrace(printWriter)
print(tag, content.toString())
}
private suspend fun flushBuffer() = withContext(Dispatchers.IO) {
if (! isWriting.compareAndSet(false, true)) return@withContext
val logContent : String
synchronized(logBuffer) {
if (logBuffer.isEmpty()) {
isWriting.set(false)
return@withContext
}
logContent = logBuffer.toString()
logBuffer.clear()
}
try {
val fileName = "normal_log.txt"
val logFile = File("$normalLogDirPath${File.separator}$fileName")
logFile.parentFile?.mkdirs()
if (! logFile.exists()) {
logFile.createNewFile()
addLogHead(logFile, getCurrentTime())
val stringWriter = StringWriter()
val printWriter = PrintWriter(stringWriter)
throwable.printStackTrace(printWriter)
if (System.currentTimeMillis() - GlobalData.lastUploadLogTime > uploadTimeInterval) {
updateNormalLog()
}
BufferedWriter(FileWriter(logFile, true)).use { writer ->
writer.write(logContent)
writer.flush()
}
if (logFile.length() >= 8 * MemoryConstants.MB) {
rotateLogFile(logFile)
}
} catch (e : IOException) {
Log.e("LogUtil", "Error in flushBuffer: ${e.message}")
LogUtils.getConfig().setFilePrefix(this.vehicleName)
LogUtils.e("$tag---$stringWriter")
} catch (e : Exception) {
Log.e("LogUtil", "Error in flushBuffer: ${e.message}")
} finally {
isWriting.set(false)
e.printStackTrace()
}
}
private fun rotateLogFile(file : File) {
if (! file.exists()) return
val newFileName = buildString {
append(AppUtils.getAppVersionCode())
append("_")
append(GlobalData.driverInfoBean?.vehicleName ?: "unknown")
append("_")
append(GlobalData.driverInfoBean?.userName ?: "unknown")
append("_")
append(TimeUtils.getNowString())
append(".txt")
fun print(tag : String?, content : String?) {
if (content == null || content.isEmpty()) {
return
}
if (GlobalData.driverInfoBean == null || GlobalData.driverInfoBean?.vehicleName.isNullOrBlank()) {
Log.e(tag, content)
return
}
val newFile = File("$normalLogDirPath${File.separator}$newFileName")
try {
if (file.renameTo(newFile)) {
compressAndUploadLog(newFile) // 创建新的日志文件
file.createNewFile()
addLogHead(file, getCurrentTime())
} else {
print("LogUtil", "Failed to rename log file")
if (System.currentTimeMillis() - GlobalData.lastUploadLogTime > uploadTimeInterval) {
updateNormalLog()
}
LogUtils.getConfig().setFilePrefix(this.vehicleName)
LogUtils.e("$tag---$content")
} catch (e : Exception) {
print("LogUtil", "Error during log rotation: ${e.message}")
e.printStackTrace()
}
}
private fun compressAndUploadLog(logFile : File) = coroutineScope.launch {
try {
val compressedFile = File("${logFile.absolutePath}.7z")
compress(logFile, compressedFile.absolutePath)
upload(logFile, compressedFile)
} catch (e : Exception) {
print("LogUtil", e.toString())
private val vehicleName : String
get() {
var name : String? = "未知"
if (! GlobalData.driverInfoBean?.vehicleName.isNullOrBlank()) {
try {
name = buildString {
append(GlobalData.driverInfoBean?.vehicleName?.replace("/", "")
?.replace(" ", ""))
append("_")
append(GlobalData.driverInfoBean?.userName ?: "未知")
}
} catch (e : Exception) {
e.printStackTrace()
}
}
return name ?: "未知"
}
}
private fun deleteLog(file : File) {
try {
FileUtils.delete(file.absolutePath)
file.delete()
} catch (e : Exception) {
e.printStackTrace()
}
}
fun updateNormalLog() {
thread {
if (GlobalData.token.isNullOrBlank()) {
return@thread
}
val fileName = "normal_log.txt"
val file = File("$normalLogDirPath${File.separator}$fileName")
val reName =
"${AppUtils.getAppVersionCode()}_${GlobalData.driverInfoBean?.vehicleName}_${GlobalData.driverInfoBean?.userName}_${TimeUtils.getNowString()}.txt"
val reNamePath = "$normalLogDirPath${File.separator}$reName"
file.renameTo(File(reNamePath))
normalLogDirPath?.let { it ->
File(it).listFiles()?.forEach {
if (it.length() / MemoryConstants.MB >= 10) {
deleteLog(it)
return@thread
}
if (it.exists() && ! it.name.contains("normal_log")) {
if (it.name.contains("7z")) {
upload(null, desFile = it)
} else {
val zipNamePath = it.absolutePath + ".7z"
val zipFile = File(zipNamePath)
if (! zipFile.exists()) {
try {
zipFile.createNewFile()
} catch (e : IOException) {
e.printStackTrace()
}
GlobalData.lastUploadLogTime = System.currentTimeMillis()
val files = File(normalLogDirPath).listFiles()
if (files.isNullOrEmpty()) {
return
}
for (file in files) {
try {
if (file.isDirectory()) {
file.delete()
FileUtils.deleteAllInDir(file)
} else if (file.getName().endsWith("7z")) {
uploadFile(null, File(file.absolutePath))
} else {
executors?.submit {
val vehicleName = vehicleName
val zipNamePath =
normalLogDirPath + File.separator + AppUtils.getAppVersionCode() + "_" + vehicleName + "_" + this.currentTime + ".txt.7z"
val zipFile = File(zipNamePath)
if (! zipFile.exists()) {
try {
zipFile.createNewFile()
compress(file, zipNamePath)
} catch (e : IOException) {
e.printStackTrace()
}
compress(it, zipNamePath)
}
}
}
} catch (e : Exception) {
e.printStackTrace()
}
}
}
private fun compress(srcFile : File, desFilePath : String) {
try {
val out = XZCompressorOutputStream(FileOutputStream(desFilePath))
val out = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
XZCompressorOutputStream(Files.newOutputStream(Paths.get(desFilePath)))
} else {
XZCompressorOutputStream(FileOutputStream(desFilePath))
}
addToArchiveCompression(out, srcFile, File(desFilePath))
} catch (e : Exception) {
e.printStackTrace()
}
}
private fun uploadFile(srcFile : File?, desFile : File) {
val requestBody : RequestBody = desFile.asRequestBody("multipart/form-data".toMediaType())
val part = MultipartBody.Part.createFormData("file", desFile.name, requestBody)
val disposable =
LogRetrofitHelper.getDefaultService().uploadLog(part, desFile.name, "rescue-app")
.subscribeOn(Schedulers.io()).subscribe({ it ->
if (it.code == 200) {
deleteLog(desFile);
}
if (srcFile != null) {
deleteLog(srcFile);
}
}, {
if (srcFile != null) {
deleteLog(srcFile);
}
}, {})
}
private fun addToArchiveCompression(sevenZOutputFile : XZCompressorOutputStream,
srcFile : File,
desFile : File) {
if (srcFile.isFile) {
if (srcFile.isFile()) {
var inputStream : FileInputStream? = null
try {
inputStream = FileInputStream(srcFile)
val b = ByteArray(2048)
var count : Int
while (inputStream.read(b).also { count = it } != - 1) {
while ((inputStream.read(b).also { count = it }) != - 1) {
sevenZOutputFile.write(b, 0, count)
}
sevenZOutputFile.close()
inputStream.close()
upload(srcFile, desFile)
uploadFile(srcFile, desFile)
} catch (e : Exception) {
e.printStackTrace()
} finally {
@ -263,42 +240,8 @@ object LogUtil {
}
}
private fun upload(srcFile : File?, desFile : File) {
val requestBody : RequestBody = desFile.asRequestBody("multipart/form-data".toMediaType())
val part = MultipartBody.Part.createFormData("file", desFile.name, requestBody)
val disposable =
LogRetrofitHelper.getDefaultService().uploadLog(part, desFile.name, "rescue-app")
.subscribeOn(Schedulers.io()).subscribe({ it ->
if (it.code == 200) {
deleteLog(desFile)
}
srcFile?.let {
deleteLog(it)
}
}, {}, {})
}
private fun addLogHead(file : File, time : String) {
file.appendBytes("${time}---应用版本---${AppUtils.getAppVersionName()}".toByteArray())
file.appendBytes("\n".toByteArray())
file.appendBytes("$time---系统版本---Android${DeviceUtils.getSDKVersionName()} ${DeviceUtils.getSDKVersionCode()}".toByteArray())
file.appendBytes("\n".toByteArray())
file.appendBytes("$time---ROM---${DeviceUtils.getManufacturer()} ${DeviceUtils.getModel()}".toByteArray())
file.appendBytes("\n".toByteArray())
file.appendBytes("$time---build---${AppUtils.getAppVersionCode()}".toByteArray())
file.appendBytes("\n".toByteArray())
file.appendBytes("$time---APP名称---中道救援-司机端".toByteArray())
file.appendBytes("\n".toByteArray())
file.appendBytes("$time---车辆名称---${GlobalData.driverInfoBean?.vehicleName}".toByteArray())
file.appendBytes("\n".toByteArray())
file.appendBytes("$time---司机名称---${GlobalData.driverInfoBean?.userName ?: GlobalData.driverInfoBean?.userName}".toByteArray())
file.appendBytes("\n".toByteArray())
}
private fun getCurrentTime() : String {
return TimeUtils.millis2String(System.currentTimeMillis(), "yyyy/MM/dd HH:mm:ss.SSS")
}
private val currentTime : String?
get() = TimeUtils.millis2String(System.currentTimeMillis(), "yyyy_MM_dd HH:mm:ss:SS")
class LogTask(appContext : Context, workerParams : WorkerParameters) :
Worker(appContext, workerParams) {
@ -307,4 +250,11 @@ object LogUtil {
return Result.success()
}
}
}
}

View File

@ -5,7 +5,6 @@ import android.media.AudioAttributes
import android.media.AudioManager
import android.media.MediaPlayer
import androidx.core.content.ContextCompat
import com.za.base.AppConfig
import com.za.bean.request.AppNewOrderVoiceRequest
import com.za.common.GlobalData
import com.za.common.log.LogUtil
@ -61,18 +60,21 @@ object SpeechManager {
}
private var originVolume = 0
private var defaultVolume = 10
private var originVolume = defaultVolume
private var audioManager : AudioManager? = null
private fun setMaxAudioVolume() {
try {
if (! AppConfig.isRelease) {
audioManager =
ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java)
if (audioManager == null) {
return
}
val audioManager =
ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java)
originVolume = audioManager?.getStreamVolume(AudioManager.STREAM_MUSIC) ?: 0
originVolume = audioManager?.getStreamVolume(AudioManager.STREAM_MUSIC) ?: defaultVolume
val maxVolume = audioManager?.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC,
maxVolume ?: 1,
maxVolume ?: defaultVolume,
AudioManager.FLAG_PLAY_SOUND)
} catch (e : Exception) {
LogUtil.print("setMaxAudioVolume", e)
@ -80,15 +82,12 @@ object SpeechManager {
}
private fun resetAudioVolume() {
try {
if (! AppConfig.isRelease) {
return
}
val audioManager =
ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java)
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC,
originVolume,
AudioManager.FLAG_PLAY_SOUND)
try { // val audioManager =
// ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java)
// audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC,
// originVolume,
// AudioManager.FLAG_PLAY_SOUND)
LogUtil.print("resetAudioVolume", "resetAudioVolume")
} catch (e : Exception) {
LogUtil.print("resetAudioVolume", e)
}
@ -153,9 +152,7 @@ object SpeechManager {
}
// 设置最大音量
setMaxAudioVolume()
// 开始播放
setMaxAudioVolume() // 开始播放
mediaPlayer?.start()
} catch (e : Exception) {
LogUtil.print("playNewOrder", "播放出错: ${e.message}")
@ -174,7 +171,7 @@ object SpeechManager {
mediaPlayer?.setDataSource(url)
mediaPlayer?.setOnPreparedListener { // 设置最大音量
setMaxAudioVolume() // 准备完成后开始播放
// 准备完成后开始播放
it.start()
}
mediaPlayer?.setOnCompletionListener { // 播放完成后释放资源
@ -185,8 +182,7 @@ object SpeechManager {
releaseMediaPlayer()
true
}
// 异步准备避免阻塞UI线程
setMaxAudioVolume() // 异步准备避免阻塞UI线程
mediaPlayer?.prepareAsync()
} catch (e : Exception) {
LogUtil.print("playNewOrderFromNet", "播放出错: ${e.message}")

View File

@ -2,80 +2,89 @@ package com.za.common.speech
import android.annotation.SuppressLint
import android.content.Context
import android.media.AudioManager
import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.speech.tts.TextToSpeech.Engine.KEY_PARAM_VOLUME
import android.speech.tts.UtteranceProgressListener
import com.za.common.log.LogUtil
import java.util.Locale
@SuppressLint("StaticFieldLeak")
object TTSManager {
private var tts: TextToSpeech? = null
private var context: Context? = null
private var listener: OnTTSListener? = null
private var tts : TextToSpeech? = null
private var context : Context? = null
private var listener : OnTTSListener? = null
fun init(context: Context?, onTTSListener: OnTTSListener) {
this.context = context
this.listener = onTTSListener
initTTS()
}
fun init(context : Context?, onTTSListener : OnTTSListener) {
this.context = context
this.listener = onTTSListener
initTTS()
}
private fun initTTS() {
tts = TextToSpeech(context) { status ->
if (status == TextToSpeech.SUCCESS) {
val result = tts?.setLanguage(Locale.getDefault())
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
LogUtil.print("TTS", "Language not supported")
listener?.onTTSFailed("Language not supported")
} else {
LogUtil.print("TTS", "TTS initialized successfully")
listener?.onTTSInitialized()
}
} else {
LogUtil.print("TTS", "Initialization failed")
listener?.onTTSFailed("Initialization failed")
}
}
private fun initTTS() {
tts = TextToSpeech(context) { status ->
if (status == TextToSpeech.SUCCESS) {
val result = tts?.setLanguage(Locale.getDefault())
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
LogUtil.print("TTS", "Language not supported")
listener?.onTTSFailed("Language not supported")
} else {
LogUtil.print("TTS", "TTS initialized successfully")
listener?.onTTSInitialized()
}
} else {
LogUtil.print("TTS", "Initialization failed")
listener?.onTTSFailed("Initialization failed")
}
}
tts?.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
override fun onStart(utteranceId: String) {
LogUtil.print("TTS", "Speech started")
}
tts?.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
override fun onStart(utteranceId : String) {
LogUtil.print("TTS", "Speech started")
}
override fun onDone(utteranceId: String) {
LogUtil.print("TTS", "Speech completed")
listener?.onTTSSuccess()
}
override fun onDone(utteranceId : String) {
LogUtil.print("TTS", "Speech completed")
listener?.onTTSSuccess()
}
override fun onError(utteranceId: String) {
LogUtil.print("TTS", "Speech error")
listener?.onTTSFailed("Speech error")
}
})
}
override fun onError(utteranceId : String) {
LogUtil.print("TTS", "Speech error")
listener?.onTTSFailed("Speech error")
}
})
}
fun speak(text: String?) {
if (tts != null && tts?.isSpeaking == false) {
tts?.speak(text, TextToSpeech.QUEUE_FLUSH, null, "uniqueId")
}
}
fun speak(text : String?) {
if (tts != null && tts?.isSpeaking == false) { //设置最大音量
val am = context?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val sb2value = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
am.setStreamVolume(AudioManager.STREAM_MUSIC, sb2value, 0)
val bundle = Bundle()
bundle.putFloat(KEY_PARAM_VOLUME, 1.0f)
tts?.speak(text, TextToSpeech.QUEUE_FLUSH, bundle, "uniqueId")
}
}
fun stop() {
tts?.takeIf { it.isSpeaking }?.stop()
}
fun stop() {
tts?.takeIf { it.isSpeaking }?.stop()
}
fun shutdown() {
tts?.apply {
stop()
shutdown()
}
tts = null
}
fun shutdown() {
tts?.apply {
stop()
shutdown()
}
tts = null
}
}
interface OnTTSListener {
fun onTTSInitialized()
fun onTTSSuccess()
fun onTTSFailed(errorMessage: String?)
fun onTTSInitialized()
fun onTTSSuccess()
fun onTTSFailed(errorMessage : String?)
}

View File

@ -22,9 +22,11 @@ import com.za.bean.ReportHistoryBean
import com.za.bean.ReportHistoryRequest
import com.za.bean.ReportInfoRequest
import com.za.bean.ReportItem
import com.za.bean.SaveSignatureRequest
import com.za.bean.SettleInfoRequest
import com.za.bean.TaskNotesBean
import com.za.bean.TaskSettlementAndTraceBean
import com.za.bean.UnifiedOCRWithCompressRequest
import com.za.bean.UpdateVersionBean
import com.za.bean.UpdateVersionRequest
import com.za.bean.UploadChangeBatteryRequest
@ -289,4 +291,12 @@ interface ApiService {
@POST("driverApp/task/getTaskNotes")
fun getTaskNotes(@Body request : TaskNotesRequest) : Observable<BaseResponse<TaskNotesBean>>
@POST("driverApp/supplier/unifiedOCRWithCompress")
fun unifiedOCRWithCompress(@Body request : UnifiedOCRWithCompressRequest) : Observable<BaseResponse<String>>
@POST("driverApp/v2/user/saveSignature")
fun saveSignature(@Body request : SaveSignatureRequest) : Observable<BaseResponse<String>>
}

View File

@ -11,6 +11,7 @@ import com.za.base.view.warnBean
import com.za.bean.BaseResponse
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.offline.OfflineService
import com.za.service.location.ZdLocationManager
import io.reactivex.rxjava3.core.Observer
import io.reactivex.rxjava3.disposables.Disposable
@ -128,6 +129,7 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
ToastUtils.showShort("登陆信息已过期,请重新登录")
ZdLocationManager.stopContinuousLocation()
GlobalData.clearUserCache()
OfflineService.stop()
ActivityUtils.startLauncherActivity()
} catch (e : Exception) {
LogUtil.print("handlerTokenExpired", e)

View File

@ -113,16 +113,15 @@ object CommonMethod {
})
}
private var lastFetchGenerateInfoTime : Long = 0L
fun getGenerateInfo(vehicleId : Int? = null,
userId : Int? = null,
success : (DriverInfoBean) -> Unit = {},
failed : (String?) -> Unit = {}) {
fun getGenerateInfo(success : (DriverInfoBean) -> Unit = {}, failed : (String?) -> Unit = {}) {
val generalInfoRequest =
GeneralInfoRequest(vehicleId = vehicleId ?: GlobalData.driverInfoBean?.vehicleId,
driverId = userId ?: GlobalData.driverInfoBean?.userId,
deviceId = DeviceUtil.getAndroidId(ActivityUtils.getTopActivity()))
GeneralInfoRequest(vehicleId = GlobalData.vehicleId.takeIf { it != null && it != 0 }
?: GlobalData.driverInfoBean?.vehicleId,
driverId = GlobalData.driverId.takeIf { it != null && it != 0 }
?: GlobalData.driverInfoBean?.userId,
deviceId = GlobalData.deviceId
?: DeviceUtil.getAndroidId(ActivityUtils.getTopActivity()))
LogUtil.print("getGenerateInfo", "request=${generalInfoRequest.toJson()}")
RetrofitHelper.getDefaultService().generalInfo(generalInfoRequest)
@ -130,21 +129,39 @@ object CommonMethod {
.subscribe(object : BaseObserver<DriverInfoBean>() {
override fun doSuccess(it : DriverInfoBean?) {
if (it == null) {
failed("获取车辆信息失败")
if (GlobalData.driverInfoBean != null) {
success(GlobalData.driverInfoBean !!)
} else {
failed("获取车辆信息失败")
}
LogUtil.print("getGenerateInfo", "获取车辆信息失败")
return
}
if (GlobalData.driverId == null || GlobalData.driverId == 0) {
GlobalData.driverId = it.userId
}
if (GlobalData.vehicleId == null || GlobalData.vehicleId == 0) {
GlobalData.vehicleId = it.vehicleId
}
if (GlobalData.deviceId.isNullOrBlank()) {
GlobalData.deviceId = it.deviceId
}
if (GlobalData.driverInfoBean != null && (System.currentTimeMillis() - lastFetchGenerateInfoTime < 1000 * 10)) {
LogUtil.print("getGenerateInfo",
"获取车辆信息成功但是时间间隔小于10秒不更新车辆信息")
success(it)
return
}
GlobalData.driverInfoBean = it
lastFetchGenerateInfoTime = System.currentTimeMillis()
LogUtil.print("GlobalData.driverInfoBean",
"${GlobalData.driverInfoBean?.toJson()}}")
GlobalData.driverInfoBean = it
lastFetchGenerateInfoTime = System.currentTimeMillis()
LogUtil.print("GlobalData.driverInfoBean", "${it.toJson()}}")
success(it)
}
@ -197,7 +214,7 @@ object CommonMethod {
}
override fun doFailure(code : Int, msg : String?) {
LogUtil.print("getNewOrder", "doFailure=$msg")
}
})
}
@ -233,92 +250,45 @@ object CommonMethod {
?.getOfflineTaskFromTaskId(inServicingOrder.taskId ?: 0).isNullOrEmpty()
) {
LogUtil.print("queryOrderList", "走离线任务逻辑")
inServicingOrder = inServicingOrder.copy(taskState = RoomHelper.db?.orderDao()
?.getCurrentOrder()?.taskState)
if (GlobalData.currentOrder?.serviceTypeName != inServicingOrder.serviceTypeName) {
LogUtil.print("queryOrderList", "订单类型发生变化")
RoomHelper.db?.photoTemplateDao()
?.deleteOrderPhotoTemplateFromTaskId(taskId = inServicingOrder.taskId
?: 0)
fetchPhotoTemplate(orderInfo = inServicingOrder,
isNeedUpload = true,
success = {
GlobalData.currentOrder = inServicingOrder
OfflineManager.start(GlobalData.currentOrder?.taskId)
context?.let {
ReportFloatingManager.startService(it)
}
success(inServicingOrder, waitServiceOrders)
},
failed = {
GlobalData.currentOrder = inServicingOrder
OfflineManager.start(GlobalData.currentOrder?.taskId)
context?.let {
ReportFloatingManager.startService(it)
}
success(inServicingOrder, waitServiceOrders)
})
}
return
}
if (GlobalData.currentOrder?.serviceTypeName != inServicingOrder.serviceTypeName) {
LogUtil.print("queryOrderList", "订单类型发生变化")
RoomHelper.db?.photoTemplateDao()
?.deleteOrderPhotoTemplateFromTaskId(taskId = inServicingOrder.taskId
?: 0)
fetchPhotoTemplate(orderInfo = inServicingOrder,
isNeedUpload = true,
success = {
GlobalData.currentOrder = inServicingOrder
OfflineManager.start(GlobalData.currentOrder?.taskId)
context?.let {
ReportFloatingManager.startService(it)
}
success(inServicingOrder, waitServiceOrders)
},
failed = {
GlobalData.currentOrder = inServicingOrder
OfflineManager.start(GlobalData.currentOrder?.taskId)
context?.let {
ReportFloatingManager.startService(it)
}
success(inServicingOrder, waitServiceOrders)
})
return
val offlineTaskState =
RoomHelper.db?.orderDao()?.getCurrentOrder()?.taskState
?: inServicingOrder.taskState
inServicingOrder = inServicingOrder.copy(taskState = offlineTaskState)
}
GlobalData.currentOrder = inServicingOrder
success(inServicingOrder, waitServiceOrders)
val listData = RoomHelper.db?.photoTemplateDao()
?.queryCurrentOrderHasTaskNode(inServicingOrder.userOrderId ?: 0)
LogUtil.print("queryOrderList", "listData=${listData.toJson()}")
if (listData.isNullOrEmpty() || (! listData[0].serviceTypeName.isNullOrBlank() && listData[0].serviceTypeName != inServicingOrder.serviceTypeName)) {
RoomHelper.db?.photoTemplateDao()
?.deleteOrderPhotoTemplateFromTaskId(taskId = inServicingOrder.taskId
?: 0)
fetchPhotoTemplate(orderInfo = inServicingOrder)
LogUtil.print("queryOrderList", "照片模版为空,重新获取照片模版")
}
it.forEach {
if (RoomHelper.db?.orderDao()
?.getOrderInfoFromTaskId(it.taskId ?: 0) != null
) {
RoomHelper.db?.orderDao()?.update(orderInfo = it)
val task = RoomHelper.db?.orderDao()?.getOrderInfoFromTaskId(it.taskId ?: 0)
if (task != null) {
RoomHelper.db?.orderDao()
?.update(orderInfo = it.copy(taskState = task.taskState))
} else {
RoomHelper.db?.orderDao()?.insertOrder(orderInfo = it)
}
}
fetchPhotoTemplate(inServicingOrder)
queryElectronOrder(orderInfo = inServicingOrder)
WaterMarkerTemplateManager.fetchWaterTemplate(inServicingOrder.taskCode ?: "",
taskId = inServicingOrder.taskId ?: 0,
inServicingOrder.userOrderId ?: 0)
OfflineManager.start(GlobalData.currentOrder?.taskId)
context?.let {
ReportFloatingManager.startService(it)
}
success(inServicingOrder, waitServiceOrders)
}
override fun doFailure(code : Int, msg : String?) {
@ -334,7 +304,6 @@ object CommonMethod {
}
fun fetchPhotoTemplate(orderInfo : OrderInfo?,
isNeedUpload : Boolean? = false,
success : () -> Unit = {},
failed : (String?) -> Unit = {}) {
val photoTemplateRequest = PhotoTemplateRequest(orderInfo?.userOrderId)
@ -343,13 +312,6 @@ object CommonMethod {
.subscribe(object : BaseObserver<List<PhotoTemplateInfo>>() {
override fun doSuccess(it : List<PhotoTemplateInfo>?) {
if (it.isNullOrEmpty()) {
LogUtil.print("fetchPhotoTemplate",
"模板为null ${photoTemplateRequest.toJson()}")
failed("未找到模版")
return
}
if (isNeedUpload == false) {
val listData = RoomHelper.db?.photoTemplateDao()
?.queryCurrentOrderHasTaskNode(orderInfo?.userOrderId ?: 0)
if (! listData.isNullOrEmpty()) {
@ -358,6 +320,31 @@ object CommonMethod {
success()
return
}
val list = LocalPhotoTemplateControl.buildPhotoTemplate(orderInfo)
list.forEachIndexed { _, item ->
val photoTemplateInfo = item.copy(userOrderId = orderInfo?.userOrderId,
taskCode = orderInfo?.taskCode,
myCustomPhotoType = Const.PhotoType.InServicing,
advanceTime = orderInfo?.advanceTime,
taskId = orderInfo?.taskId,
serviceTypeName = orderInfo?.serviceTypeName,
needWaterMarker = orderInfo?.needWaterMarker,
needShowPhoneBrand = orderInfo?.needShowPhoneBrand,
photoSource = 0)
RoomHelper.db?.photoTemplateDao()?.insert(photoTemplateInfo)
}
LogUtil.print("queryPhotoTemplate", "使用本地模版==${listData.toJson()}")
success()
return
}
val listData = RoomHelper.db?.photoTemplateDao()
?.queryCurrentOrderHasTaskNode(orderInfo?.userOrderId ?: 0)
if (! listData.isNullOrEmpty()) {
LogUtil.print("queryPhotoTemplate", "模板已经存在==${listData.toJson()}")
success()
return
}
it.forEachIndexed { _, item ->
@ -365,6 +352,7 @@ object CommonMethod {
taskCode = orderInfo?.taskCode,
myCustomPhotoType = Const.PhotoType.InServicing,
advanceTime = orderInfo?.advanceTime,
serviceTypeName = orderInfo?.serviceTypeName,
taskId = orderInfo?.taskId,
needWaterMarker = orderInfo?.needWaterMarker,
needShowPhoneBrand = orderInfo?.needShowPhoneBrand,
@ -377,7 +365,30 @@ object CommonMethod {
override fun doFailure(code : Int, msg : String?) {
LogUtil.print("fetchPhotoTemplate", "err==$msg")
failed(msg)
val listData = RoomHelper.db?.photoTemplateDao()
?.queryCurrentOrderHasTaskNode(orderInfo?.userOrderId ?: 0)
if (! listData.isNullOrEmpty()) {
LogUtil.print("queryPhotoTemplate", "模板已经存在==${listData.toJson()}")
success()
return
}
val list = LocalPhotoTemplateControl.buildPhotoTemplate(orderInfo)
list.forEachIndexed { _, item ->
val photoTemplateInfo = item.copy(userOrderId = orderInfo?.userOrderId,
taskCode = orderInfo?.taskCode,
myCustomPhotoType = Const.PhotoType.InServicing,
advanceTime = orderInfo?.advanceTime,
serviceTypeName = orderInfo?.serviceTypeName,
taskId = orderInfo?.taskId,
needWaterMarker = orderInfo?.needWaterMarker,
needShowPhoneBrand = orderInfo?.needShowPhoneBrand,
photoSource = 0)
RoomHelper.db?.photoTemplateDao()?.insert(photoTemplateInfo)
}
LogUtil.print("queryPhotoTemplate", "使用本地模版==${listData.toJson()}")
success()
return
}
})
}
@ -406,7 +417,8 @@ object CommonMethod {
carVin = it?.vinNo,
serviceContent = it?.serviceTerm,
serverAcceptCarSignPath = it?.recipientSignPath,
serverServicePeopleSignPath = it?.waitstaffSignPath,
serverServicePeopleSignPath = it?.waitstaffSignPath
?: GlobalData.driverInfoBean?.signatureUrl,
orderType = it?.serviceName)
it?.damageFileList?.forEach { item ->
val damagePhotoBean = EleCarDamagePhotoBean(userOrderId = it.userOrderId,
@ -417,6 +429,7 @@ object CommonMethod {
RoomHelper.db?.eleCarDamagePhotoDao()?.insert(damagePhotoBean)
}
RoomHelper.db?.eleWorkOrderDao()?.insertEleWorkOrder(temp)
LogUtil.print("queryElectronOrder", "插入成功 ${temp.toJson()}")
success(temp)
}

File diff suppressed because it is too large Load Diff

View File

@ -91,7 +91,7 @@ object OfflineManager {
}
}
private fun stop() {
fun stop() {
uploadDispose?.dispose()
uploadDispose = null
isUploading = false

View File

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

View File

@ -16,6 +16,9 @@ interface OrderDao {
@Query("select * from order_info where taskId =:taskId")
fun getOrderInfoFromTaskId(taskId: Int): OrderInfo?
@Query("select * from order_info where taskCode =:taskCode")
fun getOrderInfoFromTaskCode(taskCode: String): OrderInfo?
//获取当前正在执行的订单
@Query("select * from order_info where isCurrent ==1")
fun getCurrentOrder(): OrderInfo?

View File

@ -23,4 +23,5 @@ data class DriverInfoBean(
val vehicleState : Int? = null, //车辆状态 0 空闲 1 忙碌
val plateNumber : String? = null, //车牌号
val deviceId : String? = null,
val signatureUrl : String? = null, //手写签名照地址
) : Parcelable

View File

@ -30,6 +30,7 @@ interface PushListener {
fun reDispatchOrder(jpushBean : JpushBean)
fun broadcast(string : String)
fun importantTip(jpushBean : JpushBean)
fun reportHandle(jpushBean : JpushBean)
}
data class LastJPushBean(val msg : String, val time : Long = System.currentTimeMillis())
@ -67,33 +68,49 @@ object ServiceManager {
private fun setupPushMessageReceiver(context : Context) { // 注册推送消息接收器
registerPushListener(object : PushListener {
override fun broadcast(msg : String) {
sendMessageToMainProcess(context = context, "broadcast", msg)
sendMessageToMainProcess(context = context, Const.PushMessageType.BROADCAST, msg)
}
override fun giveUpOrder(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, "giveUp", Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.GIVE_UP,
Gson().toJson(jpushBean))
}
override fun importantTip(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context,
"importantTip",
Const.PushMessageType.IMPORTANT_TIP,
Gson().toJson(jpushBean))
}
override fun newOrderMsg(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, "newOrder", Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.NEW_ORDER,
Gson().toJson(jpushBean))
}
override fun reDispatchOrder(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, "reDispatch", Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.RE_DISPATCH,
Gson().toJson(jpushBean))
}
override fun revokeOrder(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, "revoke", Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.REVOKE,
Gson().toJson(jpushBean))
}
override fun modifyOrder(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, "modify", Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.MODIFY,
Gson().toJson(jpushBean))
}
override fun reportHandle(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context,
Const.PushMessageType.REPORT_HANDLE,
Gson().toJson(jpushBean))
}
})
}
@ -145,6 +162,7 @@ object ServiceManager {
1 -> handleTypeOneMessage(jpushOrderInfoBean)
2 -> handlerModifyOrderMessage(jpushOrderInfoBean)
3 -> importantTip(jpushOrderInfoBean)
4 -> reportHandler(jpushOrderInfoBean)
else -> LogUtil.print("JpushMessage",
"Unknown push type: ${jpushOrderInfoBean.pushType}")
}
@ -171,9 +189,9 @@ object ServiceManager {
// Handle type one messages
private fun handleTypeOneMessage(jpushOrderBean : JpushBean) {
when (jpushOrderBean.typeDesc) {
"giveUp" -> giveUpOrder(jpushOrderBean)
"revoke" -> revokeOrder(jpushOrderBean)
"reDispatch" -> reDispatchOrder(jpushOrderBean)
Const.PushMessageType.GIVE_UP -> giveUpOrder(jpushOrderBean)
Const.PushMessageType.REVOKE -> revokeOrder(jpushOrderBean)
Const.PushMessageType.RE_DISPATCH -> reDispatchOrder(jpushOrderBean)
else -> LogUtil.print("JpushMessage", "Unknown typeDesc: ${jpushOrderBean.typeDesc}")
}
}
@ -218,6 +236,10 @@ object ServiceManager {
pushListener?.importantTip(jpushOrderBean)
}
private fun reportHandler(jpushOrderBean : JpushBean) {
pushListener?.reportHandle(jpushOrderBean)
}
// Disconnect from JPush and MQTT
fun disconnect(context : Context) {
Handler(Looper.getMainLooper()).post {
@ -259,13 +281,13 @@ object ServiceManager {
1 -> {
when (jpushOrderInfoBean.typeDesc) {
"giveUp" -> sendNotification(GlobalData.application,
Const.PushMessageType.GIVE_UP -> sendNotification(GlobalData.application,
"订单:${jpushOrderInfoBean.taskCode ?: ""}已被放弃!")
"revoke" -> sendNotification(GlobalData.application,
Const.PushMessageType.REVOKE -> sendNotification(GlobalData.application,
"订单:${jpushOrderInfoBean.taskCode ?: ""}已被撤回!")
"reDispatch" -> sendNotification(GlobalData.application,
Const.PushMessageType.RE_DISPATCH -> sendNotification(GlobalData.application,
"订单:${jpushOrderInfoBean.taskCode ?: ""}被改派!")
else -> {}

View File

@ -22,7 +22,9 @@ import com.za.bean.JpushBean
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.speech.SpeechManager
import com.za.room.RoomHelper
import com.za.servicing.R
import com.za.ui.order_report.ReportFloatingManager
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
import com.za.ui.view.CommonDialogFragment
@ -113,6 +115,20 @@ class ZdPushServiceReceive : BroadcastReceiver() {
}
}
Const.PushMessageType.REPORT_HANDLE -> {
try {
val jpushBean = Gson().fromJson(message, JpushBean::class.java)
val activity = BaseActivityLifecycleCallbacks.Companion.getCurrentActivity()
?: ActivityUtils.getTopActivity()
if (activity is AppCompatActivity) {
handeReportMessage(jpushBean = jpushBean)
}
} catch (e : Exception) {
LogUtil.print("PushActivityLifecycleCallbacks",
"处理重要提示消息失败: ${e.message}")
}
}
}
}
}
@ -183,6 +199,24 @@ class ZdPushServiceReceive : BroadcastReceiver() {
}
}
private fun handeReportMessage(jpushBean : JpushBean) {
SpeechManager.speech("您提交的报批已经处理!")
BaseActivityLifecycleCallbacks.getCurrentActivity()?.let { currentActivity ->
if (currentActivity is AppCompatActivity) {
AlertDialog.Builder(currentActivity).setTitle("提醒")
.setMessage("您提交的报批已经处理,是否前往查看?")
.setPositiveButton("确定") { dialog, _ ->
dialog.dismiss()
currentActivity.finish()
val order = RoomHelper.db?.orderDao()
?.getOrderInfoFromTaskCode(taskCode = jpushBean.taskCode ?: "")
ReportFloatingManager.goReportPage(context = currentActivity,
orderInfo = order)
}.show()
}
}
}
// Handle broadcast messages
private fun handleBroadcast(msg : String) {
try {

View File

@ -21,6 +21,7 @@ class ConnectionOptionWrapper(
isCleanSession = true
keepAliveInterval = 90 // Keep alive interval in seconds
isAutomaticReconnect = true
maxInflight = 1000
mqttVersion = MqttConnectOptions.MQTT_VERSION_3_1_1
connectionTimeout = 30 // Connection timeout in seconds
}

View File

@ -1,67 +1,54 @@
package com.za.service.mqtt
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.annotation.SuppressLint
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.util.DeviceUtil
import com.za.common.util.Tools.macSignature
import com.za.service.ServiceManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.eclipse.paho.android.service.MqttAndroidClient
import org.eclipse.paho.client.mqttv3.IMqttActionListener
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended
import org.eclipse.paho.client.mqttv3.MqttClient
import org.eclipse.paho.client.mqttv3.IMqttToken
import org.eclipse.paho.client.mqttv3.MqttCallback
import org.eclipse.paho.client.mqttv3.MqttConnectOptions
import org.eclipse.paho.client.mqttv3.MqttException
import org.eclipse.paho.client.mqttv3.MqttMessage
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence
import kotlin.concurrent.Volatile
object MyMqttClient {
private lateinit var clientId : String
private lateinit var topic : String
private val mqttClient : MqttClient by lazy {
MqttClient("tcp://${MqttConfig.END_POINT}:1883", clientId, MemoryPersistence())
}
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val _connectionState = MutableStateFlow(false)
private val connectionState : StateFlow<Boolean> = _connectionState
@SuppressLint("StaticFieldLeak")
private var mqttClient : MqttAndroidClient? = null
fun initialize(deviceId : String?) {
clientId = "${MqttConfig.GROUP_ID}@@@$deviceId"
topic = "${MqttConfig.TOPIC_PREFIX}/$clientId"
mqttClient = MqttAndroidClient(GlobalData.application,
"tcp://${MqttConfig.END_POINT}:1883",
clientId,
MemoryPersistence())
setupMqttCallbacks()
connect()
LogUtil.print("MyMqttClient ", "initialize success")
Log.e("MyMqttClient ", "initialize success")
}
private fun setupMqttCallbacks() {
mqttClient.setCallback(object : MqttCallbackExtended {
override fun connectComplete(reconnect : Boolean, serverURI : String) {
val status = if (reconnect) "Reconnected" else "Connected"
LogUtil.print("MyMqttClient ", "$status to: $serverURI")
_connectionState.value = true
subscribeTopic()
}
mqttClient?.setCallback(object : MqttCallback {
override fun connectionLost(throwable : Throwable) {
LogUtil.print("MyMqttClient ", "Connection lost: ${throwable.message}")
_connectionState.value = false
LogUtil.print("MyMqttClient ",
"Connection lost: ${throwable.message}") // connect()
}
override fun messageArrived(topic : String, mqttMessage : MqttMessage) {
Handler(Looper.getMainLooper()).post {
val message = String(mqttMessage.payload)
LogUtil.print("MyMqttClient ", "Message arrived: $message")
ServiceManager.handlerPushMsg(message)
}
val message = String(mqttMessage.payload)
LogUtil.print("MyMqttClient ", "Message arrived: $message")
ServiceManager.handlerPushMsg(message)
}
override fun deliveryComplete(token : IMqttDeliveryToken) {
@ -70,64 +57,102 @@ object MyMqttClient {
})
}
@Volatile
private var isConnecting : Boolean? = null
private fun connect() {
if (connectionState.value && mqttClient.isConnected) {
LogUtil.print("MyMqttClient ", "Already connected")
if (isConnecting == true) {
return
}
try {
isConnecting = true
coroutineScope.launch {
try {
val options = ConnectionOptionWrapper(MqttConfig.INSTANCE_ID,
MqttConfig.ACCESS_KEY,
clientId,
MqttConfig.SECRET_KEY).mqttConnectOptions
val mqttConnectOption = MqttConnectOptions()
mqttConnectOption.userName =
"Signature|" + MqttConfig.ACCESS_KEY + "|" + MqttConfig.INSTANCE_ID
mqttConnectOption.password = macSignature(clientId, MqttConfig.SECRET_KEY).toCharArray()
mqttConnectOption.isCleanSession = true
mqttConnectOption.keepAliveInterval = 90
mqttConnectOption.isAutomaticReconnect = true
mqttConnectOption.maxInflight = 1000
mqttConnectOption.maxReconnectDelay = 30 * 1000
mqttConnectOption.mqttVersion = MqttConnectOptions.MQTT_VERSION_3_1_1
mqttConnectOption.connectionTimeout = 30
withContext(Dispatchers.IO) {
mqttClient.connect(options)
mqttClient?.connect(mqttConnectOption, null, object : IMqttActionListener {
override fun onSuccess(asyncActionToken : IMqttToken?) {
isConnecting = false
LogUtil.print("MyMqttClient ", "connect success==")
subscribeTopic()
}
_connectionState.value = true // Update connection state after successful connection
} catch (e : MqttException) {
LogUtil.print("MyMqttClient ", "Connection failed: ${e.message}")
_connectionState.value = false // Update connection state on failure
}
override fun onFailure(asyncActionToken : IMqttToken?, exception : Throwable?) {
isConnecting = false
LogUtil.print("MyMqttClient ", "connect failed== ${exception?.message}")
}
})
} catch (e : MqttException) {
LogUtil.print("MyMqttClient ", "Connection failed2: ${e.message}")
}
}
//检测mqtt连接状态
fun publishMessage() {
if (mqttClient.isConnected) {
if (mqttClient == null) {
initialize(deviceId = DeviceUtil.getAndroidId(GlobalData.application))
return
}
if (mqttClient?.isConnected == true) {
LogUtil.print("MyMqttClient ", "mqttClient.hasConnected")
return
}
connect()
LogUtil.print("MyMqttClient ", "mqttClient 断开重新初始化")
ServiceManager.initialize(GlobalData.application)
}
private fun subscribeTopic() {
coroutineScope.launch {
try {
mqttClient.subscribe(topic, MqttConfig.QOS_LEVEL)
LogUtil.print("MyMqttClient ", "Subscribed to topic: $topic")
} catch (e : MqttException) {
LogUtil.print("MyMqttClient ", "Subscribe failed: ${e.message}")
try {
if (mqttClient?.isConnected == true) {
mqttClient?.subscribe(topic,
MqttConfig.QOS_LEVEL,
null,
object : IMqttActionListener {
override fun onSuccess(asyncActionToken : IMqttToken?) {
LogUtil.print("MyMqttClient ", "Subscribed to topic: $topic")
}
override fun onFailure(asyncActionToken : IMqttToken?,
exception : Throwable) {
LogUtil.print("MyMqttClient ", "Subscribe failed: ${exception.message}")
}
})
} else {
LogUtil.print("MyMqttClient ", "Cannot subscribe: MQTT client is not connected")
}
} catch (e : MqttException) {
LogUtil.print("MyMqttClient ", "Subscribe failed2: ${e.message}")
}
}
fun disconnect() {
coroutineScope.launch {
try {
if (connectionState.value) {
mqttClient.disconnect()
_connectionState.value = false
LogUtil.print("MyMqttClient ", "Disconnected successfully")
}
} catch (e : MqttException) {
LogUtil.print("MyMqttClient ", "Disconnect failed: ${e.message}")
} finally {
coroutineScope.cancel()
try {
if (mqttClient?.isConnected == true) {
mqttClient?.disconnect(null, object : IMqttActionListener {
override fun onSuccess(asyncActionToken : IMqttToken?) {
LogUtil.print("MyMqttClient ", "Disconnected")
}
override fun onFailure(asyncActionToken : IMqttToken?, exception : Throwable) {
LogUtil.print("MyMqttClient ", "Disconnect failed")
}
})
}
} catch (e : MqttException) {
LogUtil.print("MyMqttClient ", "Disconnect failed: ${e.message}")
}
}
}

View File

@ -6,11 +6,13 @@ import android.graphics.Color
import android.view.ViewGroup
import android.webkit.JavascriptInterface
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
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.material3.LinearProgressIndicator
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
@ -23,6 +25,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.net.toUri
import coil.compose.AsyncImage
import com.blankj.utilcode.util.ToastUtils
import com.tencent.smtt.sdk.WebChromeClient
import com.tencent.smtt.sdk.WebSettings
@ -35,6 +39,7 @@ import com.za.base.view.HeadView
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.ext.finish
import com.za.servicing.R
class CommonH5Activity : BaseActivity() {
private var webView : WebView? = null
@ -45,7 +50,6 @@ class CommonH5Activity : BaseActivity() {
val url = intent.getStringExtra(EXTRA_URL)
val title = intent.getStringExtra(EXTRA_TITLE)
val isCanBack = intent.getBooleanExtra(EXTRA_CAN_BACK, true)
if (url.isNullOrBlank()) {
ToastUtils.showLong("无效的URL")
finish()
@ -165,7 +169,15 @@ private fun CommonH5Screen(url : String,
Scaffold(topBar = {
if (title.isNotBlank()) {
HeadView(title = title, onBack = { handleBackPress(context, webView) })
HeadView(title = title, onBack = { handleBackPress(context, webView) }, action = {
AsyncImage(model = R.drawable.sv_browth,
contentDescription = "",
modifier = Modifier.size(24.dp).clickable{
val intent = Intent(Intent.ACTION_VIEW)
intent.setData(url.toUri())
context.startActivity(intent)
})
})
}
}) { paddingValues ->
Box(modifier = Modifier
@ -186,6 +198,7 @@ private fun CommonH5Screen(url : String,
webView = this
onWebViewCreated(this)
loadUrl(url)
LogUtil.print("H5Activity url", url)
}
})
@ -228,6 +241,7 @@ private fun setupWebViewClients(webView : WebView,
}
override fun shouldOverrideUrlLoading(p0 : WebView?, p1 : String?) : Boolean {
LogUtil.print("H5Activity url", "$p1")
p0?.loadUrl(p1)
return false
}

View File

@ -104,20 +104,34 @@ class ServicingMainActivity : BaseActivity() {
Gson().toJson(jpushBean))
}
override fun reportHandle(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context,
Const.PushMessageType.REPORT_HANDLE,
Gson().toJson(jpushBean))
}
override fun newOrderMsg(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, Const.PushMessageType.NEW_ORDER, Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.NEW_ORDER,
Gson().toJson(jpushBean))
}
override fun reDispatchOrder(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, Const.PushMessageType.RE_DISPATCH, Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.RE_DISPATCH,
Gson().toJson(jpushBean))
}
override fun revokeOrder(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, Const.PushMessageType.REVOKE, Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.REVOKE,
Gson().toJson(jpushBean))
}
override fun modifyOrder(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context, Const.PushMessageType.MODIFY, Gson().toJson(jpushBean))
sendMessageToMainProcess(context = context,
Const.PushMessageType.MODIFY,
Gson().toJson(jpushBean))
}
})
}

View File

@ -87,9 +87,7 @@ class ServicingMainVm : BaseVm<ServicingMainVm.Action, ServicingMainVm.UiState>(
return
}
GlobalData.token = it.token
CommonMethod.getGenerateInfo(vehicleId = it.vehicleId,
userId = it.userId,
success = { success() },
CommonMethod.getGenerateInfo(success = { success() },
failed = { failure(it ?: "") })
}

View File

@ -26,8 +26,12 @@ import android.widget.ImageView
import androidx.core.app.NotificationCompat
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.ServiceUtils
import com.za.base.AppConfig
import com.za.bean.db.order.OrderInfo
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.servicing.R
import com.za.ui.h5.CommonH5Activity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -66,7 +70,7 @@ class ReportFloatingManager : Service() {
}
}
override fun onBind(intent : Intent?) : IBinder?{
override fun onBind(intent : Intent?) : IBinder? {
LogUtil.print("ReportFloatingManager service", "onBind")
return null
}
@ -183,7 +187,7 @@ class ReportFloatingManager : Service() {
if (! isMoving && System.currentTimeMillis() - startClickTime < CLICK_THRESHOLD) {
openMainActivity()
} else if (isDragging) { // 只保存位置,不执行吸附
} else if (isDragging) {
savePosition()
}
@ -195,9 +199,11 @@ class ReportFloatingManager : Service() {
if (ActivityUtils.getTopActivity() is OrderReportActivity) {
return
}
val intent = Intent(this, OrderReportActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
// goReportPage(orderInfo = GlobalData.currentOrder, context = this)
val intent = Intent(this, OrderReportActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
startActivity(intent)
}
@ -296,6 +302,13 @@ class ReportFloatingManager : Service() {
ServiceUtils.stopService(ReportFloatingManager::class.java)
}
}
fun goReportPage(orderInfo : OrderInfo?, context : Context) {
val url =
"${AppConfig.Resource_URL.replace("/res","")}/h5/supplier/dispatch/reportIndex?userOrderId=${orderInfo?.userOrderId}&type=2&userOrderCode=${orderInfo?.taskCode}&driverId=${GlobalData.driverInfoBean?.userId}"
CommonH5Activity.goH5Activity(context, url, "")
}
}
}

View File

@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -392,7 +393,11 @@ fun InServicingPhotoView(modifier : Modifier = Modifier,
Text(text = photoTemplateInfo.imageTitle ?: "其他",
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
color = Color.Black)
maxLines = 2,
overflow = TextOverflow.Ellipsis,
color = Color.Black,
modifier = Modifier.wrapContentWidth().takeIf { photoTemplateInfo.doHaveFilm == 1 }
?: Modifier.fillMaxWidth(0.7f))
Spacer(modifier = Modifier.width(5.dp))
if (photoTemplateInfo.doHaveFilm == 1) {
Text(text = "* 必拍",
@ -474,10 +479,12 @@ fun InServicingPhotoViewIsCanClick(modifier : Modifier = Modifier,
Text(text = photoTemplateInfo.imageTitle ?: "其他",
fontWeight = FontWeight.Medium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
fontSize = 14.sp,
color = Color.Black)
maxLines = 2,
overflow = TextOverflow.Ellipsis,
color = Color.Black,
modifier = Modifier.wrapContentWidth().takeIf { photoTemplateInfo.doHaveFilm == 1 }
?: Modifier.fillMaxWidth(0.7f))
Spacer(modifier = Modifier.width(5.dp))
if (photoTemplateInfo.doHaveFilm == 1 && isCanClick) {
Text(text = "* 必拍",
@ -722,7 +729,8 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
.clip(shape = RoundedCornerShape(3.dp)),
contentScale = ContentScale.FillBounds)
} else {
AsyncImage(model = photoTemplateInfo.getFormatPhotoUrl(),
AsyncImage(model = photoTemplateInfo.getFormatPhotoUrl()?.toIntOrNull()
?: photoTemplateInfo.getFormatPhotoUrl(),
contentDescription = "",
modifier = Modifier
.fillMaxSize()
@ -746,16 +754,16 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
contentDescription = "",
modifier = Modifier
.fillMaxSize()
.clickable {
.noDoubleClick {
if (! isCanClick) {
return@clickable
return@noDoubleClick
}
if (photoTemplateInfo.photoLocalPath.isNullOrBlank()) {
if (! PermissionX.isGranted(context,
android.Manifest.permission.ACCESS_FINE_LOCATION)
) {
ToastUtils.showShort("定位权限未开启!")
return@clickable
return@noDoubleClick
}
LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(isNeedAddress = true, success = {
@ -771,7 +779,7 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
LogUtil.print("上传图片定位获取失败",
"使用全局定位location==${GlobalData.currentLocation.toJson()}")
})
return@clickable
return@noDoubleClick
}
showReTakePhotoDialog.value = true
}

View File

@ -88,6 +88,7 @@ class CheckVehicleVm : IServicingVm<CheckVehicleVm.Action, CheckVehicleVm.UiStat
LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(isNeedAddress = true, success = {
LoadingManager.hideLoading()
doUploadOfflineTask(it, tempPhotoList = tempPhotoList)
}, failed = {
LoadingManager.hideLoading()
if (GlobalData.currentLocation != null) {
@ -114,28 +115,26 @@ class CheckVehicleVm : IServicingVm<CheckVehicleVm.Action, CheckVehicleVm.UiStat
address = it?.address,
templatePhotoInfoList = tempPhotoList.toList())
if (! getCurrentOrderOfflineTask().isNullOrEmpty()) {
val offlineUpdateTaskBean = OfflineUpdateTaskBean(type = taskRequest.type,
taskId = taskRequest.taskId,
userId = taskRequest.userId,
vehicleId = taskRequest.vehicleId,
currentState = taskRequest.currentState,
offlineMode = 1,
operateTime = taskRequest.operateTime,
updateTaskLat = taskRequest.lat,
updateTaskLng = taskRequest.lng,
updateTaskAddress = taskRequest.address,
templatePhotoInfoList = taskRequest.templatePhotoInfoList,
advanceTime = getCurrentOrder()?.advanceTime,
taskCode = getCurrentOrder()?.taskCode,
userOrderId = getCurrentOrder()?.userOrderId,
offlineTitle = "现场验车",
offlineType = 1)
insertOfflineTask(offlineUpdateTaskBean)
updateOrder(getCurrentOrder()?.copy(taskState = getCurrentOrder()?.getNextStatus()))
updateState(uiState.value.copy(goNextPage = UpdateTaskBean(nextState = getCurrentOrder()?.taskState),
orderInfo = getCurrentOrder()))
}
val offlineUpdateTaskBean = OfflineUpdateTaskBean(type = taskRequest.type,
taskId = taskRequest.taskId,
userId = taskRequest.userId,
vehicleId = taskRequest.vehicleId,
currentState = taskRequest.currentState,
offlineMode = 1,
operateTime = taskRequest.operateTime,
updateTaskLat = taskRequest.lat,
updateTaskLng = taskRequest.lng,
updateTaskAddress = taskRequest.address,
templatePhotoInfoList = taskRequest.templatePhotoInfoList,
advanceTime = getCurrentOrder()?.advanceTime,
taskCode = getCurrentOrder()?.taskCode,
userOrderId = getCurrentOrder()?.userOrderId,
offlineTitle = "现场验车",
offlineType = 1)
insertOfflineTask(offlineUpdateTaskBean)
updateOrder(getCurrentOrder()?.copy(taskState = getCurrentOrder()?.getNextStatus()))
updateState(uiState.value.copy(goNextPage = UpdateTaskBean(nextState = getCurrentOrder()?.taskState),
orderInfo = getCurrentOrder()))
}
private fun updateTemplate() {

View File

@ -39,6 +39,7 @@ import com.za.ext.finish
import com.za.ext.navigationActivity
import com.za.ext.noDoubleClick
import com.za.ui.order_report.OrderReportActivity
import com.za.ui.order_report.ReportFloatingManager
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
import kotlinx.coroutines.launch
@ -58,10 +59,8 @@ fun OrderDetailScreen(orderInfo : OrderInfo?) {
Box(modifier = Modifier
.weight(1f)
.height(60.dp)
.noDoubleClick { // val url =
// "${AppConfig.BASE_URL}/h5/supplier/dispatch/reportIndex?userOrderId=${GlobalData.currentOrder?.userOrderId}&type=2&userOrderCode=${GlobalData.currentOrder?.taskCode}&driverId=${GlobalData.driverInfoBean?.userId}"
//
// CommonH5Activity.goH5Activity(context, url, "")
.noDoubleClick {
// ReportFloatingManager.goReportPage(orderInfo = orderInfo, context = context)
context.navigationActivity(OrderReportActivity::class.java)
}

View File

@ -121,30 +121,27 @@ class InOperationVm : IServicingVm<InOperationVm.Action, InOperationVm.UiState>(
address = it?.address,
templatePhotoInfoList = tempPhotoList.toList())
if (! getCurrentOrderOfflineTask().isNullOrEmpty()) {
val offlineUpdateTaskBean = OfflineUpdateTaskBean(type = taskRequest.type,
taskId = taskRequest.taskId,
userId = taskRequest.userId,
vehicleId = taskRequest.vehicleId,
currentState = taskRequest.currentState,
offlineMode = 1,
operateTime = taskRequest.operateTime,
updateTaskLat = taskRequest.lat,
updateTaskLng = taskRequest.lng,
flowType = taskRequest.flowType,
templatePhotoInfoList = taskRequest.templatePhotoInfoList,
updateTaskAddress = taskRequest.address,
advanceTime = uiState.value.orderInfo?.advanceTime,
taskCode = uiState.value.orderInfo?.taskCode,
userOrderId = uiState.value.orderInfo?.userOrderId,
offlineTitle = "准备拖车",
offlineType = 1)
insertOfflineTask(offlineUpdateTaskBean)
updateOrder(getCurrentOrder()?.copy(taskState = getCurrentOrder()?.getNextStatus()))
updateState(uiState.value.copy(goNextPage = UpdateTaskBean(nextState = getCurrentOrder()?.taskState),
orderInfo = getCurrentOrder()))
}
val offlineUpdateTaskBean = OfflineUpdateTaskBean(type = taskRequest.type,
taskId = taskRequest.taskId,
userId = taskRequest.userId,
vehicleId = taskRequest.vehicleId,
currentState = taskRequest.currentState,
offlineMode = 1,
operateTime = taskRequest.operateTime,
updateTaskLat = taskRequest.lat,
updateTaskLng = taskRequest.lng,
flowType = taskRequest.flowType,
templatePhotoInfoList = taskRequest.templatePhotoInfoList,
updateTaskAddress = taskRequest.address,
advanceTime = uiState.value.orderInfo?.advanceTime,
taskCode = uiState.value.orderInfo?.taskCode,
userOrderId = uiState.value.orderInfo?.userOrderId,
offlineTitle = "准备拖车",
offlineType = 1)
insertOfflineTask(offlineUpdateTaskBean)
updateOrder(getCurrentOrder()?.copy(taskState = getCurrentOrder()?.getNextStatus()))
updateState(uiState.value.copy(goNextPage = UpdateTaskBean(nextState = getCurrentOrder()?.taskState),
orderInfo = getCurrentOrder()))
}
private fun updateTask() {

View File

@ -50,7 +50,6 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.TimeUtils
import com.blankj.utilcode.util.ToastUtils
import com.za.base.Const
@ -64,14 +63,12 @@ import com.za.base.view.HeadView
import com.za.bean.db.ele.EleCarDamagePhotoBean
import com.za.bean.db.ele.EleWorkOrderBean
import com.za.common.GlobalData
import com.za.common.util.AppFileManager
import com.za.common.util.ServicingSpeechManager
import com.za.ext.finish
import com.za.ext.getLastSix
import com.za.ext.goNextPage
import com.za.servicing.R
import com.za.ui.view.SignatureView
import java.io.File
import kotlin.math.ceil
@Preview
@ -127,33 +124,6 @@ fun ConfirmEleScreen(vm : ConfirmEleVm = viewModel()) {
})
}
if (uiState.value.showServicePeopleSignDialog == true) {
CommonDialog(cancelText = "取消",
confirmText = "保存",
title = "是否将签名保存,以后的工单中默认使用此签名",
cancelEnable = true,
cancel = {
vm.dispatch(ConfirmEleVm.Action.UpdateState(uiState.value.copy(
showServicePeopleSignDialog = false)))
},
dismiss = {
vm.dispatch(ConfirmEleVm.Action.UpdateState(uiState.value.copy(
showServicePeopleSignDialog = false)))
},
confirm = {
if (uiState.value.eleWorkOrderBean?.localServicePeopleSignPath.isNullOrEmpty()) {
ToastUtils.showShort("请先进行签名")
vm.dispatch(ConfirmEleVm.Action.UpdateState(uiState.value.copy(
showServicePeopleSignDialog = false)))
return@CommonDialog
}
File(uiState.value.eleWorkOrderBean?.localServicePeopleSignPath ?: "").copyTo(File(
AppFileManager.getDriverSignPath(context)), true)
vm.dispatch(ConfirmEleVm.Action.UpdateState(uiState.value.copy(
showServicePeopleSignDialog = false)))
})
}
if (uiState.value.showServiceSignatureUploadFailedDialog == true) {
CommonDialog(cancelText = "取消",
confirmText = "离线上传",
@ -408,8 +378,8 @@ fun ConfirmEleScreen(vm : ConfirmEleVm = viewModel()) {
}
vm.dispatch(ConfirmEleVm.Action.UpdateAcceptSignature(it))
},
serverPath = uiState.value.eleWorkOrderBean?.serverAcceptCarSignPath
?: uiState.value.eleWorkOrderBean?.localAcceptCarSignPath)
serverPath = uiState.value.eleWorkOrderBean?.localAcceptCarSignPath
?: uiState.value.eleWorkOrderBean?.serverAcceptCarSignPath)
}
}
@ -442,15 +412,9 @@ fun ConfirmEleScreen(vm : ConfirmEleVm = viewModel()) {
return@SignatureView
}
vm.dispatch(ConfirmEleVm.Action.UploadServiceSignature(it))
if (! FileUtils.isFileExists(File(AppFileManager.getDriverSignPath(
context)))
) {
vm.updateState(uiState.value.copy(showServicePeopleSignDialog = true))
}
},
serverPath = uiState.value.eleWorkOrderBean?.serverServicePeopleSignPath
?: uiState.value.eleWorkOrderBean?.localServicePeopleSignPath)
serverPath = uiState.value.eleWorkOrderBean?.localServicePeopleSignPath
?: uiState.value.eleWorkOrderBean?.serverServicePeopleSignPath)
}
}
}

View File

@ -3,8 +3,6 @@ package com.za.ui.servicing.order_confirm
import androidx.lifecycle.viewModelScope
import com.amap.api.location.AMapLocation
import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.NetworkUtils
import com.blankj.utilcode.util.ToastUtils
import com.za.base.Const
@ -17,7 +15,6 @@ import com.za.bean.request.SaveEleOrderRequest
import com.za.bean.request.UpdateTaskBean
import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.util.AppFileManager
import com.za.ext.toJson
import com.za.net.BaseObserver
import com.za.net.CommonMethod
@ -186,7 +183,8 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
} else null,
hasSuccess = eleWorkOrderBean.isSuccess,
recipientSignPath = eleWorkOrderBean.serverAcceptCarSignPath,
waitstaffSignPath = eleWorkOrderBean.serverServicePeopleSignPath,
waitstaffSignPath = eleWorkOrderBean.serverServicePeopleSignPath
?: GlobalData.driverInfoBean?.signatureUrl,
eleState = 3,
userOrderId = getCurrentOrder()?.userOrderId)
insertOfflineTask(offlineUpdateTaskBean)
@ -206,7 +204,8 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
} else null,
hasSuccess = eleWorkOrderBean.isSuccess,
recipientSignPath = eleWorkOrderBean.serverAcceptCarSignPath,
waitstaffSignPath = eleWorkOrderBean.serverServicePeopleSignPath,
waitstaffSignPath = eleWorkOrderBean.serverServicePeopleSignPath
?: GlobalData.driverInfoBean?.signatureUrl,
eleState = 3,
userOrderId = getCurrentOrder()?.userOrderId)
insertOfflineTask(offlineUpdateTaskBean)
@ -223,11 +222,12 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
return
}
if (getCurrentOrder()?.flowType != 2 && eleWorkOrderBean.isSuccess == null) {
if (getCurrentOrder()?.flowType != Const.TUO_CHE && eleWorkOrderBean.isSuccess == null) {
showTipDialog("请选择服务是否成功!")
return
}
if (uiState.value.isAddSmallWheel == true && uiState.value.wheelNum == null || uiState.value.wheelNum == 0) {
showTipDialog("请输入辅助轮个数")
return
@ -243,11 +243,16 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
return
}
if (eleWorkOrderBean.localAcceptCarSignPath.isNullOrBlank() || eleWorkOrderBean.localServicePeopleSignPath.isNullOrBlank()) {
if (eleWorkOrderBean.localAcceptCarSignPath.isNullOrBlank()) {
showTipDialog("请先上传签名!")
return
}
if (eleWorkOrderBean.serverServicePeopleSignPath.isNullOrBlank() && eleWorkOrderBean.localServicePeopleSignPath.isNullOrBlank()) {
showTipDialog("请上传服务人员签名!")
return
}
LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(success = {
LoadingManager.hideLoading()
@ -314,14 +319,7 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
orderInfo = getCurrentOrder()))
LogUtil.print("电子表单更新车辆损伤照片", "eleWorkOrderBean==${photoList.toJson()}")
if (! it.localServicePeopleSignPath.isNullOrBlank() || ! it.serverServicePeopleSignPath.isNullOrBlank()) {
return@queryElectronOrder
}
if (FileUtils.isFileExists(File(AppFileManager.getDriverSignPath(context = ActivityUtils.getTopActivity())))) {
updateServiceSignature(AppFileManager.getDriverSignPath(context = ActivityUtils.getTopActivity()))
}
LogUtil.print("电子表单", "eleWorkOrderBean==${it.toJson()}")
}, failed = {
LoadingManager.hideLoading()
@ -354,6 +352,5 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
val showOfflineDialog : Boolean? = null,
val showAcceptCarSignUploadFailedDialog : Boolean? = null,
val showServiceSignatureUploadFailedDialog : Boolean? = null,
val showServicePeopleSignDialog : Boolean? = null, //服务人员签名弹窗
)
}

View File

@ -21,9 +21,12 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@ -35,7 +38,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -44,6 +49,7 @@ import coil.compose.AsyncImage
import com.blankj.utilcode.util.ConvertUtils
import com.blankj.utilcode.util.ToastUtils
import com.za.base.BaseActivity
import com.za.base.theme.bgColor
import com.za.base.theme.black65
import com.za.base.theme.buttonBgColor
import com.za.base.view.CommonDialog
@ -195,6 +201,9 @@ fun VerifyOrderScreen(vm : VerifyOrderVm = viewModel()) {
VerifyCarView(orderInfo = uiState.value.orderInfo,
newCarVin = uiState.value.newCarVin,
newCarVinPath = uiState.value.newCarPhotoPath,
newCarChanged = {
vm.updateState(uiState.value.copy(newCarVin = it))
},
recognize = {
vm.dispatch(VerifyOrderVm.Action.Recognize(it))
})
@ -237,6 +246,7 @@ fun VerifyOrderScreen(vm : VerifyOrderVm = viewModel()) {
private fun VerifyCarView(
orderInfo : OrderInfo?,
newCarVin : String? = null,
newCarChanged : (String?) -> Unit,
newCarVinPath : String? = null,
recognize : (String) -> Unit,
) {
@ -298,10 +308,19 @@ private fun VerifyCarView(
fontSize = 13.sp,
fontWeight = FontWeight.Medium)
Spacer(modifier = Modifier.width(10.dp))
Text(text = newCarVin ?: "",
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
color = Color.Black)
TextField(value = newCarVin ?: "",
onValueChange = { newCarChanged(it) },
modifier = Modifier.background(color = bgColor,
shape = RoundedCornerShape(4.dp)),
colors = TextFieldDefaults.colors()
.copy(focusedContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Text),
textStyle = TextStyle.Default.copy(fontSize = 16.sp,
color = Color.Black,
fontWeight = FontWeight.Medium))
}
Spacer(modifier = Modifier.height(10.dp))

View File

@ -5,6 +5,7 @@ import com.blankj.utilcode.util.ToastUtils
import com.za.base.Const
import com.za.base.IServicingVm
import com.za.base.view.LoadingManager
import com.za.bean.UnifiedOCRWithCompressRequest
import com.za.bean.db.order.OrderInfo
import com.za.bean.request.OrderPhotoOcrRecognizeRequest
import com.za.bean.request.UpdateTaskBean
@ -45,7 +46,7 @@ class VerifyOrderVm : IServicingVm<VerifyOrderVm.Action, VerifyOrderVm.UiState>(
LoadingManager.showLoading()
CommonMethod.uploadImage(file = File(localPath), success = {
LoadingManager.hideLoading()
if (it.isNullOrBlank() == true) {
if (it.isNullOrBlank()) {
return@uploadImage
}
doRecognize(it)
@ -60,7 +61,8 @@ class VerifyOrderVm : IServicingVm<VerifyOrderVm.Action, VerifyOrderVm.UiState>(
val orderPhotoOcrRecognizeRequest =
OrderPhotoOcrRecognizeRequest(GlobalData.currentOrder?.userOrderId, 2, uploadPath)
LogUtil.print("$tag doRecognize", "请求参数==${orderPhotoOcrRecognizeRequest.toJson()}")
RetrofitHelper.getDefaultService().orderPhotoOcrRecognize(orderPhotoOcrRecognizeRequest)
val unifiedOCRWithCompressRequest= UnifiedOCRWithCompressRequest(ocrType = 4, imageUrl = uploadPath)
RetrofitHelper.getDefaultService().unifiedOCRWithCompress(unifiedOCRWithCompressRequest)
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : String?) {

View File

@ -13,12 +13,13 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowLeft
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -27,6 +28,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@ -40,7 +42,6 @@ import com.za.base.view.CommonDialog
import com.za.bean.db.order.OrderInfo
import com.za.common.util.MapUtil
import com.za.ext.callPhone
import com.za.ext.finish
import com.za.servicing.R
import com.za.ui.servicing.in_servicing_setting.OrderRequirementsActivity
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
@ -207,23 +208,25 @@ fun InServicingHeadView(title : String,
}
}
Column {
CenterAlignedTopAppBar(modifier = Modifier.fillMaxWidth(),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors()
.copy(containerColor = headBgColor, titleContentColor = Color.White),
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
navigationIcon = {
Column {
Box(modifier = Modifier
.fillMaxWidth()
.background(color = headBgColor)
.padding(horizontal = 10.dp, vertical = 5.dp), contentAlignment = Alignment.Center) {
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween) {
if (isCanBack) {
AsyncImage(model = R.drawable.sv_back,
Icon(Icons.Default.KeyboardArrowLeft,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clickable { onBack() }
.padding(10.dp))
.padding(5.dp),
tint = Color.White)
}
},
actions = {
Box(modifier = Modifier
.size(46.dp)
.clickable {
@ -236,7 +239,16 @@ fun InServicingHeadView(title : String,
contentDescription = "",
modifier = Modifier.fillMaxSize())
}
})
}
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(text = title,
fontSize = 16.sp,
color = Color.White.copy(alpha = 0.95f),
fontWeight = FontWeight.Medium,
style = TextStyle.Default.copy())
}
}
AppTipsView()
}

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp"
android:height="128dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M664.9,359.1a35.2,35.2 0,0 1,8.5 36l-64,192a35.2,35.2 0,0 1,-22.3 22.3l-192,64a35.2,35.2 0,0 1,-44.5 -44.5l64,-192a35.2,35.2 0,0 1,22.3 -22.3l192,-64a35.2,35.2 0,0 1,36 8.5zM475.8,475.8L439.7,584.3l108.5,-36.2 36.2,-108.5 -108.5,36.2z"
android:fillColor="#ffffff"/>
<path
android:pathData="M512,819.2a307.2,307.2 0,1 0,0 -614.4,307.2 307.2,0 0,0 0,614.4zM896,512A384,384 0,1 1,128 512a384,384 0,0 1,768 0z"
android:fillColor="#ffffff"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB