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

260
app/proguard-rules.pro vendored
View File

@ -18,4 +18,262 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-dontwarn java.lang.invoke.StringConcatFactory
# 保留行号用于调试
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
# 保留基本属性
-keepattributes Signature
-keepattributes *Annotation*
-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.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.** { *; }
#
## 保留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 {
public static final android.os.Parcelable$Creator *;
}
# 保留Serializable实现类
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 保留WebView相关
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String);
}
# 保留native方法
-keepclasseswithmembernames class * {
native <methods>;
}
## 保留View的getter和setter
#-keepclassmembers public class * extends android.view.View {
# void set*(***); *** get*();
#}
# CameraX相关
#-keep class androidx.camera.** { *; }
#-dontwarn androidx.camera.**
#
## Coil图片加载库
#-keep class coil.** { *; }
#-dontwarn coil.**
#
## PermissionX权限库
#-keep class com.permissionx.guolindev.** { *; }
#
## 高德地图相关
#-keep class com.amap.api.**{*;}
#-keep class com.autonavi.**{*;}
#-keep class com.loc.**{*;}
#-dontwarn com.amap.api.**
#-dontwarn com.autonavi.**
#-dontwarn com.loc.**
#
## JPush相关
#-dontoptimize
#-dontwarn cn.jpush.**
#-keep class cn.jpush.** { *; }
#-dontwarn cn.jcore.**
#-keep class cn.jcore.** { *; }
#
## Retrofit网络库
#-keepattributes Signature
#-keepattributes Exceptions
#-dontwarn retrofit2.**
#-keep class retrofit2.** { *; }
#-keepclasseswithmembers class * {
# @retrofit2.http.* <methods>;
#}
#
## OkHttp
#-dontwarn okhttp3.**
#-dontwarn okio.**
#-keep class okhttp3.** { *; }
#-keep class okio.** { *; }
#
## RxJava
#-dontwarn io.reactivex.**
#-keep class io.reactivex.** { *; }
#-dontwarn sun.misc.**
#-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
# long producerIndex;
# long consumerIndex;
#}
#
## 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 *
#
##mmkv
#-renamesourcefileattribute SourceFile
#-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod
#-keep public class * {
# public protected *;
#}
##
### 保留所有类的方法名
##-keepclassmembernames class * {
## java.lang.Class class$(java.lang.String);
## java.lang.Class class$(java.lang.String, boolean);
##}
#
## 保留枚举类中的特殊静态方法
#-keepclassmembers class * extends java.lang.Enum {
# public static **[] values();
# public static ** valueOf(java.lang.String);
#}
#
## MQTT相关
#-keep class org.eclipse.paho.** { *; }
#-dontwarn org.eclipse.paho.**
#
## Gson
#-keep class com.google.gson.** { *; }
#-keepattributes Signature
#-keepattributes *Annotation*
#-keep class * implements com.google.gson.TypeAdapterFactory
#-keep class * implements com.google.gson.JsonSerializer
#-keep class * implements com.google.gson.JsonDeserializer
#
### Kotlin相关
###-keep class kotlin.** { *; }
##-keep class kotlin.Metadata { *; }
##-dontwarn kotlin.**
##-keepclassmembers class **$WhenMappings {
## <fields>;
##}
##-keepclassmembers class kotlin.Metadata {
## public <methods>;
##}
#
## Kotlin协程
##-keepclassmembernames class kotlinx.** {
## volatile <fields>;
##}
##-keepclassmembers class * {
## @kotlin.coroutines.jvm.internal.DebugMetadata *;
## @kotlin.coroutines.jvm.internal.SuspendLambda *;
##}
##-keep class kotlinx.coroutines.** { *; }
##-dontwarn kotlinx.coroutines.**
#
## Kotlin反射
##-keep class kotlin.reflect.** { *; }
##-dontwarn kotlin.reflect.**
#
## 保留Kotlin数据类
##-keepclassmembers class ** {
## public ** component*();
## public ** copy(...);
##}
#
## 保留Kotlin数据类的toString/hashCode/equals方法
##-keepclassmembers class ** {
## public java.lang.String toString();
## public int hashCode();
## 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

@ -1,23 +0,0 @@
package com.za.sdk.demo
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() { // Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.za.sdk.demo", appContext.packageName)
}
}

View File

@ -1,4 +1,5 @@
[versions] [versions]
accompanistPager = "0.32.0"
cameraCore = "1.4.1" cameraCore = "1.4.1"
coilCompose = "2.6.0" coilCompose = "2.6.0"
commonsCompress = "1.23.0" commonsCompress = "1.23.0"
@ -6,15 +7,13 @@ compiler = "4.14.2"
converterGson = "2.9.0" converterGson = "2.9.0"
core = "3.5.3" core = "3.5.3"
crashreport = "4.0.4" crashreport = "4.0.4"
faceDetection = "16.1.7"
fastjson = "1.2.69" fastjson = "1.2.69"
glide = "4.16.0" glide = "4.16.0"
gson = "2.11.0" gson = "2.11.0"
jcore = "5.0.0"
faceDetection = "16.1.7"
jpush = "5.6.0" jpush = "5.6.0"
location = "5.6.1" location = "5.6.1"
loggingInterceptor = "4.11.0" loggingInterceptor = "4.11.0"
#这是一个长期维护的版本,不建议升级
mmkv = "1.3.11" mmkv = "1.3.11"
orgEclipsePahoAndroidService = "1.1.1" orgEclipsePahoAndroidService = "1.1.1"
orgEclipsePahoClientMqttv3 = "1.2.5" orgEclipsePahoClientMqttv3 = "1.2.5"
@ -39,14 +38,19 @@ rxjava = "3.1.7"
search = "7.3.0" search = "7.3.0"
tbssdk = "44286" tbssdk = "44286"
uiGraphics = "1.7.7" uiGraphics = "1.7.7"
uiTooling = "1.7.7"
uiToolingPreview = "1.7.7" uiToolingPreview = "1.7.7"
uiVersion = "1.7.7" uiVersion = "1.7.7"
utilcodex = "1.31.1" utilcodex = "1.31.1"
workRuntimeKtx = "2.10.0" workRuntimeKtx = "2.10.0"
xdmap = "8.1.0" xdmap = "8.1.0"
activity = "1.10.0"
xz = "1.9" xz = "1.9"
exifinterface = "1.3.7" exifinterface = "1.3.7"
uiToolingVersion = "1.4.0"
[libraries] [libraries]
accompanist-pager = { module = "com.google.accompanist:accompanist-pager", version.ref = "accompanistPager" }
accompanist-pager-indicators = { module = "com.google.accompanist:accompanist-pager-indicators", version.ref = "accompanistPager" }
adapter-rxjava3 = { module = "com.squareup.retrofit2:adapter-rxjava3", version.ref = "converterGson" } adapter-rxjava3 = { module = "com.squareup.retrofit2:adapter-rxjava3", version.ref = "converterGson" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraCore" } androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraCore" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "cameraCore" } androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "cameraCore" }
@ -60,6 +64,7 @@ androidx-ui = { module = "androidx.compose.ui:ui", version.ref = "uiVersion" }
androidx-ui-graphics = { module = "androidx.compose.ui:ui-graphics", version.ref = "uiGraphics" } androidx-ui-graphics = { module = "androidx.compose.ui:ui-graphics", version.ref = "uiGraphics" }
androidx-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "uiGraphics" } androidx-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "uiGraphics" }
androidx-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "uiGraphics" } androidx-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "uiGraphics" }
androidx-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "uiTooling" }
androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "uiToolingPreview" } androidx-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "uiToolingPreview" }
androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" } coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
@ -69,6 +74,7 @@ compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "compi
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
core = { module = "com.google.zxing:core", version.ref = "core" } core = { module = "com.google.zxing:core", version.ref = "core" }
crashreport = { module = "com.tencent.bugly:crashreport", version.ref = "crashreport" } crashreport = { module = "com.tencent.bugly:crashreport", version.ref = "crashreport" }
face-detection = { module = "com.google.mlkit:face-detection", version.ref = "faceDetection" }
fastjson = { module = "com.alibaba:fastjson", version.ref = "fastjson" } fastjson = { module = "com.alibaba:fastjson", version.ref = "fastjson" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" } gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
@ -96,12 +102,13 @@ search = { module = "com.amap.api:search", version.ref = "search" }
tbssdk = { module = "com.tencent.tbs:tbssdk", version.ref = "tbssdk" } tbssdk = { module = "com.tencent.tbs:tbssdk", version.ref = "tbssdk" }
utilcodex = { module = "com.blankj:utilcodex", version.ref = "utilcodex" } utilcodex = { module = "com.blankj:utilcodex", version.ref = "utilcodex" }
xdmap = { module = "com.amap.api:3dmap", version.ref = "xdmap" } xdmap = { module = "com.amap.api:3dmap", version.ref = "xdmap" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
xz = { module = "org.tukaani:xz", version.ref = "xz" } xz = { module = "org.tukaani:xz", version.ref = "xz" }
androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" } androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" }
face-detection = { module = "com.google.mlkit:face-detection", version.ref = "faceDetection" } ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiToolingVersion" }
ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiToolingPreview" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
android-library = { id = "com.android.library", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

View File

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

View File

@ -1,3 +1,5 @@
-dontwarn java.lang.invoke.StringConcatFactory
# 保留行号用于调试 # 保留行号用于调试
-keepattributes SourceFile,LineNumberTable -keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile -renamesourcefileattribute SourceFile
@ -8,19 +10,42 @@
-keepattributes Exceptions -keepattributes Exceptions
-keepattributes InnerClasses -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文件 # 保留R文件
-keepclassmembers class **.R$* { -keepclassmembers class **.R$* {
public static <fields>; public static <fields>;
} }
# 保留servicing模块中的所有model类 # 保留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模块中的所有接口 # 保留servicing模块中的所有接口
-keep interface com.za.servicing.** { *; } -keep interface com.za.servicing.** { *; }
-keep interface com.za.net.** { *; }
# 保留servicing模块中的所有枚举 # 保留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实现类 # 保留Parcelable实现类
-keep class * implements android.os.Parcelable { -keep class * implements android.os.Parcelable {
@ -58,6 +83,10 @@
void set*(***); *** get*(); void set*(***); *** get*();
} }
# CameraX相关
-keep class androidx.camera.** { *; }
-dontwarn androidx.camera.**
# Coil图片加载库 # Coil图片加载库
-keep class coil.** { *; } -keep class coil.** { *; }
-dontwarn coil.** -dontwarn coil.**
@ -75,7 +104,6 @@
# JPush相关 # JPush相关
-dontoptimize -dontoptimize
-dontpreverify
-dontwarn cn.jpush.** -dontwarn cn.jpush.**
-keep class cn.jpush.** { *; } -keep class cn.jpush.** { *; }
-dontwarn cn.jcore.** -dontwarn cn.jcore.**
@ -105,13 +133,12 @@
long consumerIndex; long consumerIndex;
} }
#blankJ # BlankJ工具库
-dontwarn com.blankj.utilcode.** -dontwarn com.blankj.utilcode.**
-keep class com.blankj.utilcode.** { *; }
-keepclassmembers class * { -keepclassmembers class * {
@com.blankj.utilcode.util.BusUtils$Bus <methods>; @com.blankj.utilcode.util.BusUtils$Bus <methods>;
} }
-keep public class * extends com.blankj.utilcode.util.ApiUtils$BaseApi -keep public class * extends com.blankj.utilcode.util.ApiUtils$BaseApi
-keep,allowobfuscation @interface com.blankj.utilcode.util.ApiUtils$Api -keep,allowobfuscation @interface com.blankj.utilcode.util.ApiUtils$Api
-keep @com.blankj.utilcode.util.ApiUtils$Api class * -keep @com.blankj.utilcode.util.ApiUtils$Api class *
@ -122,41 +149,27 @@
-keep public class * { -keep public class * {
public protected *; public protected *;
} }
# Preserve all .class method names.
# 保留所有类的方法名
-keepclassmembernames class * { -keepclassmembernames class * {
java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String);
java.lang.Class class$(java.lang.String, boolean); 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 { -keepclassmembers class * extends java.lang.Enum {
public static **[] values(); public static **[] values();
public static ** valueOf(java.lang.String); public static ** valueOf(java.lang.String);
} }
-keepclassmembers class * implements java.io.Serializable { # MQTT相关
static final long serialVersionUID; -keep class org.eclipse.paho.** { *; }
static final java.io.ObjectStreamField[] serialPersistentFields; -dontwarn org.eclipse.paho.**
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# Gson # Gson
-keep class com.google.gson.** { *; } -keep class com.google.gson.** { *; }
-keepattributes Signature -keepattributes Signature
-keepattributes *Annotation* -keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class * implements com.google.gson.TypeAdapterFactory -keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer -keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer -keep class * implements com.google.gson.JsonDeserializer
@ -192,7 +205,7 @@
public ** component*(); public ** component*();
public ** copy(...); public ** copy(...);
} }
#
# 保留Kotlin数据类的toString/hashCode/equals方法 # 保留Kotlin数据类的toString/hashCode/equals方法
-keepclassmembers class ** { -keepclassmembers class ** {
public java.lang.String toString(); public java.lang.String toString();
@ -200,4 +213,45 @@
public boolean equals(java.lang.Object); 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 -dontwarn java.lang.invoke.StringConcatFactory

View File

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

View File

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

View File

@ -39,6 +39,12 @@ open class PushMessageActivity : AppCompatActivity() {
context = this@PushMessageActivity) 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) { override fun newOrderMsg(jpushBean : JpushBean) {
sendMessageToMainProcess(type = Const.PushMessageType.NEW_ORDER, sendMessageToMainProcess(type = Const.PushMessageType.NEW_ORDER,
message = Gson().toJson(jpushBean), message = Gson().toJson(jpushBean),

View File

@ -15,7 +15,10 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb 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.platform.LocalView
import androidx.compose.ui.unit.Density
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat 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) { MaterialTheme(colorScheme = colorScheme) {
CompositionLocalProvider(LocalRippleConfiguration provides RippleConfiguration(rippleAlpha = RippleAlpha( CompositionLocalProvider(LocalRippleConfiguration provides RippleConfiguration(rippleAlpha = RippleAlpha(
0f, 0f,
0f, 0f,
0f, 0f,
0f))) { 0f)),
LocalDensity provides Density(fontScale = 1f, density = widthPixels / 360f)) {
ProvideTextStyle(value = MaterialTheme.typography.bodyLarge, content = content) ProvideTextStyle(value = MaterialTheme.typography.bodyLarge, content = content)
} }
} }

View File

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

View File

@ -31,4 +31,14 @@ data class TaskNotesBean(val taskNotes : String? = null, //救援要求
val customerNotes : String? = null, //客户提醒 val customerNotes : String? = null, //客户提醒
val otherNotes : String? = null, //特殊提醒 val otherNotes : String? = null, //特殊提醒
val modelVinNo : 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 numbering : String? = null, // 图片编号
val recognizeType : Int? = null, //orc 识别类型 0 无 1 车牌号 2 车架号 val recognizeType : Int? = null, //orc 识别类型 0 无 1 车牌号 2 车架号
//以下属性非后台返回属性 //以下属性非后台返回属性
val serviceTypeName : String? = null, //服务类型名称
val userOrderId : Int? = null, val userOrderId : Int? = null,
val taskCode : String? = null, val taskCode : String? = null,
val taskId : Int? = 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.AppConfig
import com.za.base.Const import com.za.base.Const
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.common.log.LogUtil
import com.za.room.RoomHelper import com.za.room.RoomHelper
import com.za.room.db.user.DriverInfoBean import com.za.room.db.user.DriverInfoBean
import com.za.service.location.ZdLocationManager import com.za.service.location.ZdLocationManager
@ -49,14 +50,26 @@ object GlobalData : GlobalLocalData() {
var driverInfoBean : DriverInfoBean? = null var driverInfoBean : DriverInfoBean? = null
get() { get() {
if (field == null) { return try {
field = localDriverInfoBean 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) { set(value) {
localDriverInfoBean = value try {
field = value 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) 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() { fun clearUserCache() {
token = null token = null
aesKey = null aesKey = null
@ -147,6 +199,10 @@ object GlobalData : GlobalLocalData() {
loginTime = null loginTime = null
isLoginRecognition = null isLoginRecognition = null
isHasShowKeepAlive = false isHasShowKeepAlive = false
lastUploadLogTime = 0
deviceId = null
vehicleId = null
driverId = null
if (AppConfig.isRelease) { if (AppConfig.isRelease) {
networkEnv = if (AppConfig.isRelease) { networkEnv = if (AppConfig.isRelease) {

View File

@ -2,69 +2,72 @@ package com.za.common.log
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.os.Build
import android.util.Log import android.util.Log
import androidx.work.Configuration import androidx.work.Configuration
import androidx.work.PeriodicWorkRequest import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.Worker import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.blankj.utilcode.constant.MemoryConstants
import com.blankj.utilcode.util.AppUtils import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.DeviceUtils
import com.blankj.utilcode.util.FileUtils import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.TimeUtils import com.blankj.utilcode.util.TimeUtils
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.util.AppFileManager
import io.reactivex.rxjava3.schedulers.Schedulers 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.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.asRequestBody
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream
import java.io.BufferedWriter
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.FileWriter
import java.io.IOException import java.io.IOException
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter 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.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.concurrent.thread
object LogUtil { object LogUtil {
private var context : Application? = null private var context : Application? = null
private var logDestinationPath : String? = null private var executors : ExecutorService? = null
private var orderLogDirPath : String? = null
private var normalLogDirPath : String? = 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) { fun init(context : Application) {
this.context = context this.context = context
executors = Executors.newSingleThreadExecutor()
logDestinationPath = AppFileManager.getLogPath(context).also { path -> val logDestinationPath : String = context.filesDir.absolutePath + File.separator + "Log"
createDirectoryIfNotExists(path) val logDesFile = File(logDestinationPath)
orderLogDirPath = if (! logDesFile.exists()) {
"$path${File.separator}order_log".also { createDirectoryIfNotExists(it) } logDesFile.mkdir()
normalLogDirPath =
"$path${File.separator}normal_log".also { createDirectoryIfNotExists(it) }
} }
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) initializeWorkManager(context)
} }
private fun createDirectoryIfNotExists(path : String) {
File(path).apply { if (! exists()) mkdir() }
}
private fun initializeWorkManager(context : Application) { private fun initializeWorkManager(context : Application) {
if (! WorkManager.isInitialized()) { if (! WorkManager.isInitialized()) {
WorkManager.initialize(context, WorkManager.initialize(context,
@ -73,183 +76,157 @@ object LogUtil {
WorkManager.getInstance(context).apply { WorkManager.getInstance(context).apply {
cancelAllWorkByTag("logWorkRequest") cancelAllWorkByTag("logWorkRequest")
enqueue(PeriodicWorkRequest.Builder(LogTask::class.java, 20, TimeUnit.MINUTES) enqueue(PeriodicWorkRequest.Builder(LogTask::class.java, 80, TimeUnit.MINUTES)
.addTag("logWorkRequest").build()) .addTag("logWorkRequest").build())
} }
} }
fun print(tag : String, content : String) { fun print(tag : String?, throwable : Throwable?) {
val time = getCurrentTime() if (throwable == null) {
val logEntry = "$time---$tag---$content\n" return
Log.e("normal", "$tag---$content")
synchronized(logBuffer) {
logBuffer.append(logEntry)
} }
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 { try {
val fileName = "normal_log.txt" val stringWriter = StringWriter()
val logFile = File("$normalLogDirPath${File.separator}$fileName") val printWriter = PrintWriter(stringWriter)
throwable.printStackTrace(printWriter)
logFile.parentFile?.mkdirs() if (System.currentTimeMillis() - GlobalData.lastUploadLogTime > uploadTimeInterval) {
updateNormalLog()
if (! logFile.exists()) {
logFile.createNewFile()
addLogHead(logFile, getCurrentTime())
} }
LogUtils.getConfig().setFilePrefix(this.vehicleName)
BufferedWriter(FileWriter(logFile, true)).use { writer -> LogUtils.e("$tag---$stringWriter")
writer.write(logContent)
writer.flush()
}
if (logFile.length() >= 8 * MemoryConstants.MB) {
rotateLogFile(logFile)
}
} catch (e : IOException) {
Log.e("LogUtil", "Error in flushBuffer: ${e.message}")
} catch (e : Exception) { } catch (e : Exception) {
Log.e("LogUtil", "Error in flushBuffer: ${e.message}") e.printStackTrace()
} finally {
isWriting.set(false)
} }
} }
private fun rotateLogFile(file : File) { fun print(tag : String?, content : String?) {
if (! file.exists()) return if (content == null || content.isEmpty()) {
return
val newFileName = buildString { }
append(AppUtils.getAppVersionCode()) if (GlobalData.driverInfoBean == null || GlobalData.driverInfoBean?.vehicleName.isNullOrBlank()) {
append("_") Log.e(tag, content)
append(GlobalData.driverInfoBean?.vehicleName ?: "unknown") return
append("_")
append(GlobalData.driverInfoBean?.userName ?: "unknown")
append("_")
append(TimeUtils.getNowString())
append(".txt")
} }
val newFile = File("$normalLogDirPath${File.separator}$newFileName")
try { try {
if (file.renameTo(newFile)) { if (System.currentTimeMillis() - GlobalData.lastUploadLogTime > uploadTimeInterval) {
compressAndUploadLog(newFile) // 创建新的日志文件 updateNormalLog()
file.createNewFile()
addLogHead(file, getCurrentTime())
} else {
print("LogUtil", "Failed to rename log file")
} }
LogUtils.getConfig().setFilePrefix(this.vehicleName)
LogUtils.e("$tag---$content")
} catch (e : Exception) { } catch (e : Exception) {
print("LogUtil", "Error during log rotation: ${e.message}") e.printStackTrace()
} }
} }
private fun compressAndUploadLog(logFile : File) = coroutineScope.launch { private val vehicleName : String
try { get() {
val compressedFile = File("${logFile.absolutePath}.7z") var name : String? = "未知"
compress(logFile, compressedFile.absolutePath) if (! GlobalData.driverInfoBean?.vehicleName.isNullOrBlank()) {
upload(logFile, compressedFile) try {
} catch (e : Exception) { name = buildString {
print("LogUtil", e.toString()) append(GlobalData.driverInfoBean?.vehicleName?.replace("/", "")
?.replace(" ", ""))
append("_")
append(GlobalData.driverInfoBean?.userName ?: "未知")
}
} catch (e : Exception) {
e.printStackTrace()
}
}
return name ?: "未知"
} }
}
private fun deleteLog(file : File) { private fun deleteLog(file : File) {
try { try {
FileUtils.delete(file.absolutePath) file.delete()
} catch (e : Exception) { } catch (e : Exception) {
e.printStackTrace() e.printStackTrace()
} }
} }
fun updateNormalLog() { fun updateNormalLog() {
thread { GlobalData.lastUploadLogTime = System.currentTimeMillis()
if (GlobalData.token.isNullOrBlank()) { val files = File(normalLogDirPath).listFiles()
return@thread if (files.isNullOrEmpty()) {
} return
val fileName = "normal_log.txt" }
val file = File("$normalLogDirPath${File.separator}$fileName") for (file in files) {
val reName = try {
"${AppUtils.getAppVersionCode()}_${GlobalData.driverInfoBean?.vehicleName}_${GlobalData.driverInfoBean?.userName}_${TimeUtils.getNowString()}.txt" if (file.isDirectory()) {
val reNamePath = "$normalLogDirPath${File.separator}$reName" file.delete()
file.renameTo(File(reNamePath)) FileUtils.deleteAllInDir(file)
normalLogDirPath?.let { it -> } else if (file.getName().endsWith("7z")) {
File(it).listFiles()?.forEach { uploadFile(null, File(file.absolutePath))
if (it.length() / MemoryConstants.MB >= 10) { } else {
deleteLog(it) executors?.submit {
return@thread val vehicleName = vehicleName
} val zipNamePath =
if (it.exists() && ! it.name.contains("normal_log")) { normalLogDirPath + File.separator + AppUtils.getAppVersionCode() + "_" + vehicleName + "_" + this.currentTime + ".txt.7z"
if (it.name.contains("7z")) { val zipFile = File(zipNamePath)
upload(null, desFile = it) if (! zipFile.exists()) {
} else { try {
val zipNamePath = it.absolutePath + ".7z" zipFile.createNewFile()
val zipFile = File(zipNamePath) compress(file, zipNamePath)
if (! zipFile.exists()) { } catch (e : IOException) {
try { e.printStackTrace()
zipFile.createNewFile()
} catch (e : IOException) {
e.printStackTrace()
}
} }
compress(it, zipNamePath)
} }
} }
} }
} catch (e : Exception) {
e.printStackTrace()
} }
} }
} }
private fun compress(srcFile : File, desFilePath : String) { private fun compress(srcFile : File, desFilePath : String) {
try { 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)) addToArchiveCompression(out, srcFile, File(desFilePath))
} catch (e : Exception) { } catch (e : Exception) {
e.printStackTrace() 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, private fun addToArchiveCompression(sevenZOutputFile : XZCompressorOutputStream,
srcFile : File, srcFile : File,
desFile : File) { desFile : File) {
if (srcFile.isFile) { if (srcFile.isFile()) {
var inputStream : FileInputStream? = null var inputStream : FileInputStream? = null
try { try {
inputStream = FileInputStream(srcFile) inputStream = FileInputStream(srcFile)
val b = ByteArray(2048) val b = ByteArray(2048)
var count : Int 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.write(b, 0, count)
} }
sevenZOutputFile.close() sevenZOutputFile.close()
inputStream.close() inputStream.close()
upload(srcFile, desFile) uploadFile(srcFile, desFile)
} catch (e : Exception) { } catch (e : Exception) {
e.printStackTrace() e.printStackTrace()
} finally { } finally {
@ -263,42 +240,8 @@ object LogUtil {
} }
} }
private fun upload(srcFile : File?, desFile : File) { private val currentTime : String?
val requestBody : RequestBody = desFile.asRequestBody("multipart/form-data".toMediaType()) get() = TimeUtils.millis2String(System.currentTimeMillis(), "yyyy_MM_dd HH:mm:ss:SS")
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")
}
class LogTask(appContext : Context, workerParams : WorkerParameters) : class LogTask(appContext : Context, workerParams : WorkerParameters) :
Worker(appContext, workerParams) { Worker(appContext, workerParams) {
@ -307,4 +250,11 @@ object LogUtil {
return Result.success() return Result.success()
} }
} }
}
}

View File

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

View File

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

View File

@ -22,9 +22,11 @@ import com.za.bean.ReportHistoryBean
import com.za.bean.ReportHistoryRequest import com.za.bean.ReportHistoryRequest
import com.za.bean.ReportInfoRequest import com.za.bean.ReportInfoRequest
import com.za.bean.ReportItem import com.za.bean.ReportItem
import com.za.bean.SaveSignatureRequest
import com.za.bean.SettleInfoRequest import com.za.bean.SettleInfoRequest
import com.za.bean.TaskNotesBean import com.za.bean.TaskNotesBean
import com.za.bean.TaskSettlementAndTraceBean import com.za.bean.TaskSettlementAndTraceBean
import com.za.bean.UnifiedOCRWithCompressRequest
import com.za.bean.UpdateVersionBean import com.za.bean.UpdateVersionBean
import com.za.bean.UpdateVersionRequest import com.za.bean.UpdateVersionRequest
import com.za.bean.UploadChangeBatteryRequest import com.za.bean.UploadChangeBatteryRequest
@ -289,4 +291,12 @@ interface ApiService {
@POST("driverApp/task/getTaskNotes") @POST("driverApp/task/getTaskNotes")
fun getTaskNotes(@Body request : TaskNotesRequest) : Observable<BaseResponse<TaskNotesBean>> 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.bean.BaseResponse
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.offline.OfflineService
import com.za.service.location.ZdLocationManager import com.za.service.location.ZdLocationManager
import io.reactivex.rxjava3.core.Observer import io.reactivex.rxjava3.core.Observer
import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.disposables.Disposable
@ -128,6 +129,7 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
ToastUtils.showShort("登陆信息已过期,请重新登录") ToastUtils.showShort("登陆信息已过期,请重新登录")
ZdLocationManager.stopContinuousLocation() ZdLocationManager.stopContinuousLocation()
GlobalData.clearUserCache() GlobalData.clearUserCache()
OfflineService.stop()
ActivityUtils.startLauncherActivity() ActivityUtils.startLauncherActivity()
} catch (e : Exception) { } catch (e : Exception) {
LogUtil.print("handlerTokenExpired", e) LogUtil.print("handlerTokenExpired", e)

View File

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

View File

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

View File

@ -16,6 +16,9 @@ interface OrderDao {
@Query("select * from order_info where taskId =:taskId") @Query("select * from order_info where taskId =:taskId")
fun getOrderInfoFromTaskId(taskId: Int): OrderInfo? 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") @Query("select * from order_info where isCurrent ==1")
fun getCurrentOrder(): OrderInfo? fun getCurrentOrder(): OrderInfo?

View File

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

View File

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

View File

@ -22,7 +22,9 @@ import com.za.bean.JpushBean
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.common.speech.SpeechManager import com.za.common.speech.SpeechManager
import com.za.room.RoomHelper
import com.za.servicing.R 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.servicing.order_give_up.OrderGiveUpActivity
import com.za.ui.view.CommonDialogFragment 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 // Handle broadcast messages
private fun handleBroadcast(msg : String) { private fun handleBroadcast(msg : String) {
try { try {

View File

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

View File

@ -1,67 +1,54 @@
package com.za.service.mqtt package com.za.service.mqtt
import android.os.Handler import android.annotation.SuppressLint
import android.os.Looper
import android.util.Log
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.common.util.DeviceUtil
import com.za.common.util.Tools.macSignature
import com.za.service.ServiceManager import com.za.service.ServiceManager
import kotlinx.coroutines.CoroutineScope import org.eclipse.paho.android.service.MqttAndroidClient
import kotlinx.coroutines.Dispatchers import org.eclipse.paho.client.mqttv3.IMqttActionListener
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.client.mqttv3.IMqttDeliveryToken import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended import org.eclipse.paho.client.mqttv3.IMqttToken
import org.eclipse.paho.client.mqttv3.MqttClient 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.MqttException
import org.eclipse.paho.client.mqttv3.MqttMessage import org.eclipse.paho.client.mqttv3.MqttMessage
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence
import kotlin.concurrent.Volatile
object MyMqttClient { object MyMqttClient {
private lateinit var clientId : String private lateinit var clientId : String
private lateinit var topic : String private lateinit var topic : String
private val mqttClient : MqttClient by lazy { @SuppressLint("StaticFieldLeak")
MqttClient("tcp://${MqttConfig.END_POINT}:1883", clientId, MemoryPersistence()) private var mqttClient : MqttAndroidClient? = null
}
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val _connectionState = MutableStateFlow(false)
private val connectionState : StateFlow<Boolean> = _connectionState
fun initialize(deviceId : String?) { fun initialize(deviceId : String?) {
clientId = "${MqttConfig.GROUP_ID}@@@$deviceId" clientId = "${MqttConfig.GROUP_ID}@@@$deviceId"
topic = "${MqttConfig.TOPIC_PREFIX}/$clientId" topic = "${MqttConfig.TOPIC_PREFIX}/$clientId"
mqttClient = MqttAndroidClient(GlobalData.application,
"tcp://${MqttConfig.END_POINT}:1883",
clientId,
MemoryPersistence())
setupMqttCallbacks() setupMqttCallbacks()
connect() connect()
LogUtil.print("MyMqttClient ", "initialize success") LogUtil.print("MyMqttClient ", "initialize success")
Log.e("MyMqttClient ", "initialize success")
} }
private fun setupMqttCallbacks() { private fun setupMqttCallbacks() {
mqttClient.setCallback(object : MqttCallbackExtended { mqttClient?.setCallback(object : MqttCallback {
override fun connectComplete(reconnect : Boolean, serverURI : String) {
val status = if (reconnect) "Reconnected" else "Connected"
LogUtil.print("MyMqttClient ", "$status to: $serverURI")
_connectionState.value = true
subscribeTopic()
}
override fun connectionLost(throwable : Throwable) { override fun connectionLost(throwable : Throwable) {
LogUtil.print("MyMqttClient ", "Connection lost: ${throwable.message}") LogUtil.print("MyMqttClient ",
_connectionState.value = false "Connection lost: ${throwable.message}") // connect()
} }
override fun messageArrived(topic : String, mqttMessage : MqttMessage) { override fun messageArrived(topic : String, mqttMessage : MqttMessage) {
Handler(Looper.getMainLooper()).post { val message = String(mqttMessage.payload)
val message = String(mqttMessage.payload) LogUtil.print("MyMqttClient ", "Message arrived: $message")
LogUtil.print("MyMqttClient ", "Message arrived: $message") ServiceManager.handlerPushMsg(message)
ServiceManager.handlerPushMsg(message)
}
} }
override fun deliveryComplete(token : IMqttDeliveryToken) { override fun deliveryComplete(token : IMqttDeliveryToken) {
@ -70,64 +57,102 @@ object MyMqttClient {
}) })
} }
@Volatile
private var isConnecting : Boolean? = null
private fun connect() { private fun connect() {
if (connectionState.value && mqttClient.isConnected) { if (isConnecting == true) {
LogUtil.print("MyMqttClient ", "Already connected")
return return
} }
try {
isConnecting = true
coroutineScope.launch { val mqttConnectOption = MqttConnectOptions()
try { mqttConnectOption.userName =
val options = ConnectionOptionWrapper(MqttConfig.INSTANCE_ID, "Signature|" + MqttConfig.ACCESS_KEY + "|" + MqttConfig.INSTANCE_ID
MqttConfig.ACCESS_KEY, mqttConnectOption.password = macSignature(clientId, MqttConfig.SECRET_KEY).toCharArray()
clientId, mqttConnectOption.isCleanSession = true
MqttConfig.SECRET_KEY).mqttConnectOptions 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(mqttConnectOption, null, object : IMqttActionListener {
mqttClient.connect(options) 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) { override fun onFailure(asyncActionToken : IMqttToken?, exception : Throwable?) {
LogUtil.print("MyMqttClient ", "Connection failed: ${e.message}") isConnecting = false
_connectionState.value = false // Update connection state on failure LogUtil.print("MyMqttClient ", "connect failed== ${exception?.message}")
} }
})
} catch (e : MqttException) {
LogUtil.print("MyMqttClient ", "Connection failed2: ${e.message}")
} }
} }
//检测mqtt连接状态 //检测mqtt连接状态
fun publishMessage() { fun publishMessage() {
if (mqttClient.isConnected) {
if (mqttClient == null) {
initialize(deviceId = DeviceUtil.getAndroidId(GlobalData.application))
return
}
if (mqttClient?.isConnected == true) {
LogUtil.print("MyMqttClient ", "mqttClient.hasConnected") LogUtil.print("MyMqttClient ", "mqttClient.hasConnected")
return return
} }
connect()
LogUtil.print("MyMqttClient ", "mqttClient 断开重新初始化") LogUtil.print("MyMqttClient ", "mqttClient 断开重新初始化")
ServiceManager.initialize(GlobalData.application)
} }
private fun subscribeTopic() { private fun subscribeTopic() {
coroutineScope.launch { try {
try { if (mqttClient?.isConnected == true) {
mqttClient.subscribe(topic, MqttConfig.QOS_LEVEL) mqttClient?.subscribe(topic,
LogUtil.print("MyMqttClient ", "Subscribed to topic: $topic") MqttConfig.QOS_LEVEL,
} catch (e : MqttException) { null,
LogUtil.print("MyMqttClient ", "Subscribe failed: ${e.message}") 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() { fun disconnect() {
coroutineScope.launch { try {
try { if (mqttClient?.isConnected == true) {
if (connectionState.value) { mqttClient?.disconnect(null, object : IMqttActionListener {
mqttClient.disconnect() override fun onSuccess(asyncActionToken : IMqttToken?) {
_connectionState.value = false LogUtil.print("MyMqttClient ", "Disconnected")
LogUtil.print("MyMqttClient ", "Disconnected successfully") }
}
} catch (e : MqttException) { override fun onFailure(asyncActionToken : IMqttToken?, exception : Throwable) {
LogUtil.print("MyMqttClient ", "Disconnect failed: ${e.message}") LogUtil.print("MyMqttClient ", "Disconnect failed")
} finally { }
coroutineScope.cancel() })
} }
} 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.view.ViewGroup
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -23,6 +25,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.net.toUri
import coil.compose.AsyncImage
import com.blankj.utilcode.util.ToastUtils import com.blankj.utilcode.util.ToastUtils
import com.tencent.smtt.sdk.WebChromeClient import com.tencent.smtt.sdk.WebChromeClient
import com.tencent.smtt.sdk.WebSettings 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.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.ext.finish import com.za.ext.finish
import com.za.servicing.R
class CommonH5Activity : BaseActivity() { class CommonH5Activity : BaseActivity() {
private var webView : WebView? = null private var webView : WebView? = null
@ -45,7 +50,6 @@ class CommonH5Activity : BaseActivity() {
val url = intent.getStringExtra(EXTRA_URL) val url = intent.getStringExtra(EXTRA_URL)
val title = intent.getStringExtra(EXTRA_TITLE) val title = intent.getStringExtra(EXTRA_TITLE)
val isCanBack = intent.getBooleanExtra(EXTRA_CAN_BACK, true) val isCanBack = intent.getBooleanExtra(EXTRA_CAN_BACK, true)
if (url.isNullOrBlank()) { if (url.isNullOrBlank()) {
ToastUtils.showLong("无效的URL") ToastUtils.showLong("无效的URL")
finish() finish()
@ -165,7 +169,15 @@ private fun CommonH5Screen(url : String,
Scaffold(topBar = { Scaffold(topBar = {
if (title.isNotBlank()) { 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 -> }) { paddingValues ->
Box(modifier = Modifier Box(modifier = Modifier
@ -186,6 +198,7 @@ private fun CommonH5Screen(url : String,
webView = this webView = this
onWebViewCreated(this) onWebViewCreated(this)
loadUrl(url) loadUrl(url)
LogUtil.print("H5Activity url", url)
} }
}) })
@ -228,6 +241,7 @@ private fun setupWebViewClients(webView : WebView,
} }
override fun shouldOverrideUrlLoading(p0 : WebView?, p1 : String?) : Boolean { override fun shouldOverrideUrlLoading(p0 : WebView?, p1 : String?) : Boolean {
LogUtil.print("H5Activity url", "$p1")
p0?.loadUrl(p1) p0?.loadUrl(p1)
return false return false
} }

View File

@ -104,20 +104,34 @@ class ServicingMainActivity : BaseActivity() {
Gson().toJson(jpushBean)) Gson().toJson(jpushBean))
} }
override fun reportHandle(jpushBean : JpushBean) {
sendMessageToMainProcess(context = context,
Const.PushMessageType.REPORT_HANDLE,
Gson().toJson(jpushBean))
}
override fun newOrderMsg(jpushBean : 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) { 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) { 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) { 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 return
} }
GlobalData.token = it.token GlobalData.token = it.token
CommonMethod.getGenerateInfo(vehicleId = it.vehicleId, CommonMethod.getGenerateInfo(success = { success() },
userId = it.userId,
success = { success() },
failed = { failure(it ?: "") }) failed = { failure(it ?: "") })
} }

View File

@ -26,8 +26,12 @@ import android.widget.ImageView
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import com.blankj.utilcode.util.ActivityUtils import com.blankj.utilcode.util.ActivityUtils
import com.blankj.utilcode.util.ServiceUtils 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.common.log.LogUtil
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.h5.CommonH5Activity
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job 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") LogUtil.print("ReportFloatingManager service", "onBind")
return null return null
} }
@ -183,7 +187,7 @@ class ReportFloatingManager : Service() {
if (! isMoving && System.currentTimeMillis() - startClickTime < CLICK_THRESHOLD) { if (! isMoving && System.currentTimeMillis() - startClickTime < CLICK_THRESHOLD) {
openMainActivity() openMainActivity()
} else if (isDragging) { // 只保存位置,不执行吸附 } else if (isDragging) {
savePosition() savePosition()
} }
@ -195,9 +199,11 @@ class ReportFloatingManager : Service() {
if (ActivityUtils.getTopActivity() is OrderReportActivity) { if (ActivityUtils.getTopActivity() is OrderReportActivity) {
return 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) startActivity(intent)
} }
@ -296,6 +302,13 @@ class ReportFloatingManager : Service() {
ServiceUtils.stopService(ReportFloatingManager::class.java) 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.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -392,7 +393,11 @@ fun InServicingPhotoView(modifier : Modifier = Modifier,
Text(text = photoTemplateInfo.imageTitle ?: "其他", Text(text = photoTemplateInfo.imageTitle ?: "其他",
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
fontSize = 14.sp, 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)) Spacer(modifier = Modifier.width(5.dp))
if (photoTemplateInfo.doHaveFilm == 1) { if (photoTemplateInfo.doHaveFilm == 1) {
Text(text = "* 必拍", Text(text = "* 必拍",
@ -474,10 +479,12 @@ fun InServicingPhotoViewIsCanClick(modifier : Modifier = Modifier,
Text(text = photoTemplateInfo.imageTitle ?: "其他", Text(text = photoTemplateInfo.imageTitle ?: "其他",
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
fontSize = 14.sp, 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)) Spacer(modifier = Modifier.width(5.dp))
if (photoTemplateInfo.doHaveFilm == 1 && isCanClick) { if (photoTemplateInfo.doHaveFilm == 1 && isCanClick) {
Text(text = "* 必拍", Text(text = "* 必拍",
@ -722,7 +729,8 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
.clip(shape = RoundedCornerShape(3.dp)), .clip(shape = RoundedCornerShape(3.dp)),
contentScale = ContentScale.FillBounds) contentScale = ContentScale.FillBounds)
} else { } else {
AsyncImage(model = photoTemplateInfo.getFormatPhotoUrl(), AsyncImage(model = photoTemplateInfo.getFormatPhotoUrl()?.toIntOrNull()
?: photoTemplateInfo.getFormatPhotoUrl(),
contentDescription = "", contentDescription = "",
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -746,16 +754,16 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
contentDescription = "", contentDescription = "",
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.clickable { .noDoubleClick {
if (! isCanClick) { if (! isCanClick) {
return@clickable return@noDoubleClick
} }
if (photoTemplateInfo.photoLocalPath.isNullOrBlank()) { if (photoTemplateInfo.photoLocalPath.isNullOrBlank()) {
if (! PermissionX.isGranted(context, if (! PermissionX.isGranted(context,
android.Manifest.permission.ACCESS_FINE_LOCATION) android.Manifest.permission.ACCESS_FINE_LOCATION)
) { ) {
ToastUtils.showShort("定位权限未开启!") ToastUtils.showShort("定位权限未开启!")
return@clickable return@noDoubleClick
} }
LoadingManager.showLoading() LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(isNeedAddress = true, success = { ZdLocationManager.getSingleLocation(isNeedAddress = true, success = {
@ -771,7 +779,7 @@ fun InServicingPhotoItemView(modifier : Modifier = Modifier,
LogUtil.print("上传图片定位获取失败", LogUtil.print("上传图片定位获取失败",
"使用全局定位location==${GlobalData.currentLocation.toJson()}") "使用全局定位location==${GlobalData.currentLocation.toJson()}")
}) })
return@clickable return@noDoubleClick
} }
showReTakePhotoDialog.value = true showReTakePhotoDialog.value = true
} }

View File

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

View File

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

View File

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

View File

@ -50,7 +50,6 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.TimeUtils import com.blankj.utilcode.util.TimeUtils
import com.blankj.utilcode.util.ToastUtils import com.blankj.utilcode.util.ToastUtils
import com.za.base.Const 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.EleCarDamagePhotoBean
import com.za.bean.db.ele.EleWorkOrderBean import com.za.bean.db.ele.EleWorkOrderBean
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.util.AppFileManager
import com.za.common.util.ServicingSpeechManager import com.za.common.util.ServicingSpeechManager
import com.za.ext.finish import com.za.ext.finish
import com.za.ext.getLastSix import com.za.ext.getLastSix
import com.za.ext.goNextPage import com.za.ext.goNextPage
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.view.SignatureView import com.za.ui.view.SignatureView
import java.io.File
import kotlin.math.ceil import kotlin.math.ceil
@Preview @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) { if (uiState.value.showServiceSignatureUploadFailedDialog == true) {
CommonDialog(cancelText = "取消", CommonDialog(cancelText = "取消",
confirmText = "离线上传", confirmText = "离线上传",
@ -408,8 +378,8 @@ fun ConfirmEleScreen(vm : ConfirmEleVm = viewModel()) {
} }
vm.dispatch(ConfirmEleVm.Action.UpdateAcceptSignature(it)) vm.dispatch(ConfirmEleVm.Action.UpdateAcceptSignature(it))
}, },
serverPath = uiState.value.eleWorkOrderBean?.serverAcceptCarSignPath serverPath = uiState.value.eleWorkOrderBean?.localAcceptCarSignPath
?: uiState.value.eleWorkOrderBean?.localAcceptCarSignPath) ?: uiState.value.eleWorkOrderBean?.serverAcceptCarSignPath)
} }
} }
@ -442,15 +412,9 @@ fun ConfirmEleScreen(vm : ConfirmEleVm = viewModel()) {
return@SignatureView return@SignatureView
} }
vm.dispatch(ConfirmEleVm.Action.UploadServiceSignature(it)) 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 serverPath = uiState.value.eleWorkOrderBean?.localServicePeopleSignPath
?: 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 androidx.lifecycle.viewModelScope
import com.amap.api.location.AMapLocation 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.NetworkUtils
import com.blankj.utilcode.util.ToastUtils import com.blankj.utilcode.util.ToastUtils
import com.za.base.Const import com.za.base.Const
@ -17,7 +15,6 @@ import com.za.bean.request.SaveEleOrderRequest
import com.za.bean.request.UpdateTaskBean import com.za.bean.request.UpdateTaskBean
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.common.util.AppFileManager
import com.za.ext.toJson import com.za.ext.toJson
import com.za.net.BaseObserver import com.za.net.BaseObserver
import com.za.net.CommonMethod import com.za.net.CommonMethod
@ -186,7 +183,8 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
} else null, } else null,
hasSuccess = eleWorkOrderBean.isSuccess, hasSuccess = eleWorkOrderBean.isSuccess,
recipientSignPath = eleWorkOrderBean.serverAcceptCarSignPath, recipientSignPath = eleWorkOrderBean.serverAcceptCarSignPath,
waitstaffSignPath = eleWorkOrderBean.serverServicePeopleSignPath, waitstaffSignPath = eleWorkOrderBean.serverServicePeopleSignPath
?: GlobalData.driverInfoBean?.signatureUrl,
eleState = 3, eleState = 3,
userOrderId = getCurrentOrder()?.userOrderId) userOrderId = getCurrentOrder()?.userOrderId)
insertOfflineTask(offlineUpdateTaskBean) insertOfflineTask(offlineUpdateTaskBean)
@ -206,7 +204,8 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
} else null, } else null,
hasSuccess = eleWorkOrderBean.isSuccess, hasSuccess = eleWorkOrderBean.isSuccess,
recipientSignPath = eleWorkOrderBean.serverAcceptCarSignPath, recipientSignPath = eleWorkOrderBean.serverAcceptCarSignPath,
waitstaffSignPath = eleWorkOrderBean.serverServicePeopleSignPath, waitstaffSignPath = eleWorkOrderBean.serverServicePeopleSignPath
?: GlobalData.driverInfoBean?.signatureUrl,
eleState = 3, eleState = 3,
userOrderId = getCurrentOrder()?.userOrderId) userOrderId = getCurrentOrder()?.userOrderId)
insertOfflineTask(offlineUpdateTaskBean) insertOfflineTask(offlineUpdateTaskBean)
@ -223,11 +222,12 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
return return
} }
if (getCurrentOrder()?.flowType != 2 && eleWorkOrderBean.isSuccess == null) { if (getCurrentOrder()?.flowType != Const.TUO_CHE && eleWorkOrderBean.isSuccess == null) {
showTipDialog("请选择服务是否成功!") showTipDialog("请选择服务是否成功!")
return return
} }
if (uiState.value.isAddSmallWheel == true && uiState.value.wheelNum == null || uiState.value.wheelNum == 0) { if (uiState.value.isAddSmallWheel == true && uiState.value.wheelNum == null || uiState.value.wheelNum == 0) {
showTipDialog("请输入辅助轮个数") showTipDialog("请输入辅助轮个数")
return return
@ -243,11 +243,16 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
return return
} }
if (eleWorkOrderBean.localAcceptCarSignPath.isNullOrBlank() || eleWorkOrderBean.localServicePeopleSignPath.isNullOrBlank()) { if (eleWorkOrderBean.localAcceptCarSignPath.isNullOrBlank()) {
showTipDialog("请先上传签名!") showTipDialog("请先上传签名!")
return return
} }
if (eleWorkOrderBean.serverServicePeopleSignPath.isNullOrBlank() && eleWorkOrderBean.localServicePeopleSignPath.isNullOrBlank()) {
showTipDialog("请上传服务人员签名!")
return
}
LoadingManager.showLoading() LoadingManager.showLoading()
ZdLocationManager.getSingleLocation(success = { ZdLocationManager.getSingleLocation(success = {
LoadingManager.hideLoading() LoadingManager.hideLoading()
@ -314,14 +319,7 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
orderInfo = getCurrentOrder())) orderInfo = getCurrentOrder()))
LogUtil.print("电子表单更新车辆损伤照片", "eleWorkOrderBean==${photoList.toJson()}") LogUtil.print("电子表单更新车辆损伤照片", "eleWorkOrderBean==${photoList.toJson()}")
LogUtil.print("电子表单", "eleWorkOrderBean==${it.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()))
}
}, failed = { }, failed = {
LoadingManager.hideLoading() LoadingManager.hideLoading()
@ -354,6 +352,5 @@ class ConfirmEleVm : IServicingVm<ConfirmEleVm.Action, ConfirmEleVm.UiState>() {
val showOfflineDialog : Boolean? = null, val showOfflineDialog : Boolean? = null,
val showAcceptCarSignUploadFailedDialog : Boolean? = null, val showAcceptCarSignUploadFailedDialog : Boolean? = null,
val showServiceSignatureUploadFailedDialog : 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.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -35,7 +38,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -44,6 +49,7 @@ import coil.compose.AsyncImage
import com.blankj.utilcode.util.ConvertUtils import com.blankj.utilcode.util.ConvertUtils
import com.blankj.utilcode.util.ToastUtils import com.blankj.utilcode.util.ToastUtils
import com.za.base.BaseActivity import com.za.base.BaseActivity
import com.za.base.theme.bgColor
import com.za.base.theme.black65 import com.za.base.theme.black65
import com.za.base.theme.buttonBgColor import com.za.base.theme.buttonBgColor
import com.za.base.view.CommonDialog import com.za.base.view.CommonDialog
@ -195,6 +201,9 @@ fun VerifyOrderScreen(vm : VerifyOrderVm = viewModel()) {
VerifyCarView(orderInfo = uiState.value.orderInfo, VerifyCarView(orderInfo = uiState.value.orderInfo,
newCarVin = uiState.value.newCarVin, newCarVin = uiState.value.newCarVin,
newCarVinPath = uiState.value.newCarPhotoPath, newCarVinPath = uiState.value.newCarPhotoPath,
newCarChanged = {
vm.updateState(uiState.value.copy(newCarVin = it))
},
recognize = { recognize = {
vm.dispatch(VerifyOrderVm.Action.Recognize(it)) vm.dispatch(VerifyOrderVm.Action.Recognize(it))
}) })
@ -237,6 +246,7 @@ fun VerifyOrderScreen(vm : VerifyOrderVm = viewModel()) {
private fun VerifyCarView( private fun VerifyCarView(
orderInfo : OrderInfo?, orderInfo : OrderInfo?,
newCarVin : String? = null, newCarVin : String? = null,
newCarChanged : (String?) -> Unit,
newCarVinPath : String? = null, newCarVinPath : String? = null,
recognize : (String) -> Unit, recognize : (String) -> Unit,
) { ) {
@ -298,10 +308,19 @@ private fun VerifyCarView(
fontSize = 13.sp, fontSize = 13.sp,
fontWeight = FontWeight.Medium) fontWeight = FontWeight.Medium)
Spacer(modifier = Modifier.width(10.dp)) Spacer(modifier = Modifier.width(10.dp))
Text(text = newCarVin ?: "", TextField(value = newCarVin ?: "",
fontWeight = FontWeight.Medium, onValueChange = { newCarChanged(it) },
fontSize = 16.sp, modifier = Modifier.background(color = bgColor,
color = Color.Black) 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)) 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.Const
import com.za.base.IServicingVm import com.za.base.IServicingVm
import com.za.base.view.LoadingManager import com.za.base.view.LoadingManager
import com.za.bean.UnifiedOCRWithCompressRequest
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.bean.request.OrderPhotoOcrRecognizeRequest import com.za.bean.request.OrderPhotoOcrRecognizeRequest
import com.za.bean.request.UpdateTaskBean import com.za.bean.request.UpdateTaskBean
@ -45,7 +46,7 @@ class VerifyOrderVm : IServicingVm<VerifyOrderVm.Action, VerifyOrderVm.UiState>(
LoadingManager.showLoading() LoadingManager.showLoading()
CommonMethod.uploadImage(file = File(localPath), success = { CommonMethod.uploadImage(file = File(localPath), success = {
LoadingManager.hideLoading() LoadingManager.hideLoading()
if (it.isNullOrBlank() == true) { if (it.isNullOrBlank()) {
return@uploadImage return@uploadImage
} }
doRecognize(it) doRecognize(it)
@ -60,7 +61,8 @@ class VerifyOrderVm : IServicingVm<VerifyOrderVm.Action, VerifyOrderVm.UiState>(
val orderPhotoOcrRecognizeRequest = val orderPhotoOcrRecognizeRequest =
OrderPhotoOcrRecognizeRequest(GlobalData.currentOrder?.userOrderId, 2, uploadPath) OrderPhotoOcrRecognizeRequest(GlobalData.currentOrder?.userOrderId, 2, uploadPath)
LogUtil.print("$tag doRecognize", "请求参数==${orderPhotoOcrRecognizeRequest.toJson()}") 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()) .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver<String>() { .subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : 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.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape 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.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -27,6 +28,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp 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.bean.db.order.OrderInfo
import com.za.common.util.MapUtil import com.za.common.util.MapUtil
import com.za.ext.callPhone import com.za.ext.callPhone
import com.za.ext.finish
import com.za.servicing.R import com.za.servicing.R
import com.za.ui.servicing.in_servicing_setting.OrderRequirementsActivity import com.za.ui.servicing.in_servicing_setting.OrderRequirementsActivity
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
@ -207,23 +208,25 @@ fun InServicingHeadView(title : String,
} }
} }
Column {
CenterAlignedTopAppBar(modifier = Modifier.fillMaxWidth(), Column {
colors = TopAppBarDefaults.centerAlignedTopAppBarColors() Box(modifier = Modifier
.copy(containerColor = headBgColor, titleContentColor = Color.White), .fillMaxWidth()
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) }, .background(color = headBgColor)
navigationIcon = { .padding(horizontal = 10.dp, vertical = 5.dp), contentAlignment = Alignment.Center) {
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween) {
if (isCanBack) { if (isCanBack) {
AsyncImage(model = R.drawable.sv_back, Icon(Icons.Default.KeyboardArrowLeft,
contentDescription = "", contentDescription = "",
modifier = Modifier modifier = Modifier
.size(40.dp)
.clickable { onBack() } .clickable { onBack() }
.padding(10.dp)) .padding(5.dp),
tint = Color.White)
} }
},
actions = {
Box(modifier = Modifier Box(modifier = Modifier
.size(46.dp) .size(46.dp)
.clickable { .clickable {
@ -236,7 +239,16 @@ fun InServicingHeadView(title : String,
contentDescription = "", contentDescription = "",
modifier = Modifier.fillMaxSize()) 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() 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