feat: 初始化项目结构和基本功能
- 创建项目根目录和主要子模块 - 添加基本的 Activity 和布局文件 - 实现简单的导航和电话拨打功能 - 添加相机和图像处理相关代码 - 创建网络请求和数据加密工具类 - 设置 AndroidManifest 文件和权限
This commit is contained in:
1
servicing/.gitignore
vendored
Normal file
1
servicing/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
221
servicing/build.gradle
Normal file
221
servicing/build.gradle
Normal file
@ -0,0 +1,221 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
id 'com.google.devtools.ksp'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.za.servicing'
|
||||
compileSdk 35
|
||||
|
||||
defaultConfig {
|
||||
minSdk 23
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
vectorDrawables {
|
||||
useSupportLibrary true
|
||||
}
|
||||
|
||||
manifestPlaceholders = [
|
||||
JPUSH_PKGNAME : "${applicationId}",
|
||||
JPUSH_APPKEY : "a87e46d05e9f095a2b47a304", //JPush 上注册的包名对应的 Appkey.e6c51448340caba93fd418
|
||||
JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
|
||||
|
||||
XIAOMI_APPID : "MI-2882303761518149120",
|
||||
XIAOMI_APPKEY : "MI-5381814934120",//MI-小米的APPKEY
|
||||
HUAWEI_APPID : "appid=100923923",//appid=华为的APPID"
|
||||
OPPO_APPKEY : "OP-c8ce8eafcd3940ceb85c1ccbee8863c7",//OP-oppo的APPKEY
|
||||
OPPO_APPID : "OP-30136992",//OP-oppo的APPID
|
||||
OPPO_APPSECRET: "OP-25e2baa85b7946b1b365af515515c42d",//OP-oppo的APPSECRET
|
||||
VIVO_APPKEY : "cfd443e2a1757cf537361588c988a12a",//vivo的APPKEY
|
||||
VIVO_APPID : "105470845",//vivo的APPID
|
||||
]
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
}
|
||||
buildFeatures {
|
||||
compose true
|
||||
buildConfig true // 生成 BuildConfig
|
||||
resValues true // 允许资源值生成
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion '1.5.15'
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||
}
|
||||
}
|
||||
publishing {
|
||||
singleVariant("release") {
|
||||
withSourcesJar()
|
||||
}
|
||||
}
|
||||
}
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
groupId = 'io.github.szl9'
|
||||
artifactId = 'zd_servicing'
|
||||
version = "1.0.1.9"
|
||||
|
||||
pom {
|
||||
packaging = "aar"
|
||||
name.set("zd_servicing")
|
||||
description.set("zd_servicing: Library for Android Application")
|
||||
url.set("https://github.com/szl9/4dbki6r4uv.git")
|
||||
inceptionYear.set("2024")
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name.set("MIT License")
|
||||
url.set("https://opensource.org/licenses/MIT")
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id.set("zd")
|
||||
name.set("zd szl")
|
||||
email.set("17630035658@163.com")
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection.set("scm:git@github.com:szl9/4dbki6r4uv.git")
|
||||
developerConnection.set("scm:git@github.com:szl9/4dbki6r4uv.git")
|
||||
url.set("https://github.com/szl9/4dbki6r4uv.git")
|
||||
}
|
||||
}
|
||||
|
||||
// 将 afterEvaluate 移到 publications 块内
|
||||
afterEvaluate {
|
||||
from components.release
|
||||
// 将依赖项处理放在这里
|
||||
pom.withXml {
|
||||
def root = asNode()
|
||||
def dependenciesNode = root.dependencies ? root.dependencies[0] : root.appendNode('dependencies')
|
||||
configurations.api.allDependencies.each { dep ->
|
||||
try {
|
||||
if (dep.group != null && dep.name != null && dep.version != null) {
|
||||
def dependencyNode = dependenciesNode.appendNode('dependency')
|
||||
dependencyNode.appendNode('groupId', dep.group)
|
||||
dependencyNode.appendNode('artifactId', dep.name)
|
||||
dependencyNode.appendNode('version', dep.version)
|
||||
dependencyNode.appendNode('scope', 'compile')
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.warn("Failed to add dependency ${dep} to POM", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = 'zd_servicing'
|
||||
url = layout.buildDirectory.dir("zd_servicing")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('generateRepo', Zip) {
|
||||
def publishTask = tasks.named('publishReleasePublicationToZd_servicingRepository')
|
||||
from publishTask.map { it.getRepository().getUrl() }
|
||||
into 'zd_servicing'
|
||||
archiveFileName.set('zd_servicing.zip')
|
||||
}
|
||||
dependencies {
|
||||
api libs.androidx.core.ktx
|
||||
api libs.androidx.appcompat
|
||||
api libs.material
|
||||
api libs.androidx.lifecycle.viewmodel.compose
|
||||
api libs.androidx.lifecycle.runtime.ktx
|
||||
api libs.androidx.activity.compose
|
||||
api platform(libs.androidx.compose.bom)
|
||||
api libs.androidx.ui
|
||||
api libs.androidx.ui.graphics
|
||||
api libs.androidx.ui.tooling.preview
|
||||
api libs.androidx.material3
|
||||
api libs.androidx.work.runtime.ktx
|
||||
api libs.androidx.exifinterface
|
||||
|
||||
testApi libs.junit
|
||||
androidTestApi libs.androidx.junit
|
||||
androidTestApi libs.androidx.espresso.core
|
||||
androidTestApi platform(libs.androidx.compose.bom)
|
||||
androidTestApi libs.androidx.ui.test.junit4
|
||||
debugApi libs.androidx.ui.tooling
|
||||
debugApi libs.androidx.ui.test.manifest
|
||||
|
||||
api libs.coil.compose
|
||||
api libs.coil.gif
|
||||
|
||||
api libs.permissionx
|
||||
api libs.utilcodex
|
||||
|
||||
api libs.crashreport
|
||||
|
||||
// 高德地图
|
||||
api libs.xdmap
|
||||
api libs.location
|
||||
api libs.search
|
||||
|
||||
// // JPush
|
||||
api libs.jpush
|
||||
api libs.jcore
|
||||
api libs.gson
|
||||
|
||||
// 网络
|
||||
api libs.retrofit
|
||||
api libs.converter.gson
|
||||
api libs.adapter.rxjava3
|
||||
api libs.rxjava
|
||||
api libs.rxandroid
|
||||
api libs.logging.interceptor
|
||||
api libs.fastjson
|
||||
|
||||
// 本地数据
|
||||
api libs.room.runtime
|
||||
annotationProcessor libs.room.compiler
|
||||
ksp libs.room.compiler
|
||||
api libs.mmkv
|
||||
|
||||
// 7z
|
||||
api libs.xz
|
||||
api libs.commons.compress
|
||||
|
||||
api libs.core
|
||||
api libs.tbssdk
|
||||
|
||||
// CameraX
|
||||
api libs.androidx.camera.core
|
||||
api libs.androidx.camera.camera2
|
||||
api libs.androidx.camera.lifecycle
|
||||
api libs.androidx.camera.view
|
||||
api libs.androidx.camera.extensions
|
||||
|
||||
api libs.glide
|
||||
annotationProcessor libs.compiler
|
||||
|
||||
api libs.org.eclipse.paho.client.mqttv3
|
||||
api libs.org.eclipse.paho.android.service
|
||||
}
|
||||
|
0
servicing/consumer-rules.pro
Normal file
0
servicing/consumer-rules.pro
Normal file
203
servicing/proguard-rules.pro
vendored
Normal file
203
servicing/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
# 保留行号用于调试
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-renamesourcefileattribute SourceFile
|
||||
|
||||
# 保留基本属性
|
||||
-keepattributes Signature
|
||||
-keepattributes *Annotation*
|
||||
-keepattributes Exceptions
|
||||
-keepattributes InnerClasses
|
||||
|
||||
# 保留R文件
|
||||
-keepclassmembers class **.R$* {
|
||||
public static <fields>;
|
||||
}
|
||||
|
||||
# 保留servicing模块中的所有model类
|
||||
-keep class com.za.servicing.model.** { *; }
|
||||
|
||||
# 保留servicing模块中的所有接口
|
||||
-keep interface com.za.servicing.** { *; }
|
||||
|
||||
# 保留servicing模块中的所有枚举
|
||||
-keepclassmembers enum com.za.servicing.** { *; }
|
||||
|
||||
# 保留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*();
|
||||
}
|
||||
|
||||
# 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
|
||||
-dontpreverify
|
||||
-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.**
|
||||
|
||||
-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 *;
|
||||
}
|
||||
# Preserve all .class method names.
|
||||
|
||||
-keepclassmembernames class * {
|
||||
java.lang.Class class$(java.lang.String);
|
||||
java.lang.Class class$(java.lang.String, boolean);
|
||||
}
|
||||
|
||||
# Preserve all native method names and the names of their classes.
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# Preserve the special static methods that are required in all enumeration
|
||||
# classes.
|
||||
|
||||
-keepclassmembers class * extends java.lang.Enum {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keepclassmembers class * implements java.io.Serializable {
|
||||
static final long serialVersionUID;
|
||||
static final java.io.ObjectStreamField[] serialPersistentFields;
|
||||
private void writeObject(java.io.ObjectOutputStream);
|
||||
private void readObject(java.io.ObjectInputStream);
|
||||
java.lang.Object writeReplace();
|
||||
java.lang.Object readResolve();
|
||||
}
|
||||
|
||||
# Gson
|
||||
-keep class com.google.gson.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes *Annotation*
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
|
||||
# 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);
|
||||
}
|
||||
|
||||
-dontwarn java.lang.invoke.StringConcatFactory
|
@ -0,0 +1,24 @@
|
||||
package com.za.servicing
|
||||
|
||||
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.servicing.test", appContext.packageName)
|
||||
}
|
||||
}
|
249
servicing/src/main/AndroidManifest.xml
Normal file
249
servicing/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,249 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-sdk tools:overrideLibrary="androidx.camera.view,androidx.camera:camera-camera2,androidx.camera.camera2,androidx.camera.lifecycle,androidx.camera.core" />
|
||||
<permission
|
||||
android:name="${applicationId}.permission.JPUSH_MESSAGE"
|
||||
android:protectionLevel="signature" /> <!-- 位置相关权限 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 存储相关权限 -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- 网络相关权限 -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 蓝牙相关权限 -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 电话相关权限 -->
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<uses-permission android:name="android.permission.READ_CALL_LOG" />
|
||||
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
|
||||
tools:ignore="ProtectedPermissions" /> <!-- 系统相关权限 -->
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.REORDER_TASKS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_SETTINGS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- 多媒体相关权限 -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.FLASHLIGHT" /> <!-- 硬件特性声明 -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera.autofocus"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Intent queries for Android 11+ -->
|
||||
<queries package="${applicationId}">
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.media.action.ACTION_VIDEO_CAPTURE" />
|
||||
</intent>
|
||||
</queries>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.OPEN_DOCUMENT" />
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.za.ui.main.ServiceLauncherActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Dealer">
|
||||
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.za.ui.main.ServicingMainActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.departure_photo.DeparturePhotoActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.order_confirm.input_money.InputMoneyActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.order_confirm.modify_money.ModifyMoneyActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.new_order.NewOrderActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.order_confirm.real_order_confirm.RealOrderConfirmActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.order_confirm.receive_money.ReceiveMoneyActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.map_search.MapSearchActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name="com.za.ui.order_report.ReportFloatingManager"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name="com.za.ui.order_report.HistoryReportActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="com.za.ui.order_report.OrderReportActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="com.za.ui.h5.CommonH5Activity"
|
||||
android:exported="false"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.order_give_up.OrderGiveUpActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.in_servicing_setting.OrderRequirementsActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.order_confirm.OrderConfirmActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.destination_photo.DestinationPhotoActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.go_to_destination.GoToDestinationActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.signature.GridPaintActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.operation.InOperationActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_in_operation"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.camera.ZdCameraXActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait">
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.check_vehicle.CheckVehicleActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.verify.VerifyOrderActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.go_accident.GoAccidentSiteActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.wait_to_start.WaitToStartActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
|
||||
<service
|
||||
android:name="cn.jpush.android.service.PushService"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="cn.jpush.android.intent.REGISTER" />
|
||||
<action android:name="cn.jpush.android.intent.REPORT" />
|
||||
<action android:name="cn.jpush.android.intent.PushService" />
|
||||
<action android:name="cn.jpush.android.intent.PUSH_TIME" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name="com.za.service.jpush.JPushReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
tools:node="replace">
|
||||
<intent-filter>
|
||||
|
||||
<action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_ACTION" />
|
||||
<action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED_ACTION" />
|
||||
<action android:name="cn.jpush.android.intent.CONNECTION" />
|
||||
<action android:name="android.intent.action.USER_PRESENT" />
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||
<action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
|
||||
|
||||
<category android:name="${applicationId}" />
|
||||
</intent-filter>
|
||||
<intent-filter android:priority="1000">
|
||||
<action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" />
|
||||
<!-- Required 显示通知栏 -->
|
||||
<category android:name="${applicationId}" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.USER_PRESENT" />
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||
</intent-filter>
|
||||
<!-- Optional -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
||||
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name="com.za.service.jpush.MyJPushMessageReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
|
||||
<action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_ACTION" />
|
||||
<action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED_ACTION" />
|
||||
<action android:name="cn.jpush.android.intent.CONNECTION" />
|
||||
<action android:name="android.intent.action.USER_PRESENT" />
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||
|
||||
<category android:name="${applicationId}" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
BIN
servicing/src/main/assets/fonts/song.ttf
Normal file
BIN
servicing/src/main/assets/fonts/song.ttf
Normal file
Binary file not shown.
98
servicing/src/main/java/com/za/base/AppConfig.kt
Normal file
98
servicing/src/main/java/com/za/base/AppConfig.kt
Normal file
@ -0,0 +1,98 @@
|
||||
package com.za.base
|
||||
|
||||
import com.za.common.GlobalData
|
||||
|
||||
object AppConfig {
|
||||
var isRelease = false
|
||||
|
||||
// API 相关地址
|
||||
lateinit var BASE_URL: String // API 主地址
|
||||
lateinit var IMG_BASE_URL: String // 图片服务器地址
|
||||
lateinit var Resource_URL: String // 资源服务器地址
|
||||
|
||||
// H5 相关地址
|
||||
lateinit var TRAIN_URL: String // 培训文档地址
|
||||
lateinit var DOCMENT_URL: String // 中道资料地址
|
||||
|
||||
/**
|
||||
* 正式环境配置
|
||||
*/
|
||||
fun release() {
|
||||
isRelease = true
|
||||
|
||||
// API 配置
|
||||
BASE_URL = "https://api.sinoassist.com"
|
||||
IMG_BASE_URL = "https://api.sinoassist.com"
|
||||
Resource_URL = "https://www.sinoassist.com/res"
|
||||
|
||||
// H5 配置
|
||||
TRAIN_URL = "https://www.sinoassist.com/h5/supplier/dispatch/diverTrainDocment"
|
||||
DOCMENT_URL = "https://www.sinoassist.com/h5/supplier/dispatch/docmentList"
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核环境配置
|
||||
*/
|
||||
fun review() {
|
||||
isRelease = true
|
||||
|
||||
// API 配置
|
||||
BASE_URL = "http://interface.review.sino-assist.com"
|
||||
IMG_BASE_URL = "http://interface.review.sino-assist.com"
|
||||
Resource_URL = "https://www.sinoassist.com/res"
|
||||
}
|
||||
|
||||
/**
|
||||
* CRM1 环境配置
|
||||
*/
|
||||
fun crm1() {
|
||||
isRelease = false
|
||||
|
||||
// API 配置
|
||||
BASE_URL = "https://api1.sino-assist.com"
|
||||
IMG_BASE_URL = "https://api1.sino-assist.com"
|
||||
Resource_URL = "https://crm1.sino-assist.com/res"
|
||||
|
||||
// H5 配置
|
||||
TRAIN_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/diverTrainDocment"
|
||||
DOCMENT_URL = "https://crm1.sino-assist.com/h5/supplier/dispatch/docmentList"
|
||||
}
|
||||
|
||||
/**
|
||||
* CRM2 环境配置
|
||||
*/
|
||||
fun crm2() {
|
||||
isRelease = false
|
||||
|
||||
// API 配置
|
||||
BASE_URL = "https://api2.sino-assist.com"
|
||||
IMG_BASE_URL = "https://api2.sino-assist.com"
|
||||
Resource_URL = "https://crm2.sino-assist.com/res"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取培训文档完整地址
|
||||
* @param driverId 司机ID
|
||||
* @param keyword 关键字
|
||||
* @return 完整的培训文档URL
|
||||
*/
|
||||
fun getTrainUrl(keyWord: String = ""): String {
|
||||
if (keyWord.isEmpty()) {
|
||||
return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfo?.userId}"
|
||||
}
|
||||
return TRAIN_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfo?.userId}&keyword=$keyWord"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取中道资料完整地址
|
||||
* @param driverId 司机ID
|
||||
* @param keyword 关键字
|
||||
* @return 完整的中道资料URL
|
||||
*/
|
||||
fun getDocmentUrl(keyWord: String = ""): String {
|
||||
if (keyWord.isEmpty()) {
|
||||
return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfo?.userId}"
|
||||
}
|
||||
return DOCMENT_URL + "?token=${GlobalData.token}&driverId=${GlobalData.driverInfo?.userId}&keyword=$keyWord"
|
||||
}
|
||||
}
|
63
servicing/src/main/java/com/za/base/BaseActivity.kt
Normal file
63
servicing/src/main/java/com/za/base/BaseActivity.kt
Normal file
@ -0,0 +1,63 @@
|
||||
package com.za.base
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tencent.smtt.sdk.QbSdk
|
||||
import com.za.base.BaseVm.Companion.showTipDialog
|
||||
import com.za.base.theme.DealerTheme
|
||||
import com.za.base.view.CommonDialog
|
||||
import com.za.base.view.LoadingManager
|
||||
import com.za.common.log.LogUtil
|
||||
|
||||
abstract class BaseActivity : AppCompatActivity() {
|
||||
protected val TAG by lazy { javaClass.simpleName }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
DealerTheme {
|
||||
ContentView()
|
||||
if (LoadingManager.showLoading.value) {
|
||||
LoadingManager.LoadingView()
|
||||
}
|
||||
|
||||
if (showTipDialog.value != null) {
|
||||
CommonDialog(message = showTipDialog.value
|
||||
?: "",
|
||||
title = "提示",
|
||||
confirmText = "我已了解",
|
||||
confirm = { BaseVm.hideTipDialog() },
|
||||
cancel = { BaseVm.hideTipDialog() },
|
||||
dismiss = { BaseVm.hideTipDialog() },
|
||||
cancelEnable = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QbSdk.initX5Environment(this.application, object : QbSdk.PreInitCallback {
|
||||
override fun onCoreInitFinished() {
|
||||
// 内核初始化完成,可能为系统内核,也可能为系统内核
|
||||
LogUtil.print("initX5Environment ", "finish")
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 预初始化结束
|
||||
* 由于X5内核体积较大,需要依赖网络动态下发,所以当内核不存在的时候,默认会回调false,此时将会使用系统内核代替
|
||||
* @param isX5 是否使用X5内核
|
||||
*/
|
||||
override fun onViewInitFinished(isX5: Boolean) {
|
||||
LogUtil.print("onViewInitFinished ", "isX5=$isX5")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
abstract fun ContentView()
|
||||
}
|
||||
|
||||
|
27
servicing/src/main/java/com/za/base/BaseVm.kt
Normal file
27
servicing/src/main/java/com/za/base/BaseVm.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package com.za.base
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
abstract class BaseVm<T, U> : ViewModel() {
|
||||
val tag: String = javaClass.simpleName
|
||||
abstract fun updateState(uiState: U)
|
||||
abstract fun dispatch(action: T)
|
||||
|
||||
companion object {
|
||||
val showTipDialog = mutableStateOf<String?>(null)//提示框
|
||||
fun showTipDialog(msg: String) {
|
||||
showTipDialog.value = msg
|
||||
}
|
||||
|
||||
fun hideTipDialog() {
|
||||
showTipDialog.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class LoadState {
|
||||
data object Init : LoadState()
|
||||
data object Success : LoadState()
|
||||
data object Failed : LoadState()
|
||||
}
|
45
servicing/src/main/java/com/za/base/Const.kt
Normal file
45
servicing/src/main/java/com/za/base/Const.kt
Normal file
@ -0,0 +1,45 @@
|
||||
package com.za.base
|
||||
|
||||
object Const {
|
||||
const val Image_Max_length = 1024 * 400L
|
||||
const val faceFileName = "zd_com.dear"
|
||||
const val NetWorkException = 999
|
||||
const val PhotoNormalPath =
|
||||
"http://file-gk.sinoassist.com:38888/group1/M00/98/94/wKgDd2Uib0OAN0glAAAMOeWcU94810.png?date=2023-10-08"
|
||||
|
||||
const val DoubleClickTime = 3000
|
||||
|
||||
|
||||
const val driverSighName : String = "driver_sign.jpg"
|
||||
|
||||
|
||||
//通用的照片水印模板 code
|
||||
const val NormalWaterMarker = "-1"
|
||||
|
||||
const val CHILD_COMPANY = 1 //子公司
|
||||
|
||||
const val SMALL_REPAIR = 1 // 小修
|
||||
const val TUO_CHE = 2
|
||||
|
||||
object CarState {
|
||||
const val FREE = 0 //空闲
|
||||
const val BUSY = 1 //忙碌
|
||||
const val ZHI_MANG = 2 //置忙
|
||||
const val REPAIRING = 8 //维修中
|
||||
}
|
||||
|
||||
|
||||
object PhotoType {
|
||||
const val InServicing = 1 //服务中照片
|
||||
const val HistoryOrder = 2 //历史中照片
|
||||
const val ChangeBattery = 3 //更换电瓶照片
|
||||
const val NormalImage = 4 //普通的照片处理
|
||||
}
|
||||
|
||||
object InServiceSettingType {
|
||||
const val ON_SITE_PHOTO = 0 //现场照片
|
||||
const val ORDER_REQUIREMENTS = 1 //案件要求
|
||||
const val ORDER_DETAIL = 2 //案件详情
|
||||
const val ORDER_GIVE_UP = 3 //订单放弃
|
||||
}
|
||||
}
|
123
servicing/src/main/java/com/za/base/IServicingVm.kt
Normal file
123
servicing/src/main/java/com/za/base/IServicingVm.kt
Normal file
@ -0,0 +1,123 @@
|
||||
package com.za.base
|
||||
|
||||
import com.za.bean.db.ele.EleWorkOrderBean
|
||||
import com.za.bean.db.order.OrderInfo
|
||||
import com.za.bean.db.order.PhotoTemplateInfo
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.ext.getTaskNode
|
||||
import com.za.ext.toJson
|
||||
import com.za.net.CommonMethod
|
||||
import com.za.offline.OfflineManager
|
||||
import com.za.offline.OfflineUpdateTaskBean
|
||||
import com.za.room.RoomHelper
|
||||
|
||||
abstract class IServicingVm<T, U> : BaseVm<T, U>() {
|
||||
|
||||
fun getCurrentOrder() : OrderInfo? =
|
||||
GlobalData.currentOrder ?: RoomHelper.db?.orderDao()?.getCurrentOrder()
|
||||
|
||||
fun updateOrder(orderInfo : OrderInfo?) {
|
||||
orderInfo?.let { order ->
|
||||
if (order.isCurrent == true) {
|
||||
GlobalData.currentOrder = order
|
||||
}
|
||||
RoomHelper.db?.orderDao()?.update(order)
|
||||
LogUtil.print("$tag updateOrder", order.toJson() ?: "")
|
||||
} ?: LogUtil.print("$tag updateOrder", "order is null")
|
||||
}
|
||||
|
||||
// 使用 Flow 重写获取照片模板的方法
|
||||
//当前订单的照片模版
|
||||
fun getCurrentPhotoTemplate(success : (List<PhotoTemplateInfo>?) -> Unit,
|
||||
failure : (String?) -> Unit = {}) {
|
||||
val photoTemplateList = RoomHelper.db?.photoTemplateDao()
|
||||
?.getOrderPhotoTemplateFromTaskNode(getCurrentOrder()?.getTaskNode() ?: 0,
|
||||
getCurrentOrder()?.userOrderId ?: 0)
|
||||
if (photoTemplateList.isNullOrEmpty()) {
|
||||
CommonMethod.fetchPhotoTemplate(GlobalData.currentOrder, success = {
|
||||
val data = RoomHelper.db?.photoTemplateDao()?.getOrderPhotoTemplateFromTaskNode(
|
||||
getCurrentOrder()?.getTaskNode() ?: 0,
|
||||
getCurrentOrder()?.userOrderId ?: 0)
|
||||
success(data)
|
||||
}, failed = {
|
||||
failure(it)
|
||||
})
|
||||
} else {
|
||||
success(photoTemplateList)
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 Flow 重写获取照片模板的方法
|
||||
//获取出发前照片模版
|
||||
fun getDeparturePhotoTemplate(success : (List<PhotoTemplateInfo>?) -> Unit,
|
||||
failure : (String?) -> Unit = {}) {
|
||||
val photoTemplateList = RoomHelper.db?.photoTemplateDao()
|
||||
?.getOrderPhotoTemplateFromTaskNode(10100, getCurrentOrder()?.userOrderId ?: 0)
|
||||
if (photoTemplateList.isNullOrEmpty()) {
|
||||
CommonMethod.fetchPhotoTemplate(GlobalData.currentOrder, success = {
|
||||
val data = RoomHelper.db?.photoTemplateDao()
|
||||
?.getOrderPhotoTemplateFromTaskNode(10100, getCurrentOrder()?.userOrderId ?: 0)
|
||||
success(data)
|
||||
}, failed = {
|
||||
failure(it)
|
||||
})
|
||||
} else {
|
||||
success(photoTemplateList)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkIsGoDeparturePhoto() : Boolean {
|
||||
val list = RoomHelper.db?.photoTemplateDao()
|
||||
?.getOrderPhotoTemplateFromTaskNode(10100, getCurrentOrder()?.userOrderId ?: 0)
|
||||
return ! list.isNullOrEmpty()
|
||||
}
|
||||
|
||||
fun updateCurrentEleWorkOrder(eleWorkOrderBean : EleWorkOrderBean) {
|
||||
RoomHelper.db?.eleWorkOrderDao()?.update(eleWorkOrderBean)
|
||||
|
||||
getCurrentOrder()?.let { order ->
|
||||
val updatedOrder = order.copy(electronOrderState = eleWorkOrderBean.orderWorkStatus,
|
||||
taskSuccessStatus = if (eleWorkOrderBean.isSuccess == 1) 0 else 1)
|
||||
updateOrder(updatedOrder)
|
||||
}
|
||||
|
||||
LogUtil.print("$tag updateCurrentEleWorkOrder", eleWorkOrderBean.toJson() ?: "")
|
||||
}
|
||||
|
||||
fun getCurrentOrderOfflineTask() : List<OfflineUpdateTaskBean>? {
|
||||
return getCurrentOrder()?.taskId?.let { taskId ->
|
||||
RoomHelper.db?.offlineTaskDao()?.getOfflineTaskFromTaskId(taskId)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearCurrentOrderOfflineTask() {
|
||||
getCurrentOrder()?.taskId?.let { taskId ->
|
||||
RoomHelper.db?.offlineTaskDao()?.deleteOfflineTaskFromTaskId(taskId)
|
||||
LogUtil.print("$tag clearCurrentOrderOfflineTask", "taskId==$taskId")
|
||||
}
|
||||
}
|
||||
|
||||
fun getCurrentOrderEleWorkOrder() : EleWorkOrderBean? {
|
||||
return RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(getCurrentOrder()?.taskId ?: 0)
|
||||
}
|
||||
|
||||
fun insertOfflineTask(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
RoomHelper.db?.offlineTaskDao()?.let { offlineDao ->
|
||||
val primaryId = offlineUpdateTaskBean.primaryId ?: 0
|
||||
val existingTask = offlineDao.getOfflineTaskFromPrimaryId(primaryId)
|
||||
|
||||
if (existingTask != null) {
|
||||
offlineDao.update(offlineUpdateTaskBean)
|
||||
} else {
|
||||
offlineDao.insertOfflineTask(offlineUpdateTaskBean)
|
||||
}
|
||||
|
||||
offlineUpdateTaskBean.taskId?.let { taskId ->
|
||||
OfflineManager.start(taskId)
|
||||
}
|
||||
|
||||
LogUtil.print("$tag insertOfflineTask", offlineUpdateTaskBean.toJson() ?: "")
|
||||
}
|
||||
}
|
||||
}
|
37
servicing/src/main/java/com/za/base/theme/Color.kt
Normal file
37
servicing/src/main/java/com/za/base/theme/Color.kt
Normal file
@ -0,0 +1,37 @@
|
||||
package com.za.base.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
|
||||
val bgColor = Color(0xFFF4F5F7)
|
||||
|
||||
val MainBottomSelectColor = Color(0xFF3D4B7C)
|
||||
val MainBottomUnSelectColor = Color(0xFFA9AEBD)
|
||||
|
||||
//标题颜色
|
||||
val headBgColor = Color(0xFF3D4B7C)
|
||||
|
||||
//标题颜色
|
||||
val headTitleColor = Color(0xB3FFFFFF)
|
||||
|
||||
//按钮背景颜色
|
||||
val buttonBgColor = Color(0xFF3D4B7C)
|
||||
|
||||
val white5 = Color(0x0DFFFFFF)
|
||||
val white95 = Color(0xF2FFFFFF)
|
||||
val white80 = Color(0xCCFFFFFF)
|
||||
|
||||
val black5 = Color(0x0D000000)
|
||||
val black10 = Color(0x1A000000)
|
||||
val black20 = Color(0x33000000)
|
||||
val black30 = Color(0x4D000000)
|
||||
val black50 = Color(0x80000000)
|
||||
val black65 = Color(0xA6000000)
|
||||
val black90 = Color(0xE6000000)
|
7
servicing/src/main/java/com/za/base/theme/Padding.kt
Normal file
7
servicing/src/main/java/com/za/base/theme/Padding.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package com.za.base.theme
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.blankj.utilcode.util.BarUtils
|
||||
import com.blankj.utilcode.util.ConvertUtils
|
||||
|
||||
val headPadding = ConvertUtils.px2dp(BarUtils.getStatusBarHeight().toFloat()/2).dp
|
80
servicing/src/main/java/com/za/base/theme/Theme.kt
Normal file
80
servicing/src/main/java/com/za/base/theme/Theme.kt
Normal file
@ -0,0 +1,80 @@
|
||||
package com.za.base.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material.ripple.RippleAlpha
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalRippleConfiguration
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.RippleConfiguration
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40,
|
||||
background = bgColor,
|
||||
onBackground = bgColor,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DealerTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
darkTheme -> LightColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = Color.Transparent.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(window.decorView.findViewById(android.R.id.content)) { view, windowInsets ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
view as ViewGroup
|
||||
view.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
leftMargin = insets.left
|
||||
bottomMargin = insets.bottom
|
||||
rightMargin = insets.right
|
||||
topMargin = 0
|
||||
}
|
||||
WindowInsetsCompat.CONSUMED
|
||||
}
|
||||
}
|
||||
}
|
||||
MaterialTheme(colorScheme = colorScheme) {
|
||||
CompositionLocalProvider(LocalRippleConfiguration provides RippleConfiguration(rippleAlpha = RippleAlpha(0f, 0f, 0f, 0f))) {
|
||||
ProvideTextStyle(value = MaterialTheme.typography.bodyLarge, content = content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//object NoRippleTheme : RippleTheme {
|
||||
//
|
||||
// @Composable
|
||||
// override fun defaultColor(): Color {
|
||||
// return Color.Unspecified
|
||||
// }
|
||||
//
|
||||
// @Composable
|
||||
// override fun rippleAlpha(): RippleAlpha {
|
||||
// return RippleAlpha(0f, 0f, 0f, 0f)
|
||||
// }
|
||||
//}
|
33
servicing/src/main/java/com/za/base/theme/Type.kt
Normal file
33
servicing/src/main/java/com/za/base/theme/Type.kt
Normal file
@ -0,0 +1,33 @@
|
||||
package com.za.base.theme
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
33
servicing/src/main/java/com/za/base/view/button.kt
Normal file
33
servicing/src/main/java/com/za/base/view/button.kt
Normal file
@ -0,0 +1,33 @@
|
||||
package com.za.base.view
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.za.base.theme.buttonBgColor
|
||||
import com.za.base.theme.headBgColor
|
||||
import com.za.ext.noDoubleClick
|
||||
|
||||
@Composable
|
||||
fun CommonButton(text: String, onClick: () -> Unit) {
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.noDoubleClick { onClick() }
|
||||
.padding(horizontal = 60.dp, vertical = 10.dp)
|
||||
.background(color = buttonBgColor, shape = RoundedCornerShape(4.dp))
|
||||
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
|
||||
Text(text = text, color = Color.White,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Medium)
|
||||
}
|
||||
}
|
220
servicing/src/main/java/com/za/base/view/dialog.kt
Normal file
220
servicing/src/main/java/com/za/base/view/dialog.kt
Normal file
@ -0,0 +1,220 @@
|
||||
package com.za.base.view
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import coil.compose.AsyncImage
|
||||
import com.za.base.theme.black90
|
||||
import com.za.common.util.MapUtil
|
||||
import com.za.servicing.R
|
||||
|
||||
@Composable
|
||||
fun CommonDialog(
|
||||
title: String? = null,
|
||||
confirmText: String = "确定",
|
||||
confirm: () -> Unit,
|
||||
content: @Composable () -> Unit = {},
|
||||
message: String? = null,
|
||||
cancelText: String? = "取消",
|
||||
cancelEnable: Boolean = true,
|
||||
cancel: () -> Unit = {},
|
||||
dismiss: () -> Unit
|
||||
) {
|
||||
Dialog(onDismissRequest = { dismiss() },
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = cancelEnable,
|
||||
dismissOnClickOutside = cancelEnable)) {
|
||||
Box(modifier = Modifier
|
||||
.background(color = Color.White, shape = RoundedCornerShape(13.dp))) {
|
||||
Spacer(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(105.dp)
|
||||
.background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF),
|
||||
Color(0x00DAE8FF))),
|
||||
shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp)))
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 30.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center) {
|
||||
Spacer(modifier = Modifier.height(35.dp))
|
||||
Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
if (message == null) {
|
||||
content()
|
||||
} else {
|
||||
Text(text = message, fontSize = 14.sp, color = Color(0xFF536475))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Box(modifier = Modifier
|
||||
.width(202.dp)
|
||||
.clickable { confirm() }
|
||||
.background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp))
|
||||
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
|
||||
Text(text = confirmText, color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
if (!cancelText.isNullOrBlank() && cancelEnable) {
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { cancel() }, contentAlignment = Alignment.Center) {
|
||||
Text(text = cancelText, fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun ReTakePhotoDialog(
|
||||
title: String? = null,
|
||||
confirm: () -> Unit,
|
||||
againSubmit: () -> Unit,
|
||||
path: String? = null,
|
||||
showAgain: Boolean = false,
|
||||
cancel: () -> Unit = {},
|
||||
dismiss: () -> Unit
|
||||
) {
|
||||
Dialog(onDismissRequest = { dismiss() },
|
||||
properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true)) {
|
||||
Box(modifier = Modifier
|
||||
.background(color = Color.White, shape = RoundedCornerShape(13.dp))) {
|
||||
Spacer(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(105.dp)
|
||||
.background(brush = Brush.verticalGradient(colors = arrayListOf(Color(0xFFDAE8FF),
|
||||
Color(0x00DAE8FF))),
|
||||
shape = RoundedCornerShape(topStart = 13.dp, topEnd = 13.dp)))
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 30.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center) {
|
||||
Spacer(modifier = Modifier.height(35.dp))
|
||||
Text(text = "$title", fontSize = 16.sp, fontWeight = FontWeight.Medium, color = Color(0xFF2A4054))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(300.dp), contentAlignment = Alignment.Center) {
|
||||
AsyncImage(model = path,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.FillWidth)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Box(modifier = Modifier
|
||||
.width(202.dp)
|
||||
.clickable { confirm() }
|
||||
.background(color = Color(0xFF3A58B1), shape = RoundedCornerShape(23.dp))
|
||||
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
|
||||
Text(text = "重拍", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
|
||||
if (showAgain) {
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Box(modifier = Modifier
|
||||
.width(202.dp)
|
||||
.clickable { againSubmit() }
|
||||
.background(color = Color.Red, shape = RoundedCornerShape(23.dp))
|
||||
.padding(vertical = 12.dp), contentAlignment = Alignment.Center) {
|
||||
Text(text = "再次上传", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { cancel() }, contentAlignment = Alignment.Center) {
|
||||
Text(text = "取消", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = black90)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun ChoiceMapDialog(dismiss: () -> Unit,
|
||||
lat: Double?,
|
||||
lng: Double?,
|
||||
address: String?) {
|
||||
val context = LocalContext.current
|
||||
Dialog(onDismissRequest = { dismiss() }) {
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(180.dp)
|
||||
.background(color = Color.White, shape = RoundedCornerShape(8.dp))
|
||||
.padding(10.dp), verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceAround) {
|
||||
if (MapUtil.isGdMapInstalled(context)) {
|
||||
Column(modifier = Modifier.clickable {
|
||||
MapUtil.startNavigationGd(context, lat = lat, lng = lng, address = address)
|
||||
dismiss()
|
||||
}) {
|
||||
AsyncImage(model = R.drawable.sv_amap_icon, contentDescription = "", modifier = Modifier.size(60.dp))
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Text(text = "高德地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
|
||||
if (MapUtil.isBaiduMapInstalled(context)) {
|
||||
Column(modifier = Modifier.clickable {
|
||||
MapUtil.startNavigationBd(context, lat = lat, lng = lng, address = address)
|
||||
dismiss()
|
||||
}) {
|
||||
AsyncImage(model = R.drawable.sv_baidu_icon, contentDescription = "", modifier = Modifier.size(60.dp))
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Text(text = "百度地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
|
||||
if (MapUtil.isTencentInstalled(context)) {
|
||||
Column(modifier = Modifier.clickable {
|
||||
MapUtil.startNavigationTencent(context, lat = lat, lng = lng, address = address)
|
||||
dismiss()
|
||||
}) {
|
||||
AsyncImage(model = R.drawable.sv_tencent_icon, contentDescription = "", modifier = Modifier.size(60.dp))
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Text(text = "腾讯地图", color = Color.Black, fontWeight = FontWeight.Medium, fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
servicing/src/main/java/com/za/base/view/emptyView.kt
Normal file
90
servicing/src/main/java/com/za/base/view/emptyView.kt
Normal file
@ -0,0 +1,90 @@
|
||||
package com.za.base.view
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import com.za.servicing.R
|
||||
|
||||
@Composable
|
||||
fun EmptyView(
|
||||
modifier: Modifier = Modifier,
|
||||
message: String = "暂无数据",
|
||||
imageSize: Pair<Int, Int> = Pair(118, 143),
|
||||
imageRes: Int = R.drawable.sv_emty_data
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AsyncImage(
|
||||
model = imageRes,
|
||||
contentDescription = message,
|
||||
modifier = Modifier.size(imageSize.first.dp, imageSize.second.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
text = message,
|
||||
color = Color.Gray,
|
||||
fontSize = 14.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoadError(
|
||||
modifier: Modifier = Modifier,
|
||||
message: String = "加载出错",
|
||||
imageSize: Pair<Int, Int> = Pair(118, 143),
|
||||
onRetry: (() -> Unit)? = null
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AsyncImage(
|
||||
model = R.drawable.sv_load_error,
|
||||
contentDescription = message,
|
||||
modifier = Modifier.size(imageSize.first.dp, imageSize.second.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
text = message,
|
||||
color = Color.Gray,
|
||||
fontSize = 14.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
if (onRetry != null) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "点击重试",
|
||||
color = Color(0xFF1BA8F7),
|
||||
fontSize = 14.sp,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.clickable { onRetry() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
54
servicing/src/main/java/com/za/base/view/head.kt
Normal file
54
servicing/src/main/java/com/za/base/view/head.kt
Normal file
@ -0,0 +1,54 @@
|
||||
package com.za.base.view
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import com.za.base.theme.headBgColor
|
||||
import com.za.base.theme.headPadding
|
||||
import com.za.servicing.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HeadView(title: String, onBack: () -> Unit = {}, isCanBack: Boolean = true, action: @Composable () -> Unit = {}) {
|
||||
CenterAlignedTopAppBar(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = headBgColor)
|
||||
.systemBarsPadding()
|
||||
.padding(top = 20.dp),
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors().copy(containerColor = headBgColor, titleContentColor = Color.White),
|
||||
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
|
||||
navigationIcon = {
|
||||
if (isCanBack) {
|
||||
AsyncImage(model = R.drawable.sv_back, contentDescription = "", modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clickable { onBack() }
|
||||
.padding(10.dp))
|
||||
}
|
||||
}, actions = { action() })
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HeadViewNotBack(title: String) {
|
||||
CenterAlignedTopAppBar(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = headBgColor)
|
||||
.padding(top = headPadding),
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors().copy(containerColor = headBgColor, titleContentColor = Color.White),
|
||||
title = { Text(text = title, fontSize = 15.sp, fontWeight = FontWeight.Medium) },
|
||||
navigationIcon = {})
|
||||
}
|
46
servicing/src/main/java/com/za/base/view/loading.kt
Normal file
46
servicing/src/main/java/com/za/base/view/loading.kt
Normal file
@ -0,0 +1,46 @@
|
||||
package com.za.base.view
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import coil.decode.GifDecoder
|
||||
import coil.request.ImageRequest
|
||||
import com.za.servicing.R
|
||||
|
||||
|
||||
object LoadingManager {
|
||||
val showLoading = mutableStateOf(false)
|
||||
|
||||
fun showLoading() {
|
||||
showLoading.value = true
|
||||
}
|
||||
|
||||
fun hideLoading() {
|
||||
showLoading.value = false
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoadingView() {
|
||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
|
||||
AsyncImage(model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(R.drawable.gif_loading)
|
||||
.decoderFactory(GifDecoder.Factory())
|
||||
.build(), contentDescription = "加载中", modifier = Modifier
|
||||
.size(70.dp)
|
||||
.align(Alignment.Center))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class LoadingState {
|
||||
data object Loading : LoadingState()
|
||||
data object LoadingFailed : LoadingState()
|
||||
data object LoadingSuccess : LoadingState()
|
||||
}
|
24
servicing/src/main/java/com/za/bean/BaseResponse.kt
Normal file
24
servicing/src/main/java/com/za/bean/BaseResponse.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package com.za.bean
|
||||
|
||||
|
||||
/**
|
||||
* Created by DoggieX on 2017/7/26.
|
||||
*/
|
||||
class BaseResponse<T> {
|
||||
var code: Int = 0
|
||||
var msg: String? = null
|
||||
var message: String? = null
|
||||
var result: T? = null
|
||||
|
||||
val isOk: Boolean
|
||||
get() = code == 0 || code == 200
|
||||
|
||||
override fun toString(): String {
|
||||
return "BaseResponse{" +
|
||||
"code=" + code +
|
||||
", msg='" + msg + '\'' +
|
||||
", message='" + message + '\'' +
|
||||
", result=" + result +
|
||||
'}'
|
||||
}
|
||||
}
|
216
servicing/src/main/java/com/za/bean/DriverInfo.kt
Normal file
216
servicing/src/main/java/com/za/bean/DriverInfo.kt
Normal file
@ -0,0 +1,216 @@
|
||||
package com.za.bean
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class DriverInfo(
|
||||
var userPortrait : String? = "", //用户头像
|
||||
var token : String? = null, //
|
||||
var rongyunToken : String? = null,
|
||||
var logTime : String? = null,
|
||||
var userId : Int? = null, //用户id
|
||||
var userName : String? = null, //用户姓名
|
||||
var userPhone : String? = null, //用户手机号
|
||||
var callphone : String? = null,
|
||||
var supplierId : String? = null, //经销商id
|
||||
var supplierName : String? = null, //经销商名称
|
||||
var supplierType : Int? = null, //经销商类型 1子公司 2供应商
|
||||
var loginLogId : Int? = null,
|
||||
var serviceList : List<String>? = null, //车辆服务列表
|
||||
var assistUserCode : String? = null,
|
||||
val authStatus : Int? = null, // 0 未认证 1 已认证
|
||||
) : Parcelable {
|
||||
constructor(parcel : Parcel) : this(parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.createStringArrayList(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int) {
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel : Parcel, flags : Int) {
|
||||
parcel.writeString(userPortrait)
|
||||
parcel.writeString(token)
|
||||
parcel.writeString(rongyunToken)
|
||||
parcel.writeString(logTime)
|
||||
parcel.writeValue(userId)
|
||||
parcel.writeString(userName)
|
||||
parcel.writeString(userPhone)
|
||||
parcel.writeString(callphone)
|
||||
parcel.writeString(supplierId)
|
||||
parcel.writeString(supplierName)
|
||||
parcel.writeValue(supplierType)
|
||||
parcel.writeValue(loginLogId)
|
||||
parcel.writeStringList(serviceList)
|
||||
parcel.writeString(assistUserCode)
|
||||
parcel.writeValue(authStatus)
|
||||
}
|
||||
|
||||
override fun describeContents() : Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<DriverInfo> {
|
||||
override fun createFromParcel(parcel : Parcel) : DriverInfo {
|
||||
return DriverInfo(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size : Int) : Array<DriverInfo?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class GeneralInfo(
|
||||
var userId : Int? = null, //用户id
|
||||
val userName : String? = null, //用户名
|
||||
val userPhone : String? = null,
|
||||
val userPortrait : String? = null, //大头照
|
||||
val vehicleId : Int? = null,
|
||||
val vehicleName : String? = null,
|
||||
val vehicleState : Int? = null, //车辆状态 0 空闲 1 忙碌
|
||||
val supplierId : Int? = null,
|
||||
val supplierName : String? = null,
|
||||
val supplierType : Int?,
|
||||
val plateNumber : String? = null, //车牌号
|
||||
val serviceList : List<String>? = null,
|
||||
val deviceId : String? = null,
|
||||
val authStatus : Int? = null, // 0 未认证 1 已认证
|
||||
)
|
||||
|
||||
data class DriverIdentityAuthWebRequest(val vehicleId : Int? = null,
|
||||
val driverId : Int? = null,
|
||||
val redirectUrl : String = "https://www.sinoassist.com?esignAppScheme=zd_sinoassist://demo/realBack")
|
||||
|
||||
data class DriverIdentityAuthWebBean(val flowId : String? = null,
|
||||
val shortLink : String? = null,
|
||||
val url : String? = null)
|
||||
|
||||
|
||||
//public Integer update;//是否强制更新(1:强制更新;0:非强制更新)
|
||||
//public String path;//更新路径
|
||||
//public String newAppVersion;//版本号
|
||||
//public String description;//版本更新内容
|
||||
data class UpdateVersionBean(val update : Int? = null,
|
||||
val path : String? = null,
|
||||
val newAppVersion : String? = null,
|
||||
val description : String? = null)
|
||||
|
||||
//public String appVersion;//版本号
|
||||
//public Integer appType = 1;//app类型
|
||||
data class UpdateVersionRequest(val appVersion : String? = null, val appType : Int? = 1)
|
||||
|
||||
|
||||
// private String jobCode;
|
||||
// private String phone;
|
||||
// private String taskCode;
|
||||
// private String rescueVehicle;
|
||||
// private String deviceId;
|
||||
// private String vehicleId;
|
||||
data class LoginWithTaskRequest(val jobCode : String? = null,
|
||||
val phone : String? = null,
|
||||
val taskCode : String? = null,
|
||||
val rescueVehicle : String? = null,
|
||||
val deviceId : String? = null,
|
||||
val vehicleId : String? = null)
|
||||
|
||||
//{
|
||||
// "code": 0,
|
||||
// "msg": "请求成功",
|
||||
// "result": {
|
||||
// "supplierName": "上海安畅",
|
||||
// "vehicleName": "小小宋车辆(1)",
|
||||
// "supplierId": 1128,
|
||||
// "userPhone": "17630035658",
|
||||
// "userName": "宋志领",
|
||||
// "userId": 4967,
|
||||
// "token": "c969499d5ac9cbb9da2691ee7533f065",
|
||||
// "loginLogId": 10267,
|
||||
// "userPortrait": "http://file.sino-assist.com/group1/M00/04/E1/wKgBzGfZHjCAW8_MAAQsluXYmmc152.jpg?date=2025-03-18",
|
||||
// "serviceList": [
|
||||
// "故障--平板拖车",
|
||||
// "换胎",
|
||||
// "搭电",
|
||||
// "抢修",
|
||||
// "送水",
|
||||
// "缺汽油",
|
||||
// "缺机油",
|
||||
// "事故--平板拖车",
|
||||
// "困境救援",
|
||||
// "困境-吊车",
|
||||
// "困境救援-事故",
|
||||
// "困境救援-故障",
|
||||
// "电话技术支持",
|
||||
// "电话技术解决",
|
||||
// "派送备用钥匙",
|
||||
// "待命点间调拨",
|
||||
// "商品车运输",
|
||||
// "故障--大型拖车",
|
||||
// "故障--小型拖车",
|
||||
// "事故--大型拖车",
|
||||
// "事故--小型拖车",
|
||||
// "车辆检测",
|
||||
// "送防冻液",
|
||||
// "其他",
|
||||
// "故障拖车(免拖100公里)",
|
||||
// "代验车--取车",
|
||||
// "代验车--送车",
|
||||
// "代验车-取件",
|
||||
// "代验车--送件",
|
||||
// "应急道路救援",
|
||||
// "3吨拖车",
|
||||
// "8吨拖车",
|
||||
// "25吨拖车",
|
||||
// "50吨拖车",
|
||||
// "大力神拖车",
|
||||
// "代提车-拖车",
|
||||
// "现场小修--解档",
|
||||
// "地库救援(不含拖车)",
|
||||
// "故障拖车(免拖50公里)",
|
||||
// "充电拖车-双程",
|
||||
// "代客充电",
|
||||
// "充气",
|
||||
// "移动充电",
|
||||
// "代验车--现场代办",
|
||||
// "代验车--快递代办",
|
||||
// "代验车--取车代办",
|
||||
// "长途拖车",
|
||||
// "大板运输",
|
||||
// "商品车调拨",
|
||||
// "失车定位",
|
||||
// "医疗急救",
|
||||
// "高速救援",
|
||||
// "代客洗车",
|
||||
// "代提车-代驾",
|
||||
// "回程车运输",
|
||||
// "上门交付",
|
||||
// "充电拖车-单程"
|
||||
// ],
|
||||
// "vehicleId": 327732,
|
||||
// "supplierType": 1,
|
||||
// "jobNumber": "宋志领"
|
||||
// }
|
||||
//}
|
||||
data class LoginWithTaskBean(val supplierName : String? = null,
|
||||
val vehicleName : String? = null,
|
||||
val supplierId : Int? = null,
|
||||
val userPhone : String? = null,
|
||||
val userName : String? = null,
|
||||
val userId : Int? = null,
|
||||
val token : String? = null,
|
||||
val loginLogId : Int? = null,
|
||||
val userPortrait : String? = null,
|
||||
val serviceList : List<String>? = null,
|
||||
val jobNumber : String? = null,
|
||||
val vehicleId : Int? = null,
|
||||
val supplierType : Int? = null)
|
12
servicing/src/main/java/com/za/bean/ImageBean.kt
Normal file
12
servicing/src/main/java/com/za/bean/ImageBean.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package com.za.bean
|
||||
|
||||
data class ImageBean(
|
||||
var data: String? = null,
|
||||
var msg: String? = null,
|
||||
var success: Boolean? = null,
|
||||
var code: Int? = null) {
|
||||
|
||||
fun isOk(): Boolean {
|
||||
return code == 200
|
||||
}
|
||||
}
|
329
servicing/src/main/java/com/za/bean/JpushBean.kt
Normal file
329
servicing/src/main/java/com/za/bean/JpushBean.kt
Normal file
@ -0,0 +1,329 @@
|
||||
package com.za.bean
|
||||
|
||||
import com.blankj.utilcode.util.EncodeUtils
|
||||
import com.blankj.utilcode.util.TimeUtils
|
||||
import com.za.base.AppConfig
|
||||
import java.io.Serializable
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
data class JpushBean(
|
||||
val pushType: Int? = null, //0 新任务 1 任务取消 2 任务变更
|
||||
val taskId: Int? = null,//订单号 "taskId":5313005
|
||||
val taskCode: String? = null, //订单编码 "taskCode":"ZD20190308009965"
|
||||
val customerName: String? = null, //客户姓名 "customerName":"越继安"
|
||||
val customerPhone: String? = null, //客户电话 "customerPhone":"18078815268"
|
||||
val carBrand: String? = null, //车辆品牌 "carBrand":""
|
||||
val carModel: String? = null,//车辆型号 "carModel":"秦"
|
||||
val contract: String? = null, //车辆型号 "carModel":"秦"
|
||||
val typeDesc: String? = null, //推送的附加消息 revoke 撤回 giveUp放弃 reDispatch改派
|
||||
|
||||
val carNo: String? = null, //客户车车牌号 "carNo":"粤AF53918"
|
||||
val taskState: String? = null, //订单状态 "taskState":"GOTO"
|
||||
val address: String? = null, //任务地址 "address":"广东省广州市白云区107国道石井凰岗路342号(白云黄石、同德围地区近庆丰兴隆公园)美景大酒店"
|
||||
val addressProperty: String? = null,//任务地址类型 "addressProperty":"地面"
|
||||
val hotline: String? = null, //任务地址类型 "addressProperty":"地面"
|
||||
val schedulingFinalRule: Int? = null, //案件类型 0 传统案件 1 聚合派工
|
||||
val addressRemark: String? = null,
|
||||
val distAddress: String? = null, //目的地地址 "distAddress":"广东省广州市白云区雅岗南大道"
|
||||
val distAddressRemark: String? = null,
|
||||
val expectArriveTime: String? = null, //预计到达时间 "expectArriveTime":"2019-03-08 05:11:07"
|
||||
val serviceTypeName: String? = null,//服务类型 "serviceTypeName":"故障--平板拖车"
|
||||
val dispatchTime: String? = null, //派单时间 "dispatchTime":"2019-03-08 04:26:07"
|
||||
val lat: Double? = null,
|
||||
val lng: Double? = null,
|
||||
val distLat: Double? = null,
|
||||
val distLng: Double? = null,
|
||||
val importantTip: String? = null,
|
||||
val tipContent: String? = null,
|
||||
val hasReplaceBatteryCapable: Int? = null ,//是否有更换电瓶的能力 1 搭电可以更换电瓶 2搭电不可以更换电瓶 其他的不展示
|
||||
var voiceType : Int?=null //语音提示类型 1小修单 2拖车单 3困境单
|
||||
) : Serializable {
|
||||
fun isNeedCallCustomPhone(): Boolean {
|
||||
return "210" != customerPhone && "230" != customerPhone
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class NewOrderRequestBean(val vehicleId: Int? = null)
|
||||
|
||||
//历史订单状态
|
||||
data class HistoryTaskBean(
|
||||
val taskId: Int? = null, //订单号
|
||||
val taskCode: String? = null, //订单编码
|
||||
val userOrderId: Int? = null,
|
||||
val userOrderCode: String? = null, //只有电子工单历史补传才需要传
|
||||
val supplierAudit: Int? = null, //审核状态 0待补充 1 待审核 2审核不通过 3 审核通过
|
||||
val supplierAuditStr: String? = null, //审核状态描述
|
||||
val missingContent: String? = null, //缺失内容
|
||||
val auditFailReason: String? = null, //审核失败原因
|
||||
val customerName: String? = null, //客户姓名
|
||||
val customerPhone: String? = null, //客户电话
|
||||
val carBrand: String? = null, //车辆品牌
|
||||
val carModel: String? = null, //车辆型号
|
||||
val carNo: String? = null, //客户车车牌号 "
|
||||
val carVin: String? = null, //客户车Vin码
|
||||
val taskState: String? = null, //订单状态
|
||||
val address: String? = null, //任务地址
|
||||
val addressRemark: String? = null, //事发地地址补充
|
||||
val addressProperty: String? = null, //任务地址类型
|
||||
val lat: Double? = null,
|
||||
val lng: Double? = null,
|
||||
val distAddress: String? = null, //目的地地址
|
||||
val distAddressRemark: String? = null, //目的地地址补充
|
||||
val distLat: Double? = null,
|
||||
val distLng: Double? = null,
|
||||
val expectArriveTime: String? = null, //预计到达时间
|
||||
val serviceTypeName: String? = null, //服务类型
|
||||
val orderSource: String? = null, //
|
||||
val flowType: Int? = null, //流程类型 "flowType":2
|
||||
val settleType: Int? = null, //结算类型 1 月结 2 现金
|
||||
val supplierId: Int? = null,
|
||||
val successTime: String? = null, //完成时间 "dispatchTime":"2019-03-08 04:26:07"
|
||||
val verifyType: Int? = null, //验证类型 "verifyType":1
|
||||
val verifyValue: String? = null, //验证内容
|
||||
val holdon: Boolean? = null, //是否被挂起 "holdon":false
|
||||
val externalCode: String? = null, //流水号
|
||||
val vehicleName: String? = null,
|
||||
val traceIdAB: Int? = null,
|
||||
val mileageAB: Int? = null,
|
||||
val traceABUrl: String? = null,
|
||||
val traceIdBC: Int? = null,
|
||||
val mileageBC: Int? = null,
|
||||
val mileageCA: Int? = null,
|
||||
val traceBCUrl: String? = null,
|
||||
val createTime: String? = null,
|
||||
val acceptTime: String? = null,
|
||||
val arriveTime: String? = null,
|
||||
val arriveDestTime: String? = null,
|
||||
val giveupTime: String? = null,
|
||||
val giveupAddress: String? = null,
|
||||
val giveupLat: Double? = null,
|
||||
val giveupLng: Double? = null,
|
||||
val needWaterMarker: Boolean? = null,
|
||||
val needShowPhoneBrand: Boolean? = null,
|
||||
val policyNo: String? = null, // 平安拖车责任险 保单号
|
||||
|
||||
val electronOrderState: String? = null, //电子工单状态
|
||||
|
||||
val hasReplaceBatteryCapable: Int? = null, //是否具有更换电瓶的能力
|
||||
|
||||
val hasReplaceBattery: Int = 0 //1已更换过 2有更换能力但没更换过 0无
|
||||
) : Serializable {
|
||||
|
||||
fun getEleOrderH5Url(): String? {
|
||||
if (electronOrderState == null || electronOrderState != "3") {
|
||||
return null
|
||||
}
|
||||
val paramQuery = (("userOrderId=$userOrderId").toString() + "&userOrderCode=" + taskCode) + "&supplierId=" + userOrderId
|
||||
return AppConfig.Resource_URL + "/electronicWorkOrder/index.html?" + EncodeUtils.base64Encode2String(paramQuery.toByteArray(StandardCharsets.UTF_8))
|
||||
}
|
||||
|
||||
fun getSettleTypeStr(): String {
|
||||
return when (settleType) {
|
||||
1 -> {
|
||||
"月结"
|
||||
}
|
||||
|
||||
2 -> {
|
||||
"现金"
|
||||
}
|
||||
|
||||
else -> {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class HistoryPhotoTemplates(
|
||||
/**
|
||||
* 任务节点
|
||||
*/
|
||||
val taskStatus: Int? = null,
|
||||
/**
|
||||
* 图片节点名称
|
||||
*/
|
||||
val taskStatusString: String? = null,
|
||||
/**
|
||||
* 该节点下的所有历史照片信息
|
||||
*/
|
||||
val photoList: List<HistoryPhotoTemplateItem>? = null
|
||||
)
|
||||
|
||||
data class HistoryPhotoTemplateItem(
|
||||
val photoUrl: String? = null,
|
||||
val tag: String? = null,
|
||||
val imageTitle: String? = null,
|
||||
val lon: String? = null,
|
||||
val lat: String? = null,
|
||||
val templatePhotoType: Int = 0, //1 照片 2 工单照片
|
||||
val takePhotoTime: String? = null,
|
||||
val takeAddress: String? = null,
|
||||
val uploadStatus: String? = null,
|
||||
val uploadState: Int? = 0, //0 上传中 1 上传成功 2 上传失败
|
||||
) {
|
||||
|
||||
//获取拍照时间
|
||||
fun getPhotoTakeTime(taskState: String, historyTaskBean: HistoryTaskBean?): String? {
|
||||
if (!takePhotoTime.isNullOrBlank()) {
|
||||
return takePhotoTime
|
||||
}
|
||||
return when (taskState) {
|
||||
"13001", "15001" -> historyTaskBean?.arriveTime
|
||||
"17001" -> {
|
||||
if (2 == historyTaskBean?.flowType) {
|
||||
historyTaskBean.arriveDestTime
|
||||
} else {
|
||||
historyTaskBean?.arriveTime
|
||||
}
|
||||
}
|
||||
|
||||
"18001" -> {
|
||||
historyTaskBean?.successTime
|
||||
}
|
||||
|
||||
"18100" -> historyTaskBean?.giveupTime
|
||||
else -> TimeUtils.getNowString()
|
||||
}
|
||||
}
|
||||
|
||||
fun getPhotoLat(taskState: String, historyTaskBean: HistoryTaskBean?): Double? {
|
||||
if (!lat.isNullOrBlank()) {
|
||||
return lat.toDouble()
|
||||
}
|
||||
return when (taskState) {
|
||||
"13001", "15001" -> historyTaskBean?.lat
|
||||
"17001", "18001" -> {
|
||||
if (2 == historyTaskBean?.flowType) {
|
||||
historyTaskBean.distLat
|
||||
} else {
|
||||
historyTaskBean?.lat
|
||||
}
|
||||
}
|
||||
|
||||
"18100" -> historyTaskBean?.giveupLat
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun getPhotoLng(taskState: String, historyTaskBean: HistoryTaskBean?): Double? {
|
||||
if (!lon.isNullOrBlank()) {
|
||||
return lon.toDouble()
|
||||
}
|
||||
return when (taskState) {
|
||||
"13001", "15001" -> historyTaskBean?.lng
|
||||
"17001", "18001" -> {
|
||||
if (2 == historyTaskBean?.flowType) {
|
||||
historyTaskBean.distLng
|
||||
} else {
|
||||
historyTaskBean?.lng
|
||||
}
|
||||
}
|
||||
|
||||
"18100" -> historyTaskBean?.giveupLng
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun getPhotoAddress(taskState: String, historyTaskBean: HistoryTaskBean?): String? {
|
||||
if (!takeAddress?.replace("[", "")?.replace("]", "").isNullOrBlank()) {
|
||||
return takeAddress
|
||||
}
|
||||
return when (taskState) {
|
||||
"13001", "15001" -> historyTaskBean?.address
|
||||
"17001", "18001" -> {
|
||||
if (2 == historyTaskBean?.flowType) {
|
||||
historyTaskBean.distAddress
|
||||
} else {
|
||||
historyTaskBean?.address
|
||||
}
|
||||
}
|
||||
|
||||
"18100" -> historyTaskBean?.giveupAddress
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
data class TaskSettlementAndTraceBean(
|
||||
val settleMap: SettleMapBean? = null,
|
||||
val traceIdAB: Int? = null,
|
||||
val mileageAB: Int? = null,
|
||||
val traceABUrl: String? = null,
|
||||
val traceIdBC: Int? = null,
|
||||
val mileageBC: Int? = null,
|
||||
val mileageCA: Int? = null,
|
||||
val traceBCUrl: String? = null
|
||||
)
|
||||
|
||||
data class SettleMapBean(
|
||||
/*————————————子公司——————————*/
|
||||
val startPrice: Int? = null, //起步价
|
||||
val unitPrice: Double? = null, //每公里单价
|
||||
val mileage: Int? = null, //公里数
|
||||
val basePrice: Double? = null,//基本费用
|
||||
val roadFee: Int? = null,//路桥费
|
||||
val assistFee: Int? = null, //辅助总费用
|
||||
|
||||
/*———————————————供应商———————————————*/ // public Integer startMileage;//出发段公里数
|
||||
// public Integer carryMileage;//背车段公里数
|
||||
// public Integer backMileage;//回程段公里数
|
||||
val startRoadFee: Int? = null, //出发段过境费
|
||||
val carryRoadFee: Int? = null, //背车段过境费
|
||||
val backRoadFee: Int? = null, //回程段过境费
|
||||
val waitFee: Int? = null,//等候费
|
||||
val wheelFee: Int? = null,//辅助轮费
|
||||
val wheelNum: Int? = null, //辅助轮个数
|
||||
val wheelPrice: Int? = null, //辅助轮单价
|
||||
val dilemmaFee: Int? = null,//困境费
|
||||
val basementFee: Int? = null, //其他费用
|
||||
val totalFee: Double? = null,//费用总计
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Created by zhangj on 2019/12/4.
|
||||
*/
|
||||
class SettleInfoRequest(
|
||||
val orderId: Int? = null,
|
||||
val supplierType: Int? = null,
|
||||
val startMileage: Int? = null,
|
||||
val carryMileage: Int? = null,
|
||||
val backMileage: Int? = null,
|
||||
val startRoadFee: Int? = null,
|
||||
val carryRoadFee: Int? = null,
|
||||
val backRoadFee: Int? = null,
|
||||
val waitFee: Int? = null,
|
||||
val settleType: Int? = null,
|
||||
val wheelFee: Int? = null, //辅助轮费
|
||||
val wheelNum: Int? = null,
|
||||
val wheelPrice: Int? = null,
|
||||
val dilemmaFee: Int? = null, //困境费
|
||||
val basementFee: Int? = null, //其他费用
|
||||
val totalFee: Double? = null, //
|
||||
val startPrice: Int? = null,
|
||||
val unitPrice: Double? = null,
|
||||
val mileage: Int? = null,
|
||||
val basePrice: Double? = null,
|
||||
val assistFee: Int? = null,
|
||||
val imgInfo: String? = null,
|
||||
val imgPath: String? = null,
|
||||
)
|
||||
|
||||
data class AMapTraceBean(
|
||||
val counts: Int? = null,
|
||||
val distance: Double? = null,
|
||||
val time: Long? = null,
|
||||
val trid: Int? = null,
|
||||
val points: List<PointsBean>? = null)
|
||||
|
||||
data class PointsBean(
|
||||
val accuracy: Double? = null,
|
||||
val direction: Double? = null,
|
||||
val height: Double? = null,
|
||||
val locatetime: Long? = null,
|
||||
val location: String? = null,
|
||||
val speed: Double? = null)
|
21
servicing/src/main/java/com/za/bean/PaymentInfoBean.kt
Normal file
21
servicing/src/main/java/com/za/bean/PaymentInfoBean.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package com.za.bean
|
||||
|
||||
data class PaymentInfoBean(
|
||||
var userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null,
|
||||
val adjustAmount: Int? = null, //调整金额
|
||||
val startPrice: Int? = null, //起步价
|
||||
val limitedMileage: Int? = null, //免托公里数
|
||||
val amount: Float? = null, //收款金额
|
||||
val unitPrice: Float? = null, //单价 //如果单价为 null 并且payItem 为2 则为超限无单价项目
|
||||
val mileage: Float? = null, //公里数
|
||||
val settlementRule: String? = null,//结算规则
|
||||
val userPhone: String? = null,//客户手机号
|
||||
val tradeState: Int? = null, //交易状态 交易状态 1 待交易 2交易完成
|
||||
val isPayment: Boolean? = null, //是否需要进行客户收款
|
||||
val orderPayDetailId: Int? = null, //订单支付明细ID
|
||||
val payItem: Int? = null, //支付项目 1.现金项目 2超限项目
|
||||
val contractSettleRule: Int? = null, //合同结算规则 1系统结算 2一口价结算 其中 一口价的不允许修改金额
|
||||
val calculateAmount: Float? = null, //计算金额
|
||||
val askPayment: Boolean? = null, //是否询问是否收款
|
||||
)
|
@ -0,0 +1,33 @@
|
||||
package com.za.bean
|
||||
|
||||
data class UploadChangeBatteryRequest(
|
||||
var userOrderId: Int? = null,
|
||||
val hasReplaceBattery: Int? = null, //是否更换电瓶 1 更换
|
||||
val replaceBatteryImgList: List<String>? = null,
|
||||
val isPayment: Boolean? = null //是否需要付款
|
||||
)
|
||||
|
||||
data class FetchChangeBatteryPhotoRequest(
|
||||
val userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null//是否更换电瓶
|
||||
)
|
||||
|
||||
data class ChangeBatteryResponse(
|
||||
val userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null,
|
||||
val imageType: Int? = null,//1 开案照片,2 医疗救援附件,3 开案凭证照片,4议价凭证照片,5电子工单,6换电瓶照片
|
||||
val imageUrl: String? = null,
|
||||
val tag: String? = null
|
||||
)
|
||||
|
||||
data class BatteryCostQueryBean(
|
||||
var isPayment: Boolean? = null,
|
||||
val userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null,
|
||||
val amount: Float? = null,
|
||||
val userPhone: String? = null,
|
||||
val tradeState: Int? = null//交易状态 1 待交易 2 交易完成 6 交易关闭
|
||||
)
|
||||
|
||||
data class BatteryCostQueryRequest(var userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null)
|
56
servicing/src/main/java/com/za/bean/VehicleInfo.kt
Normal file
56
servicing/src/main/java/com/za/bean/VehicleInfo.kt
Normal file
@ -0,0 +1,56 @@
|
||||
package com.za.bean
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class VehicleInfo(
|
||||
var vehicleId: Int? = null, //车辆id
|
||||
var vehicleName: String? = null,//车辆名称(车牌号)
|
||||
var userName: String? = null, //当前用车司机姓名(工号)
|
||||
var userPhone: String? = null, //当前用车司机电话
|
||||
var status: Int? = null, //车辆状态 0 不在线 1 在线
|
||||
val plateNumber: String? = null, //车牌号
|
||||
var vehicleState: Int? = null, //车辆是否空闲 0 空闲 1 忙碌 2置忙 8维修
|
||||
var terminalId: Long? = null, //车辆高德tid
|
||||
var exitMileage: Int? = null, //上次退出公里数
|
||||
var supplierType: Int? = null, //经销商类型
|
||||
) : Parcelable {
|
||||
constructor(parcel : Parcel) : this(parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Long::class.java.classLoader) as? Long,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int) {
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel : Parcel, flags : Int) {
|
||||
parcel.writeValue(vehicleId)
|
||||
parcel.writeString(vehicleName)
|
||||
parcel.writeString(userName)
|
||||
parcel.writeString(userPhone)
|
||||
parcel.writeValue(status)
|
||||
parcel.writeString(plateNumber)
|
||||
parcel.writeValue(vehicleState)
|
||||
parcel.writeValue(terminalId)
|
||||
parcel.writeValue(exitMileage)
|
||||
parcel.writeValue(supplierType)
|
||||
}
|
||||
|
||||
override fun describeContents() : Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<VehicleInfo> {
|
||||
override fun createFromParcel(parcel : Parcel) : VehicleInfo {
|
||||
return VehicleInfo(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size : Int) : Array<VehicleInfo?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
28
servicing/src/main/java/com/za/bean/bean.kt
Normal file
28
servicing/src/main/java/com/za/bean/bean.kt
Normal file
@ -0,0 +1,28 @@
|
||||
package com.za.bean
|
||||
|
||||
//public String reportType;//报备类型名称
|
||||
// public String reportTemplate;//报备内容模板
|
||||
data class ReportItem(val reportType: String? = null,
|
||||
val reportTemplate: String? = null)
|
||||
|
||||
|
||||
//提交报备内容
|
||||
// public String taskId;//关联的任务Id
|
||||
// public String reportType;//报备类型名称
|
||||
// public String reportTemplate;//报备内容模板
|
||||
data class ReportInfoRequest(val taskId: String? = null,
|
||||
val reportType: String? = null,
|
||||
val reportTemplate: String? = null)
|
||||
|
||||
//报备历史请求
|
||||
data class ReportHistoryRequest(val taskId: String? = null)
|
||||
|
||||
|
||||
// public String reportType;//报备类型名称
|
||||
// public String reportTemplate;//报备内容模板
|
||||
// public String createTime;//备注时间
|
||||
// public int state;//1 未处理 2 已处理
|
||||
data class ReportHistoryBean(val reportType: String? = null,
|
||||
val reportTemplate: String? = null,
|
||||
val createTime: Long? = null,
|
||||
val state: Int? = null)
|
90
servicing/src/main/java/com/za/bean/db/ChangeBatteryPhoto.kt
Normal file
90
servicing/src/main/java/com/za/bean/db/ChangeBatteryPhoto.kt
Normal file
@ -0,0 +1,90 @@
|
||||
package com.za.bean.db
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "change_battery_photo")
|
||||
data class ChangeBatteryPhoto(
|
||||
//图片id
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long? = null,
|
||||
|
||||
// 流程节点
|
||||
var taskNode: Int? = null,
|
||||
|
||||
//图片描述
|
||||
var imageDescription: String? = null,
|
||||
|
||||
//图片标题
|
||||
var imageTitle: String? = null,
|
||||
|
||||
//标签
|
||||
var tag: String? = null,
|
||||
|
||||
// 是否必拍 0 非必拍 1必拍
|
||||
var doHaveFilm: Int? = null,
|
||||
|
||||
//封面地址
|
||||
var photoUrl: String? = null,
|
||||
|
||||
//图片名称
|
||||
var photoName: String? = null,
|
||||
|
||||
//图片类型
|
||||
var photoType: Int? = null, //1 走单照片 2 工单照片 3更换电瓶照片
|
||||
|
||||
//创建日期
|
||||
var createTime: String? = null,
|
||||
|
||||
var numbering: String? = null, //图片编号
|
||||
|
||||
var recognizeType: Int? = null, //ocr识别类型 0 无 1车牌号 2车架号
|
||||
|
||||
var photoTip: String? = null, //拍摄提示
|
||||
|
||||
var photoPath: String? = null, //照片路径
|
||||
|
||||
var photoContract: String? = null, //对比照
|
||||
|
||||
var photoNum: Int = 1, //对比照
|
||||
|
||||
var mustPhoto: Boolean = false, //是否必拍照片
|
||||
|
||||
var photoStatus: Int? = null, // 0 未上传 1 准备上传 2 已上传 3 上传失败 4 上传中 5 照片不符合条件 6 照片缺失地址信息 7 ocr车架号识别出错
|
||||
|
||||
var uploadedPath: String? = null,
|
||||
|
||||
var photoInfo: String? = null,
|
||||
|
||||
var index: Int? = null,
|
||||
|
||||
var userOrderId: Int? = null,
|
||||
|
||||
var taskId: Int? = null,
|
||||
|
||||
var taskCode: String? = null) {
|
||||
fun convertPhotoStatusStr(status: Int): String {
|
||||
return when (status) {
|
||||
2 -> "已上传"
|
||||
3 -> "上传失败"
|
||||
4 -> "上传中"
|
||||
5 -> "照片不符合条件"
|
||||
6 -> "照片缺失地址信息"
|
||||
7 -> "车架号识别出错"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fun getPhotoStatusColor(): Color {
|
||||
return when (this.photoStatus) {
|
||||
1 -> Color(0xFF2DBBF9)
|
||||
2 -> Color(0xFF32CD32)
|
||||
3 -> Color.Red
|
||||
4 -> Color(0xFF2DBBF9)
|
||||
5 -> Color.Red
|
||||
6 -> Color.Red
|
||||
else -> Color(0xFF2DBBF9)
|
||||
}
|
||||
}
|
||||
}
|
271
servicing/src/main/java/com/za/bean/db/NewPhotoTemplateBean.java
Normal file
271
servicing/src/main/java/com/za/bean/db/NewPhotoTemplateBean.java
Normal file
@ -0,0 +1,271 @@
|
||||
package com.za.bean.db;
|
||||
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
@Entity(tableName = "new_table_photo_template")
|
||||
public class NewPhotoTemplateBean {
|
||||
//图片id
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public long id;
|
||||
|
||||
// 流程节点
|
||||
private int taskNode;
|
||||
//图片描述
|
||||
private String imageDescription;
|
||||
//图片标题
|
||||
private String imageTitle;
|
||||
//标签
|
||||
private String tag;
|
||||
// 是否必拍 0 非必拍 1必拍
|
||||
private int doHaveFilm;
|
||||
//封面地址
|
||||
private String photoUrl;
|
||||
//图片名称
|
||||
private String photoName;
|
||||
//图片类型
|
||||
private int photoType;//1 走单照片 2 工单照片 3更换电瓶照片
|
||||
//创建日期
|
||||
private String createTime;
|
||||
private String numbering;//图片编号
|
||||
private Integer recognizeType;//ocr识别类型 0 无 1车牌号 2车架号
|
||||
|
||||
private String photoTip = "";//拍摄提示
|
||||
private String photoPath = "";//照片路径
|
||||
private String photoContract = "";//对比照
|
||||
private int photoNum = 1;//对比照
|
||||
private Boolean mustPhoto = false;//是否必拍照片
|
||||
private int photoStatus = 0;// 0 未上传 1 准备上传 2 已上传 3 上传失败 4 上传中 5 照片不符合条件 6 照片缺失地址信息 7 ocr车架号识别出错
|
||||
private String uploadedPath = "";
|
||||
private String photoInfo;
|
||||
private int index;
|
||||
private int userOrderId;
|
||||
private int taskId;
|
||||
private String taskCode;
|
||||
|
||||
public NewPhotoTemplateBean() {
|
||||
this.id = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getTaskNode() {
|
||||
return taskNode;
|
||||
}
|
||||
|
||||
public void setTaskNode(int taskNode) {
|
||||
this.taskNode = taskNode;
|
||||
}
|
||||
|
||||
public String getImageDescription() {
|
||||
return imageDescription;
|
||||
}
|
||||
|
||||
public void setImageDescription(String imageDescription) {
|
||||
this.imageDescription = imageDescription;
|
||||
}
|
||||
|
||||
public String getImageTitle() {
|
||||
return imageTitle;
|
||||
}
|
||||
|
||||
public void setImageTitle(String imageTitle) {
|
||||
this.imageTitle = imageTitle;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public int getDoHaveFilm() {
|
||||
return doHaveFilm;
|
||||
}
|
||||
|
||||
public void setDoHaveFilm(int doHaveFilm) {
|
||||
this.doHaveFilm = doHaveFilm;
|
||||
}
|
||||
|
||||
public String getPhotoUrl() {
|
||||
return photoUrl;
|
||||
}
|
||||
|
||||
public void setPhotoUrl(String photoUrl) {
|
||||
this.photoUrl = photoUrl;
|
||||
}
|
||||
|
||||
public String getPhotoName() {
|
||||
return photoName;
|
||||
}
|
||||
|
||||
public void setPhotoName(String photoName) {
|
||||
this.photoName = photoName;
|
||||
}
|
||||
|
||||
public int getPhotoType() {
|
||||
return photoType;
|
||||
}
|
||||
|
||||
public void setPhotoType(int photoType) {
|
||||
this.photoType = photoType;
|
||||
}
|
||||
|
||||
public String getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(String createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public String getNumbering() {
|
||||
return numbering;
|
||||
}
|
||||
|
||||
public void setNumbering(String numbering) {
|
||||
this.numbering = numbering;
|
||||
}
|
||||
|
||||
public Integer getRecognizeType() {
|
||||
return recognizeType;
|
||||
}
|
||||
|
||||
public void setRecognizeType(Integer recognizeType) {
|
||||
this.recognizeType = recognizeType;
|
||||
}
|
||||
|
||||
public String getPhotoTip() {
|
||||
return photoTip;
|
||||
}
|
||||
|
||||
public void setPhotoTip(String photoTip) {
|
||||
this.photoTip = photoTip;
|
||||
}
|
||||
|
||||
public String getPhotoPath() {
|
||||
return photoPath;
|
||||
}
|
||||
|
||||
public void setPhotoPath(String photoPath) {
|
||||
this.photoPath = photoPath;
|
||||
}
|
||||
|
||||
public String getPhotoContract() {
|
||||
return photoContract;
|
||||
}
|
||||
|
||||
public void setPhotoContract(String photoContract) {
|
||||
this.photoContract = photoContract;
|
||||
}
|
||||
|
||||
public int getPhotoNum() {
|
||||
return photoNum;
|
||||
}
|
||||
|
||||
public void setPhotoNum(int photoNum) {
|
||||
this.photoNum = photoNum;
|
||||
}
|
||||
|
||||
public Boolean getMustPhoto() {
|
||||
return mustPhoto;
|
||||
}
|
||||
|
||||
public void setMustPhoto(Boolean mustPhoto) {
|
||||
this.mustPhoto = mustPhoto;
|
||||
}
|
||||
|
||||
public int getPhotoStatus() {
|
||||
return photoStatus;
|
||||
}
|
||||
|
||||
public void setPhotoStatus(int photoStatus) {
|
||||
this.photoStatus = photoStatus;
|
||||
}
|
||||
|
||||
public String getUploadedPath() {
|
||||
return uploadedPath;
|
||||
}
|
||||
|
||||
public void setUploadedPath(String uploadedPath) {
|
||||
this.uploadedPath = uploadedPath;
|
||||
}
|
||||
|
||||
public String getPhotoInfo() {
|
||||
return photoInfo;
|
||||
}
|
||||
|
||||
public void setPhotoInfo(String photoInfo) {
|
||||
this.photoInfo = photoInfo;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public int getUserOrderId() {
|
||||
return userOrderId;
|
||||
}
|
||||
|
||||
public void setUserOrderId(int userOrderId) {
|
||||
this.userOrderId = userOrderId;
|
||||
}
|
||||
|
||||
public int getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
public void setTaskId(int taskId) {
|
||||
this.taskId = taskId;
|
||||
}
|
||||
|
||||
public String getTaskCode() {
|
||||
return taskCode;
|
||||
}
|
||||
|
||||
public void setTaskCode(String taskCode) {
|
||||
this.taskCode = taskCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NewPhotoTemplateBean{" +
|
||||
"id=" + id +
|
||||
", taskNode=" + taskNode +
|
||||
", imageDescription='" + imageDescription + '\'' +
|
||||
", imageTitle='" + imageTitle + '\'' +
|
||||
", tag='" + tag + '\'' +
|
||||
", doHaveFilm=" + doHaveFilm +
|
||||
", photoUrl='" + photoUrl + '\'' +
|
||||
", photoName='" + photoName + '\'' +
|
||||
", photoType=" + photoType +
|
||||
", createTime='" + createTime + '\'' +
|
||||
", numbering='" + numbering + '\'' +
|
||||
", recognizeType=" + recognizeType +
|
||||
", photoTip='" + photoTip + '\'' +
|
||||
", photoPath='" + photoPath + '\'' +
|
||||
", photoContract='" + photoContract + '\'' +
|
||||
", photoNum=" + photoNum +
|
||||
", mustPhoto=" + mustPhoto +
|
||||
", photoStatus=" + photoStatus +
|
||||
", uploadedPath='" + uploadedPath + '\'' +
|
||||
", photoInfo='" + photoInfo + '\'' +
|
||||
", index=" + index +
|
||||
", userOrderId=" + userOrderId +
|
||||
", taskId=" + taskId +
|
||||
", taskCode='" + taskCode + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.za.bean.db.ele
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
|
||||
@Entity(tableName = "ele_car_damage_photo")
|
||||
data class EleCarDamagePhotoBean(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Long? = null,
|
||||
val path: String? = null,
|
||||
val orderId: Int? = null,
|
||||
val userOrderId: Int? = null,
|
||||
val isPhoto: Boolean? = null,
|
||||
val videoThumbnailPath: String? = null,
|
||||
val uploadStatus: Int? = null,// 1 上传成功 2 正在上传 3上传失败
|
||||
val serverPath: String? = null,
|
||||
val index: Int? = null
|
||||
) : Serializable {
|
||||
fun getStatusStr(): String {
|
||||
return when (uploadStatus) {
|
||||
1 -> "上传成功"
|
||||
2 -> "正在上传"
|
||||
3 -> "上传失败"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.za.bean.db.ele
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "ele_work_order")
|
||||
data class EleWorkOrderBean(
|
||||
@PrimaryKey(autoGenerate = false) val orderId: Int,
|
||||
val userOrderId: Int? = null,
|
||||
val date: String? = null, //日期 2023-10-20
|
||||
val serviceContent: String? = null, //服务须知内容
|
||||
val orderWorkStatus: Int? = null, //电子工单状态 1车辆损伤照片 2 客户签名页面 3 服务完成 接车人签名和服务人员签名状态
|
||||
val hasBad: Boolean? = null, //车辆是否有损伤
|
||||
val carNO: String? = null,//车牌号
|
||||
val carVin: String? = null,//车架号
|
||||
val orderType: String? = null, //救援类型
|
||||
val isSuccess: Int? = null, //服务是否成功 1 成功 其他失败
|
||||
val localCustomSignPath: String? = null,
|
||||
val localAcceptCarSignPath: String? = null,//接车人本地路径
|
||||
val localServicePeopleSignPath: String? = null, //服务人员签名
|
||||
val serverCustomSignPath: String? = null,
|
||||
val serverAcceptCarSignPath: String? = null, //远程地址
|
||||
val serverServicePeopleSignPath: String? = null,
|
||||
val hasCreatedEleWorkOrderPhoto: Boolean? = null, //是否已经生成电子工单照片
|
||||
val changeBattery: Boolean? = null //是否更换电瓶
|
||||
)
|
237
servicing/src/main/java/com/za/bean/db/order/OrderInfo.kt
Normal file
237
servicing/src/main/java/com/za/bean/db/order/OrderInfo.kt
Normal file
@ -0,0 +1,237 @@
|
||||
package com.za.bean.db.order
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "order_info")
|
||||
data class OrderInfo(
|
||||
@PrimaryKey(autoGenerate = false) var taskId: Int? = null, //订单号 "taskId":5313005
|
||||
var userOrderId: Int? = null, //订单id
|
||||
var taskCode: String? = null, //订单编码 "taskCode":"ZD20190308009965"
|
||||
var customerName: String? = null, //客户姓名 "customerName":"越继安"
|
||||
var customerPhone: String? = null,//客户电话 "customerPhone":"18078815268"
|
||||
var carBrand: String? = null,//车辆品牌 "carBrand":""
|
||||
var modelVinNo: String? = null,
|
||||
var carModel: String? = null, //车辆型号 "carModel":"秦"
|
||||
var carNo: String? = null, //客户车车牌号 "carNo":"粤AF53918"
|
||||
var carVin: String? = null,//客户车Vin码 "carVin":"LGXC76C30J0132942"
|
||||
var taskState: String? = null, //订单状态 "taskState":"GOTO"
|
||||
var nextState: String? = null,//下一步状态
|
||||
var electronOrderState: Int? = null, //电子工单状态:1检查损伤 2生成车辆检查表签字 3作业完成签字
|
||||
var address: String? = null, //任务地址 "address":"广东省广州市白云区107国道石井凰岗路342号(白云黄石、同德围地区近庆丰兴隆公园)美景大酒店"
|
||||
var addressProperty: String? = null, //任务地址类型 "addressProperty":"地面"
|
||||
var addressRemark: String? = null, //事发地地址补充
|
||||
var distAddress: String? = null, //目的地地址 "distAddress":"广东省广州市白云区雅岗南大道"
|
||||
var distAddressRemark: String? = null,//目的地地址补充
|
||||
var expectArriveTime: String? = null, //预计到达时间 "expectArriveTime":"2019-03-08 05:11:07"
|
||||
var serviceTypeName: String? = null, //服务类型 "serviceTypeName":"故障--平板拖车"
|
||||
var orderSource: String? = null, // "orderSource":"中道救援-比亚迪道路救援项目"
|
||||
var flowType: Int? = null, //流程类型 0 综合服务 1小修 2拖车 3取送件 4在线技术解决 5组合服务 6拖车简化流程 7武汉交管服务
|
||||
var settleType: Int? = null,//结算类型 1 月结 2 现金
|
||||
var settleTypeStr: String? = null, //结算类型
|
||||
var supplierId: Int? = null,
|
||||
var startTime: String? = null,//发车时间
|
||||
var operationTime: String? = null, //作业完成时间
|
||||
var dispatchTime: String? = null,//派单时间 "dispatchTime":"2019-03-08 04:26:07"
|
||||
var verifyType: Int? = null,//验证类型 "verifyType": 0 跳过验证 1和2 车牌和车架号验证 5 新车验证
|
||||
var verifyValue: String? = null,//验证内容 "verifyValue":"粤AF53918"
|
||||
var holdon: Boolean? = null,//是否被挂起 "holdon":false
|
||||
var isCurrent: Boolean? = null, //是否为正在做的任务
|
||||
var flow: String? = null, //流程状态 "flow":"START,GOTO,VERIFY,EXAMINE,OPERATION,SENDTO,SETTLEMENT,FINISH"
|
||||
var externalCode: String? = null, //
|
||||
var plateNumber: String? = null,
|
||||
var distaddressProperty: String? = null,
|
||||
var vehiclePointRemark: String? = null,
|
||||
var destinationRemark: String? = null,
|
||||
var lat: Double? = null,
|
||||
var lng: Double? = null,
|
||||
var distLat: Double? = null,
|
||||
var distLng: Double? = null,
|
||||
var hotline: String? = null,
|
||||
var createTime: String? = null,
|
||||
var acceptTime: String? = null,
|
||||
var arriveTime: String? = null,
|
||||
var arriveDestTime: String? = null,
|
||||
var needECDevice: Boolean? = null,
|
||||
var needDestAddress: Boolean? = null,
|
||||
var linkToDocs: Boolean? = null,
|
||||
var linkToDaDianH5: Boolean? = null,
|
||||
var carFactory: Boolean? = null,
|
||||
var needWaterMarker: Boolean? = null,
|
||||
var needShowPhoneBrand: Boolean? = null,
|
||||
var taskNotes: String? = null, //救援要求
|
||||
var feeStandard: String? = null, //收费标准
|
||||
var customerNotes: String? = null, //客户提醒
|
||||
var otherNotes: String? = null, //特殊提醒
|
||||
var isNewCar: Boolean? = null, //是否新车
|
||||
var ECDeviceString: String? = null,//搭电设备照片提示
|
||||
var customerReportImgs: String? = null, //客户上报照片,多张照片用;分隔
|
||||
var arriveRemind: String? = null,
|
||||
var arriveRemindLink: String? = null,
|
||||
var policyNo: String? = null, // 平安拖车责任险 保单号
|
||||
var advanceTime: Long? = null,
|
||||
var importantTip: String? = null,
|
||||
var hasReplaceBatteryCapable: Int? = null, //是否有更换电瓶的能力 1 搭电可以更换电瓶 2搭电不可以更换电瓶 其他的不展示
|
||||
var taskSuccessStatus: Int? = null, //作业是否完成 0 完成 1未完成 拖车默认完成
|
||||
var tyreNumber: Int? = null, //辅助费用
|
||||
) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readString(),
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Double::class.java.classLoader) as? Double,
|
||||
parcel.readValue(Double::class.java.classLoader) as? Double,
|
||||
parcel.readValue(Double::class.java.classLoader) as? Double,
|
||||
parcel.readValue(Double::class.java.classLoader) as? Double,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readString(),
|
||||
parcel.readValue(Long::class.java.classLoader) as? Long,
|
||||
parcel.readString(),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int,
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int) {
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeValue(taskId)
|
||||
parcel.writeValue(userOrderId)
|
||||
parcel.writeString(taskCode)
|
||||
parcel.writeString(customerName)
|
||||
parcel.writeString(customerPhone)
|
||||
parcel.writeString(carBrand)
|
||||
parcel.writeString(modelVinNo)
|
||||
parcel.writeString(carModel)
|
||||
parcel.writeString(carNo)
|
||||
parcel.writeString(carVin)
|
||||
parcel.writeString(taskState)
|
||||
parcel.writeString(nextState)
|
||||
parcel.writeValue(electronOrderState)
|
||||
parcel.writeString(address)
|
||||
parcel.writeString(addressProperty)
|
||||
parcel.writeString(addressRemark)
|
||||
parcel.writeString(distAddress)
|
||||
parcel.writeString(distAddressRemark)
|
||||
parcel.writeString(expectArriveTime)
|
||||
parcel.writeString(serviceTypeName)
|
||||
parcel.writeString(orderSource)
|
||||
parcel.writeValue(flowType)
|
||||
parcel.writeValue(settleType)
|
||||
parcel.writeString(settleTypeStr)
|
||||
parcel.writeValue(supplierId)
|
||||
parcel.writeString(startTime)
|
||||
parcel.writeString(operationTime)
|
||||
parcel.writeString(dispatchTime)
|
||||
parcel.writeValue(verifyType)
|
||||
parcel.writeString(verifyValue)
|
||||
parcel.writeValue(holdon)
|
||||
parcel.writeValue(isCurrent)
|
||||
parcel.writeString(flow)
|
||||
parcel.writeString(externalCode)
|
||||
parcel.writeString(plateNumber)
|
||||
parcel.writeString(distaddressProperty)
|
||||
parcel.writeString(vehiclePointRemark)
|
||||
parcel.writeString(destinationRemark)
|
||||
parcel.writeValue(lat)
|
||||
parcel.writeValue(lng)
|
||||
parcel.writeValue(distLat)
|
||||
parcel.writeValue(distLng)
|
||||
parcel.writeString(hotline)
|
||||
parcel.writeString(createTime)
|
||||
parcel.writeString(acceptTime)
|
||||
parcel.writeString(arriveTime)
|
||||
parcel.writeString(arriveDestTime)
|
||||
parcel.writeValue(needECDevice)
|
||||
parcel.writeValue(needDestAddress)
|
||||
parcel.writeValue(linkToDocs)
|
||||
parcel.writeValue(linkToDaDianH5)
|
||||
parcel.writeValue(carFactory)
|
||||
parcel.writeValue(needWaterMarker)
|
||||
parcel.writeValue(needShowPhoneBrand)
|
||||
parcel.writeString(taskNotes)
|
||||
parcel.writeString(feeStandard)
|
||||
parcel.writeString(customerNotes)
|
||||
parcel.writeString(otherNotes)
|
||||
parcel.writeValue(isNewCar)
|
||||
parcel.writeString(ECDeviceString)
|
||||
parcel.writeString(customerReportImgs)
|
||||
parcel.writeString(arriveRemind)
|
||||
parcel.writeString(arriveRemindLink)
|
||||
parcel.writeString(policyNo)
|
||||
parcel.writeValue(advanceTime)
|
||||
parcel.writeString(importantTip)
|
||||
parcel.writeValue(hasReplaceBatteryCapable)
|
||||
parcel.writeValue(taskSuccessStatus)
|
||||
parcel.writeValue(tyreNumber)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<OrderInfo> {
|
||||
override fun createFromParcel(parcel: Parcel): OrderInfo {
|
||||
return OrderInfo(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<OrderInfo?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.za.bean.db.order
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "photo_template_bean")
|
||||
data class PhotoTemplateInfo(
|
||||
@PrimaryKey(autoGenerate = true) val primaryId: Int? = null,
|
||||
val id: Int? = null,
|
||||
val taskNode: Int? = null,
|
||||
val imageDescription: String? = null,// 图片描述
|
||||
val imageTitle: String? = null,// 图片标题
|
||||
val tag: String? = null,// 标签
|
||||
val doHaveFilm: Int? = null,//是否必拍 0 非必拍 1 必拍
|
||||
val photoUrl: String? = null,//封面地址
|
||||
val photoName: String? = null,// 图片名称
|
||||
val photoType: Int? = null,// 1 照片 2 电子工单
|
||||
val createTime: String? = null,//创建时间
|
||||
val numbering: String? = null,// 图片编号
|
||||
val recognizeType: Int? = null,//orc 识别类型 0 无 1 车牌号 2 车架号
|
||||
//以下属性非后台返回属性
|
||||
val userOrderId: Int? = null,
|
||||
val taskCode: String? = null,
|
||||
val taskId: Int? = null,
|
||||
val photoLocalPath: String? = null,//照片未添加水印之前的位置
|
||||
val photoUploadPath: String? = null,//照片上传服务器之后的路径
|
||||
val photoLocalWaterMarkerPath: String? = null,//照片水印位置
|
||||
val photoUploadStatus: Int? = null,// 0 未上传 1 准备上传 2 已上传 3 上传失败 4 上传中 5 照片不符合条件 6 照片缺失地址信息 7 ocr车架号识别出错
|
||||
val photoUploadStatusStr: String? = null,
|
||||
var advanceTime: Long? = null,
|
||||
val needWaterMarker: Boolean? = null,
|
||||
val needShowPhoneBrand: Boolean? = null,
|
||||
val myCustomPhotoType: Int? = null,// 1 默认是服务中照片订单图片 2 历史中照片 3.更换电瓶照片 4.普通的照片
|
||||
// photo info字段
|
||||
val realTakePhotoTime: String? = null,//真实拍照时间
|
||||
val photoSource: Int? = null,//1相机 2 相册 3真实位置
|
||||
val time: String? = null,// 拍照时间
|
||||
val lat: Float? = null,
|
||||
val lng: Float? = null,
|
||||
val address: String? = null,
|
||||
) {
|
||||
fun convertPhotoStatusStr(status: Int): String {
|
||||
return when (status) {
|
||||
1 -> "准备上传"
|
||||
2 -> "已上传"
|
||||
3 -> "上传失败"
|
||||
4 -> "上传中"
|
||||
5 -> "照片不符合条件"
|
||||
6 -> "照片缺失地址信息"
|
||||
7 -> "车架号识别出错"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fun getPhotoStatusColor(): Color {
|
||||
return when (this.photoUploadStatus) {
|
||||
1 -> Color(0xFF2DBBF9)
|
||||
2 -> Color(0xFF32CD32)
|
||||
3 -> Color.Red
|
||||
4 -> Color(0xFF2DBBF9)
|
||||
5 -> Color.Red
|
||||
6 -> Color.Red
|
||||
else -> Color(0xFF2DBBF9)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.za.bean.db.water_marker
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "water_marker_item")
|
||||
data class WaterMarkerItemBean(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Int = 0,
|
||||
@ColumnInfo(name = "water_left") var left: Double = 0.0,
|
||||
@ColumnInfo(name = "water_right") val right: Double = 0.0,
|
||||
val top: Double = 0.0,
|
||||
val bottom: Double = 0.0,
|
||||
val content: String? = null,
|
||||
val color: String? = null,
|
||||
val wideFont: String? = null, //高度大于宽度的系数
|
||||
val highFont: String? = null, //高度大于宽度的系数
|
||||
val waterTemplateId: Int? = null,
|
||||
val taskCode: String? = null,
|
||||
val taskId: Int? = null,
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.za.bean.db.water_marker
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
|
||||
data class WaterMarkerTemplateAndItemRef(
|
||||
@Embedded
|
||||
val waterMarkerTemplateBean: WaterMarkerTemplateBean? = null,
|
||||
@Relation(parentColumn = "taskCode", entityColumn = "taskCode")
|
||||
val waterMarkerItemBeanList: List<WaterMarkerItemBean>? = null
|
||||
)
|
@ -0,0 +1,14 @@
|
||||
package com.za.bean.db.water_marker
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "water_template")
|
||||
data class WaterMarkerTemplateBean(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Int = 0,
|
||||
val templateId: Int? = null,
|
||||
val templateName: String? = null,
|
||||
val taskCode: String? = null,
|
||||
val taskId: Int? = null,
|
||||
val updateTime: String? = null)
|
@ -0,0 +1,17 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class AcceptOrderRequest(
|
||||
var userId: Int? = null,
|
||||
var taskId: Int? = null,
|
||||
var vehicleId: Int? = null,
|
||||
var taskCode: String? = null,
|
||||
var lat: Double? = null,
|
||||
var lng: Double? = null,
|
||||
var deviceId: String? = null,
|
||||
)
|
||||
|
||||
data class RefuseOrderRequest(
|
||||
var userId: Int? = null,
|
||||
var taskId: Int? = null,
|
||||
var vehicleId: Int? = null,
|
||||
var taskCode: String? = null)
|
5
servicing/src/main/java/com/za/bean/request/Auth.kt
Normal file
5
servicing/src/main/java/com/za/bean/request/Auth.kt
Normal file
@ -0,0 +1,5 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class DriverFaceCompareRequest(val vehicleId: Int? = null, val driverId: Int? = null, val photoUrl: String? = null)
|
||||
|
||||
data class DriverFaceCompareBean(val flowId: Int? = null, val shortLink: String? = null, val url: String? = null)
|
34
servicing/src/main/java/com/za/bean/request/LoginRequest.kt
Normal file
34
servicing/src/main/java/com/za/bean/request/LoginRequest.kt
Normal file
@ -0,0 +1,34 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class LoginRequest(
|
||||
var jobCode: String? = null, //工号
|
||||
var supplierCode: String? = null, //经销商代码
|
||||
var phone: String? = null, //密码
|
||||
var phoneModel: String? = null, //手机机型
|
||||
var vehicleId: Int? = null, //车辆id
|
||||
var enterMileage: Int? = null,//上班公里数
|
||||
var deviceInfo: String? = null, //手机信息
|
||||
var regId: String? = null, //手机信息
|
||||
var lat: Double? = null,
|
||||
var lng: Double? = null,
|
||||
var deviceId: String? = null,
|
||||
var dashboardPath: String? = null //里程表照片路径
|
||||
)
|
||||
|
||||
data class PhoneBean(
|
||||
var versionRelease: String? = null,//系统版本
|
||||
var model: String? = null,//手机型号
|
||||
var brand: String? = null,//手机厂商
|
||||
var appVersion: String? = null,//app版本
|
||||
)
|
||||
|
||||
data class VehicleLogoutRequest(
|
||||
var vehicleId: Int? = null, //车辆id
|
||||
var loginLogId: Int? = null ,//用户id
|
||||
var exitMileage: Int? = null,
|
||||
)
|
||||
|
||||
|
||||
data class VerifyCodeRequest(val phone : String? = null)
|
||||
|
||||
data class VerifyCodeResponse(val verifyCode : String? = null)
|
3
servicing/src/main/java/com/za/bean/request/Order.kt
Normal file
3
servicing/src/main/java/com/za/bean/request/Order.kt
Normal file
@ -0,0 +1,3 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class UploadPhotoBean(val img: String? = null)
|
56
servicing/src/main/java/com/za/bean/request/OrderRequest.kt
Normal file
56
servicing/src/main/java/com/za/bean/request/OrderRequest.kt
Normal file
@ -0,0 +1,56 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class OrderListRequest(
|
||||
val vehicleId: Int? = null,
|
||||
val deviceId: String? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* 照片模版请求数据
|
||||
*/
|
||||
data class PhotoTemplateRequest(val userOrderId: Int? = null)
|
||||
|
||||
|
||||
//照片识别
|
||||
data class OrderPhotoOcrRecognizeRequest(
|
||||
val userOrderId: Int? = null,
|
||||
val recognizeType: Int? = null,
|
||||
val imgUrl: String? = null,
|
||||
)
|
||||
|
||||
data class TaskFinishRequest(
|
||||
val userOrderId: Int? = null,
|
||||
val lat: Double? = null,
|
||||
val lng: Double? = null,
|
||||
val operateTime: Long? = null)
|
||||
|
||||
data class GiveUpTaskRequest(
|
||||
val taskId: Int? = null, //任务id
|
||||
val userId: Int? = null, //用户id
|
||||
val vehicleId: Int? = null, //车辆id
|
||||
val lat: Double? = null,
|
||||
val lng: Double? = null,
|
||||
val address: String? = null,
|
||||
val templatePhotoInfoList: List<String?>? = null, //照片数据
|
||||
val pushGiveUpFlag: Int? = null, //1 是 0否
|
||||
)
|
||||
|
||||
data class UpdatePhotoRequest(
|
||||
val taskId: Int? = null,
|
||||
val taskState: String? = null,
|
||||
val picturePosition: Int? = null, //图片位置
|
||||
val picturePath: String? = null,//图片路径
|
||||
val imgInfo: String? = null
|
||||
)
|
||||
|
||||
data class SwitchTaskRequest(val currentTaskId: Int? = null,
|
||||
val nextTaskId: Int? = null,
|
||||
val userId: Int? = null,
|
||||
val vehicleId: Int? = null)
|
||||
|
||||
data class HistoryTasksRequest(val userId: Int? = null, val vehicleId: Int? = null)
|
||||
|
||||
data class HistoryPhotoTemplateRequest(val taskId: Int? = null)
|
||||
|
||||
data class HistoryDetailRequest(val taskId: Int? = null)
|
||||
|
@ -0,0 +1,51 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class PaymentInfoRequest(
|
||||
var userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null)
|
||||
|
||||
// private int userOrderId;
|
||||
// private int taskOrderId;
|
||||
// private int onSite;//是否在现场 1是 0否
|
||||
// private Integer orderPayDetailId;//订单支付明细ID
|
||||
// private String userPhone;//客户号码,客户不在现场时必填
|
||||
data class CustomerPaymentCreateRequest(val userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null,
|
||||
val onSite: Int? = null,
|
||||
val orderPayDetailId: Int? = null,
|
||||
val userPhone: String? = null)
|
||||
|
||||
// private int userOrderId;
|
||||
// private int taskOrderId;
|
||||
// private Float amount;
|
||||
// private String payData;//二维码链接
|
||||
// private String payCode;//付款编号
|
||||
// private String plateNumber;//车牌号
|
||||
data class CustomerPaymentCreateBean(val userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null,
|
||||
val amount: Float? = null,
|
||||
val payData: String? = null,
|
||||
val payCode: String? = null,
|
||||
val plateNumber: String? = null)
|
||||
|
||||
//private Integer userOrderId;
|
||||
//private Integer taskOrderId;
|
||||
//private Float payAmount; //收款金额
|
||||
//private Float unitPrice;//单价 //如果单价为 null 并且payItem 为2 则为超限无单价项目
|
||||
//private Float mileage;//公里数
|
||||
//private Integer orderPayDetailId;//订单支付明细ID
|
||||
//private String updateRemark;//支付项目 1.现金项目 2超限项目
|
||||
//private Float calculateAmount;//计算金额
|
||||
//private Float adjustAmount;//调整金额
|
||||
//private boolean askPayment;
|
||||
|
||||
data class PaymentUpdateRequest(val userOrderId: Int? = null,
|
||||
val taskOrderId: Int? = null,
|
||||
val payAmount: Float? = null,
|
||||
val unitPrice: Float? = null,
|
||||
val mileage: Float? = null,
|
||||
val orderPayDetailId: Int? = null,
|
||||
val updateRemark: String? = null,
|
||||
val calculateAmount: Float? = null,
|
||||
val adjustAmount: Float? = null,
|
||||
val askPayment: Boolean? = null)
|
@ -0,0 +1,51 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class SaveEleOrderRequest(
|
||||
val taskOrderId: Int? = null,
|
||||
val userOrderId: Int? = null,
|
||||
val state: Int? = null,
|
||||
val hasDamage: Int? = null, //是否有算上 1 有
|
||||
val hasSuccess: Int? = null, // 作业是否完成 1成功 0失败
|
||||
val customerSignPath: String? = null, //客户签名照片
|
||||
val recipientSignPath: String? = null, //接车人签名
|
||||
val waitstaffSignPath: String? = null, //服务人员签名
|
||||
val damageFileList: List<String?>? = null, //损伤文件列表
|
||||
val userOrderCode: String? = null, //仅在历史补传电子工单时传
|
||||
val tyreNumber: Int? = null, //小轮个数
|
||||
val lng: Double? = null,
|
||||
val lat: Double? = null,
|
||||
val isFinish: Boolean = false, //订单是否完成进入历史 true 完成
|
||||
/**
|
||||
* 操作时间(时间戳)
|
||||
*/
|
||||
val operateTime: String? = null,
|
||||
|
||||
/**
|
||||
* 0 正常上传 1离线补传
|
||||
*/
|
||||
val offlineMode: Int? = null)
|
||||
|
||||
data class QueryEleOrderRequest(val taskOrderId: Int? = null,
|
||||
val userOrderId: Int? = null,
|
||||
val userOrderCode: String? = null)
|
||||
|
||||
|
||||
data class ElectronOrderResponse(val id: Int? = null,
|
||||
val userOrderId: Int? = null,
|
||||
val state: Int? = null,
|
||||
val hasDamage: Int? = null, //1 有损伤 2 无损伤
|
||||
val hasSuccess: Int? = null,
|
||||
val customerSignPath: String? = null,
|
||||
val recipientSignPath: String? = null,
|
||||
val waitstaffSignPath: String? = null,//
|
||||
val createTime: String? = null,
|
||||
val updateTime: String? = null,
|
||||
val damageFileList: List<String>? = null,
|
||||
val vinNo: String? = null,
|
||||
val plateNumberBean: PlateNumberBean? = null,
|
||||
val serviceId: Int? = null,
|
||||
val serviceName: String? = null,
|
||||
val flowId: Int? = null,
|
||||
val serviceTerm: String? = null)
|
||||
|
||||
data class PlateNumberBean(val value: String? = null)
|
157
servicing/src/main/java/com/za/bean/request/SubBean.kt
Normal file
157
servicing/src/main/java/com/za/bean/request/SubBean.kt
Normal file
@ -0,0 +1,157 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class TodayMaintainbean(
|
||||
val tag: String? = null,//
|
||||
val tagName: String? = null, //名称
|
||||
val description: String? = null, //描述
|
||||
val img: String? = null, //图片地址
|
||||
)
|
||||
|
||||
data class TodayMaintainRequest(val vehicleId: Int? = null)
|
||||
|
||||
data class TodayMaintainUploadRequest(
|
||||
val vehicleId: String? = null,
|
||||
val driverId: String? = null,
|
||||
val tag: String? = null,
|
||||
val tagImage: String? = null,
|
||||
)
|
||||
|
||||
|
||||
data class RecognizeRefuelOcrRequestBean(
|
||||
val imageUrl: String? = null,
|
||||
val address: String? = null,
|
||||
val longitude: Double? = null,
|
||||
val latitude: Double? = null,
|
||||
val vehicleId: Double? = null,
|
||||
val driverId: Double? = null,
|
||||
)
|
||||
|
||||
data class RecognizeRefuelTicketBean(
|
||||
var refuelCode: String? = null, //加油卡号
|
||||
var quantity: Double? = null,//加油量
|
||||
var amount: Double? = null,//加油金额
|
||||
var filePath: String? = null,
|
||||
)
|
||||
|
||||
data class RecognizeRefuelTicketRequestBean(
|
||||
val vehicleId: Int? = null,
|
||||
val driverId: Int? = null,
|
||||
val address: String? = null,
|
||||
val longitude: Double? = null,
|
||||
val latitude: Double? = null,
|
||||
val refuelCode: String? = null,
|
||||
val quantity: Double? = null,
|
||||
val amount: Double? = null,
|
||||
val mileage: Double? = null,
|
||||
val refuelTime: Double? = null,
|
||||
val receiptPhotoUrls: String? = null,
|
||||
val anotherPhotoUrls: String? = null,
|
||||
)
|
||||
|
||||
|
||||
data class RepairHistoryRequestBean(val id: Int? = null,
|
||||
val driverId: Int? = null,
|
||||
val vehicleId: Int? = null)
|
||||
|
||||
data class VehicleRepairBean(
|
||||
var id: Int? = null,//维保记录ID
|
||||
var stage: String? = null, //阶段 //1 维保申请审批中 2,"维保申请不通过" 3,"维保申请通过" 4,"维保完成审批中" 5,"维保完成不通过" 6,"维保完成通过" 9,"维保终止"
|
||||
var vehicleId: Int? = null,//车辆ID
|
||||
var vehicleName: String? = null, //车辆名称
|
||||
var plateNumber: String? = null, //车牌号
|
||||
var states: Int? = null,//维保状态
|
||||
var statesStr: String? = null, //维保状态-描述
|
||||
var address: String? = null, //维修地址
|
||||
var repairPoint: String? = null, //维修点名称
|
||||
var vehicleRepairId: Int? = null,//维修点Id
|
||||
var longitude: Double? = null, //维保地址经度
|
||||
var latitude: Double? = null, //维保地址纬度
|
||||
var storePhotoUrls: String? = null, //门店照片
|
||||
var paymentType: Int? = null,//支付类型 1挂账 2现金
|
||||
var amount: Double? = null, //维保金额
|
||||
var mileage: Int? = null,//车辆里程数
|
||||
var information: String? = null, //维保信息
|
||||
var maintenancePhotoUrls: String? = null, //维保照片/项目照片
|
||||
var voucherPhotoUrls: String? = null, //维保清单/凭证照片
|
||||
var auditRemark: String? = null, //维保审核备注
|
||||
var createTime: String? = null, //创建时间
|
||||
var updateTime: String? = null, //修改时间
|
||||
) {
|
||||
fun getPayTypeStr(): String {
|
||||
return when (paymentType) {
|
||||
1 -> "月结"
|
||||
2 -> "全包"
|
||||
3 -> "现金"
|
||||
4 -> "月结+现金"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//维保提价记录
|
||||
data class FetchVehicleMaintenanceSubmitHistoryRequestBean(val vehicleId: Int? = null, val driverId: Int? = null, val id: Int? = null)
|
||||
|
||||
/**
|
||||
* 今日保养的请求信息
|
||||
*/
|
||||
data class VehicleMaintenanceHistoryBean(
|
||||
val id: Int? = null,
|
||||
val stage: String? = null,//阶段 //1 维保申请审批中 2,"维保申请不通过" 3,"维保申请通过" 4,"维保完成审批中" 5,"维保完成不通过" 6,"维保完成通过" 9,"维保终止"
|
||||
val vehicleId: Int? = null,//车辆ID
|
||||
val vehicleName: String? = null,//
|
||||
val plateNumber: String? = null,//车牌号
|
||||
val states: Int? = null,//维保状态
|
||||
val statesStr: String? = null,//维保状态-描述
|
||||
val address: String? = null,//维修地址
|
||||
val repairPoint: String? = null,//维修点名称
|
||||
val vehicleRepairId: Int? = null,//维修点Id
|
||||
val longitude: Double? = null,//维保地址经度
|
||||
val latitude: Double? = null,//维保地址纬度
|
||||
val storePhotoUrls: String? = null,//门店照片
|
||||
val paymentType: Int? = null,//支付类型 1挂账 2现金
|
||||
val amount: Double? = null,//维保金额
|
||||
val mileage: Double? = null,//车辆里程数
|
||||
val information: String? = null,//维保信息
|
||||
val maintenancePhotoUrls: String? = null,//维保照片/项目照片
|
||||
val voucherPhotoUrls: String? = null,//维保清单/凭证照片
|
||||
val auditRemark: String? = null,//维保审核备注
|
||||
val createTime: String? = null,//创建时间
|
||||
val updateTime: String? = null//修改时间
|
||||
)
|
||||
|
||||
data class VehicleRepairPointMatcherItem(
|
||||
val id: Int? = null,
|
||||
val repairName: String? = null,
|
||||
val repairLon: Double? = null,
|
||||
val repairLat: Double? = null,
|
||||
val repairAddress: String? = null,
|
||||
val supplierId: Int? = null,
|
||||
val distance: Double? = null,
|
||||
val isSelected: Boolean? = false,
|
||||
)
|
||||
|
||||
data class VehicleRepairPointMatcherListRequest(
|
||||
val vehicleId: Int? = null,
|
||||
val address: String? = null,
|
||||
val longitude: Double? = null,
|
||||
val latitude: Double? = null)
|
||||
|
||||
data class VehicleMaintenanceSubmitRequest(
|
||||
val id: Int? = null,//维保记录ID
|
||||
val vehicleId: Int? = null,//车辆ID
|
||||
val driverId: Int? = null,//司机ID
|
||||
val submitType: Int? = null,//提交类型 1维修开始申请 2维保完成申请
|
||||
val address: String? = null,//维修地址
|
||||
val longitude: Double? = null,//维修地址经度
|
||||
val latitude: Double? = null,//维修地址纬度
|
||||
val vehicleRepairId: Int? = null,//维修点Id
|
||||
val storePhotoUrls: String? = null,//门店照片
|
||||
val paymentType: Int? = null,//支付类型和结算类型
|
||||
val amount: Double? = null,//维保金额
|
||||
val mileage: Int? = null,//车辆里程数
|
||||
val information: String? = null,//维保信息/项目
|
||||
val maintenancePhotoUrls: String? = null,//维保照片/项目照片
|
||||
val voucherPhotoUrls: String? = null,//维保清单/凭证照片
|
||||
val auditRemark: String? = null,//维保审核备注
|
||||
val remark: String? = null//维保备注
|
||||
)
|
@ -0,0 +1,75 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class UpdateTaskRequest(
|
||||
val id: Int? = null,
|
||||
val type: String? = null,
|
||||
val taskId: Int? = null,
|
||||
val userId: Int? = null,
|
||||
val vehicleId: Int? = null,
|
||||
val currentState: String? = null,
|
||||
val operateTime: String? = null,
|
||||
val lat: Double? = null,
|
||||
val lng: Double? = null,
|
||||
val address: String? = null,
|
||||
val offlineMode: Int? = null,
|
||||
val content: String? = null, //验证输入内容
|
||||
val newCarCode: String? = null,
|
||||
val flowType: Int? = null,
|
||||
val success: Int? = null, //作业是否完成 0 成功 1不成功 拖车默认成功
|
||||
val templatePhotoInfoList: List<String?>? = null
|
||||
)
|
||||
|
||||
|
||||
data class UpdateTaskBean(val nextState: String? = null, val address: Long? = null)
|
||||
|
||||
|
||||
data class TaskFinishResponse(
|
||||
val nextState: String? = null,
|
||||
val advanceTime: Long? = null,
|
||||
val electronOrderState: Int? = null,
|
||||
)
|
||||
|
||||
data class UpdateOrderConfirmTaskRequest(
|
||||
val type: String? = null,
|
||||
val taskId: Int? = null,
|
||||
val userId: Int? = null,
|
||||
val vehicleId: Int? = null,
|
||||
val currentState: String? = null,
|
||||
val operateTime: String? = null,
|
||||
val offlineMode: Int? = null,
|
||||
val supplierType: Int? = null,
|
||||
val settleType: Int? = null,
|
||||
val success: Int? = null,
|
||||
val lat: Double? = null,
|
||||
val lng: Double? = null,
|
||||
val carryMileage:Int?=null,
|
||||
val startMileage:Int?=null,
|
||||
//老系统
|
||||
val wheelFee: Int? = null,
|
||||
//子公司
|
||||
val startPrice: Int? = null,
|
||||
val unitPrice: Double? = null,
|
||||
val mileage: Int? = null,
|
||||
val basePrice: Double? = null,
|
||||
val startRoadFee: Int? = null,
|
||||
val carryRoadFee: Int? = null,
|
||||
val waitFee: Int? = null,
|
||||
val wheelNum: Int? = null,
|
||||
val wheelPrice: Int? = null,
|
||||
val dilemmaFee: Int? = null,
|
||||
val basementFee: Int? = null,
|
||||
val assistFee: Double? = null,
|
||||
val totalFee: Double? = null,
|
||||
val img1: String? = null,
|
||||
val img2: String? = null,
|
||||
val img3: String? = null,
|
||||
val img4: String? = null,
|
||||
val img1Info: String? = null,
|
||||
val img2Info: String? = null,
|
||||
val img3Info: String? = null,
|
||||
val img4Info: String? = null,
|
||||
|
||||
val imgList: List<String>? = null,
|
||||
val imgInfoList: List<String>? = null,
|
||||
val templatePhotoInfoList: List<String?>? = null
|
||||
)
|
@ -0,0 +1,11 @@
|
||||
package com.za.bean.request
|
||||
|
||||
data class UploadGpsRequest(
|
||||
var vehicleId: Int? = null,
|
||||
var userId: Int? = null,
|
||||
var lat: Double? = null,
|
||||
var lng: Double? = null,
|
||||
var speed: Double? = null,
|
||||
var direction: Double? = null,
|
||||
var working: Boolean? = false,
|
||||
)
|
@ -0,0 +1,19 @@
|
||||
package com.za.bean.request
|
||||
|
||||
//车辆列表请求
|
||||
data class VehicleListRequest(
|
||||
val supplierCode: String? = null,
|
||||
val content: String? = null
|
||||
)
|
||||
|
||||
data class UpdateVehicleStateRequest(
|
||||
val vehicleId: Int? = null,
|
||||
val status: Int? = null,
|
||||
val userId: Int? = null,
|
||||
)
|
||||
|
||||
data class GeneralInfoRequest(
|
||||
val vehicleId: Int? = null,
|
||||
val driverId: Int? = null,
|
||||
val deviceId: String? = null,
|
||||
)
|
159
servicing/src/main/java/com/za/call/CallLogManager.kt
Normal file
159
servicing/src/main/java/com/za/call/CallLogManager.kt
Normal file
@ -0,0 +1,159 @@
|
||||
package com.za.call
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.provider.CallLog
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.blankj.utilcode.util.TimeUtils
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.ext.toJson
|
||||
import com.za.net.BaseObserver
|
||||
import com.za.net.RetrofitHelper
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
|
||||
object CallLogManager {
|
||||
private const val TAG = "CallLogManager"
|
||||
private var lastUploadTime : Long? = null
|
||||
set(value) {
|
||||
MMKV.defaultMMKV().encode("lastUploadCallLogTime", value ?: 0)
|
||||
field = value
|
||||
}
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeLong("lastUploadCallLogTime", 0)
|
||||
}
|
||||
|
||||
//要上传的电话
|
||||
var phoneCallContactBean : ContactRecordBean? = null
|
||||
|
||||
fun uploadCallLog(context : Context) {
|
||||
if (ActivityCompat.checkSelfPermission(context,
|
||||
Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
LogUtil.print(TAG, "没有通话记录读取权限")
|
||||
return
|
||||
}
|
||||
|
||||
if (phoneCallContactBean == null || phoneCallContactBean?.phone.isNullOrEmpty()) {
|
||||
if (GlobalData.currentOrder == null) {
|
||||
return
|
||||
}
|
||||
phoneCallContactBean = ContactRecordBean(taskId = GlobalData.currentOrder?.taskId,
|
||||
taskCode = GlobalData.currentOrder?.taskCode,
|
||||
phone = GlobalData.currentOrder?.customerPhone)
|
||||
}
|
||||
|
||||
val cursor = context.contentResolver.query(CallLog.Calls.CONTENT_URI,
|
||||
arrayOf(CallLog.Calls.NUMBER,
|
||||
CallLog.Calls.DATE,
|
||||
CallLog.Calls.DURATION,
|
||||
CallLog.Calls.TYPE),
|
||||
"${CallLog.Calls.NUMBER} = ${phoneCallContactBean?.phone} ",
|
||||
null,
|
||||
"${CallLog.Calls.DATE} DESC")
|
||||
|
||||
LogUtil.print("call count", "${cursor?.columnCount}")
|
||||
|
||||
cursor?.use { c ->
|
||||
while (c.moveToNext()) {
|
||||
val date = c.getLong(c.getColumnIndexOrThrow(CallLog.Calls.DATE))
|
||||
val duration = c.getLong(c.getColumnIndexOrThrow(CallLog.Calls.DURATION))
|
||||
val type = c.getInt(c.getColumnIndexOrThrow(CallLog.Calls.TYPE))
|
||||
|
||||
val callLong = c.getLong(c.getColumnIndexOrThrow(CallLog.Calls.DATE))
|
||||
|
||||
if (System.currentTimeMillis() - callLong > 1000 * 60 * 50 * 24) {
|
||||
break
|
||||
}
|
||||
|
||||
if (lastUploadTime != null && callLong <= lastUploadTime !!) {
|
||||
continue
|
||||
}
|
||||
|
||||
uploadCallLogToServer(contactRecordBean = phoneCallContactBean,
|
||||
date,
|
||||
duration,
|
||||
type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun uploadCallLogToServer(contactRecordBean : ContactRecordBean?,
|
||||
date : Long,
|
||||
duration : Long,
|
||||
type : Int) {
|
||||
if (contactRecordBean == null) {
|
||||
LogUtil.print("uploadCallLogToServer", "没有需要上传的电话")
|
||||
return
|
||||
}
|
||||
|
||||
val state = if (duration == 0L) {
|
||||
if (type == CallLog.Calls.REJECTED_TYPE) { // 被拒接
|
||||
3
|
||||
} else {
|
||||
2
|
||||
}
|
||||
} else {
|
||||
1
|
||||
}
|
||||
|
||||
val contactRecordRequest = ContactRecordRequest(taskId = contactRecordBean.taskId,
|
||||
state = state,
|
||||
taskCode = contactRecordBean.taskCode,
|
||||
callTime = TimeUtils.millis2String(date),
|
||||
duration = formatDuration(duration),
|
||||
remarkPath = "")
|
||||
doUploadCallLog(contactRecordRequest)
|
||||
LogUtil.print(TAG, "uploadCallLogToServer params=${contactRecordRequest.toJson()}")
|
||||
}
|
||||
|
||||
private fun doUploadCallLog(contactRecordRequest : ContactRecordRequest) {
|
||||
RetrofitHelper.getDefaultService().uploadContactRecord(contactRecordRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<String>() {
|
||||
override fun doSuccess(it : String?) {
|
||||
lastUploadTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
LogUtil.print(TAG, "doFailure code=$code, msg=$msg")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 将秒数转换为友好的时分秒格式
|
||||
fun formatDuration(duration : Long) : String {
|
||||
val hours = duration / 3600
|
||||
val minutes = (duration % 3600) / 60
|
||||
val seconds = duration % 60
|
||||
return when {
|
||||
hours > 0 && minutes == 0L && seconds == 0L -> "${hours}小时" // 只有小时
|
||||
hours > 0 -> "${hours}小时${minutes}分钟${seconds}秒" // 包含小时、分钟、秒
|
||||
minutes > 0 && seconds == 0L -> "${minutes}分钟" // 只有分钟
|
||||
minutes > 0 -> "${minutes}分钟${seconds}秒" // 包含分钟、秒
|
||||
seconds > 0 -> "${seconds}秒" // 只有秒
|
||||
else -> "" // 其他情况
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//public int taskId;
|
||||
//public int state;
|
||||
//public String taskCode;
|
||||
//public String callTime;
|
||||
//public String duration;
|
||||
//
|
||||
//public String remarkPath;
|
||||
data class ContactRecordRequest(val taskId : Int? = null,
|
||||
val state : Int? = null,
|
||||
val taskCode : String? = null,
|
||||
val callTime : String? = null,
|
||||
val duration : String? = null,
|
||||
val remarkPath : String? = null)
|
||||
|
||||
data class ContactRecordBean(val taskId : Int? = null,
|
||||
val taskCode : String? = null,
|
||||
val phone : String? = null)
|
113
servicing/src/main/java/com/za/common/GlobalData.kt
Normal file
113
servicing/src/main/java/com/za/common/GlobalData.kt
Normal file
@ -0,0 +1,113 @@
|
||||
package com.za.common
|
||||
|
||||
import android.app.Application
|
||||
import com.amap.api.location.AMapLocation
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.za.bean.DriverInfo
|
||||
import com.za.bean.VehicleInfo
|
||||
import com.za.bean.db.order.OrderInfo
|
||||
import com.za.room.RoomHelper
|
||||
|
||||
object GlobalData {
|
||||
lateinit var application : Application
|
||||
var activityCount : Int = 0
|
||||
|
||||
var token : String? = null
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeString("TOKEN", null)
|
||||
}
|
||||
set(value) {
|
||||
MMKV.defaultMMKV().encode("TOKEN", value)
|
||||
field = value
|
||||
}
|
||||
|
||||
//记录上次登录的手机号
|
||||
var lastLoginPhone : String? = null
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeString("lastLoginPhone", null)
|
||||
}
|
||||
set(value) {
|
||||
MMKV.defaultMMKV().encode("lastLoginPhone", value)
|
||||
field = value
|
||||
}
|
||||
|
||||
var aesKey : String? = null
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeString("AES_KEY", null)
|
||||
}
|
||||
set(value) {
|
||||
MMKV.defaultMMKV().encode("AES_KEY", value)
|
||||
field = value
|
||||
}
|
||||
|
||||
var driverInfo : DriverInfo? = null
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeParcelable("driverInfo", DriverInfo::class.java)
|
||||
}
|
||||
set(value) {
|
||||
MMKV.defaultMMKV().encode("driverInfo", value)
|
||||
field = value
|
||||
}
|
||||
|
||||
var vehicleInfo : VehicleInfo? = null
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeParcelable("vehicleInfo", VehicleInfo::class.java)
|
||||
}
|
||||
set(value) {
|
||||
MMKV.defaultMMKV().encode("vehicleInfo", value)
|
||||
field = value
|
||||
}
|
||||
|
||||
|
||||
var currentOrder : OrderInfo? = null
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeParcelable("currentOrder", OrderInfo::class.java)
|
||||
}
|
||||
set(value) {
|
||||
MMKV.defaultMMKV().encode("currentOrder", value)
|
||||
if (RoomHelper.db?.orderDao()?.getCurrentOrder() == null && value != null) {
|
||||
RoomHelper.db?.orderDao()?.insertOrder(value)
|
||||
} else if (value != null) {
|
||||
RoomHelper.db?.orderDao()?.update(value)
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
var currentLocation : AMapLocation? = null
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeParcelable("currentLocation", AMapLocation::class.java)
|
||||
}
|
||||
set(value) {
|
||||
value?.time = System.currentTimeMillis()
|
||||
MMKV.defaultMMKV().encode("currentLocation", value)
|
||||
field = value
|
||||
}
|
||||
|
||||
var loginTime : Long? = null
|
||||
get() {
|
||||
return MMKV.defaultMMKV().decodeLong("loginTime", System.currentTimeMillis())
|
||||
}
|
||||
set(value) {
|
||||
MMKV.defaultMMKV().encode("loginTime", value ?: System.currentTimeMillis())
|
||||
field = value
|
||||
}
|
||||
|
||||
fun clearUserCache() {
|
||||
token = null
|
||||
aesKey = null
|
||||
driverInfo = null
|
||||
vehicleInfo = null
|
||||
currentLocation = null
|
||||
loginTime = null
|
||||
}
|
||||
|
||||
fun clearAllOrderCache() {
|
||||
currentOrder = null
|
||||
RoomHelper.clearAll()
|
||||
}
|
||||
|
||||
fun clearOrderCache(taskId : Int) {
|
||||
RoomHelper.clearOrderFromTaskCode(taskId = taskId)
|
||||
}
|
||||
|
||||
}
|
35
servicing/src/main/java/com/za/common/ZDManager.kt
Normal file
35
servicing/src/main/java/com/za/common/ZDManager.kt
Normal file
@ -0,0 +1,35 @@
|
||||
package com.za.common
|
||||
|
||||
import android.app.Application
|
||||
import com.tencent.bugly.Bugly
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.tencent.mmkv.MMKVLogLevel
|
||||
import com.za.base.AppConfig
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.room.RoomHelper
|
||||
import com.za.service.location.ZdLocationManager
|
||||
|
||||
object ZDManager {
|
||||
lateinit var application : Application
|
||||
fun init(application : Application) {
|
||||
this.application = application
|
||||
thirdSdkInit()
|
||||
}
|
||||
|
||||
private fun thirdSdkInit() {
|
||||
GlobalData.application = application
|
||||
MMKV.initialize(application, MMKVLogLevel.LevelInfo)
|
||||
Bugly.init(application, "6972a6b56d", true)
|
||||
AppConfig.crm1()
|
||||
LogUtil.init(application)
|
||||
RoomHelper.init(application)
|
||||
ZdLocationManager.init(application)
|
||||
|
||||
|
||||
// 初始化讯飞SDK
|
||||
//科大讯飞初始化
|
||||
// SpeechUtility.createUtility(application,
|
||||
// SpeechConstant.APPID + "=6fd4aabe," + SpeechConstant.FORCE_LOGIN + "=true")
|
||||
// SpeechManager.init(application)
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.za.common.log
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLDecoder
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object LogRetrofitHelper {
|
||||
private var retrofit: Retrofit? = null
|
||||
private var apiService: LogService? = null
|
||||
private val loggerInterceptor = HttpLoggingInterceptor {
|
||||
try {
|
||||
if (it.contains("image/*")) {
|
||||
return@HttpLoggingInterceptor
|
||||
}
|
||||
if (it.contains("name=\"file\"; filename")) {
|
||||
return@HttpLoggingInterceptor
|
||||
}
|
||||
Log.e(
|
||||
"--network--",
|
||||
URLDecoder.decode(it.replace(Regex("%(?![0-9a-fA-F]{2})"), ""), "utf-8")
|
||||
)
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}.setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||
|
||||
|
||||
fun getDefaultService(): LogService {
|
||||
return if (apiService == null) {
|
||||
apiService = getDefaultRetrofit().create(LogService::class.java)
|
||||
apiService!!
|
||||
} else {
|
||||
apiService!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultRetrofit(): Retrofit {
|
||||
return if (retrofit == null) {
|
||||
retrofit = Retrofit.Builder().baseUrl("https://api2.sino-assist.com")
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
|
||||
.client(getOkHttpClient())
|
||||
.build()
|
||||
retrofit!!
|
||||
} else {
|
||||
retrofit!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOkHttpClient(): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.addInterceptor(loggerInterceptor)
|
||||
.build()
|
||||
}
|
||||
}
|
27
servicing/src/main/java/com/za/common/log/LogService.kt
Normal file
27
servicing/src/main/java/com/za/common/log/LogService.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package com.za.common.log
|
||||
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import okhttp3.MultipartBody
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface LogService {
|
||||
//日志上传
|
||||
@Multipart
|
||||
@POST("/oss/minio/upload")
|
||||
fun uploadLog(
|
||||
@Part file: MultipartBody.Part?,
|
||||
@Query("fileName") fileName: String?,
|
||||
@Query("bucketName") bucketName: String?
|
||||
): Observable<LogBaseResponse<String>>
|
||||
}
|
||||
|
||||
data class LogBaseResponse<T : Any>(
|
||||
val data: T?,
|
||||
val msg: String?,
|
||||
val success: Boolean?,
|
||||
val code: Int?,
|
||||
val total: Int?,
|
||||
)
|
317
servicing/src/main/java/com/za/common/log/LogUtil.kt
Normal file
317
servicing/src/main/java/com/za/common/log/LogUtil.kt
Normal file
@ -0,0 +1,317 @@
|
||||
package com.za.common.log
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.PeriodicWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.blankj.utilcode.constant.MemoryConstants
|
||||
import com.blankj.utilcode.util.AppUtils
|
||||
import com.blankj.utilcode.util.DeviceUtils
|
||||
import com.blankj.utilcode.util.FileUtils
|
||||
import com.blankj.utilcode.util.TimeUtils
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.util.AppFileManager
|
||||
import com.za.servicing.BuildConfig
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
object LogUtil {
|
||||
private var context: Application? = null
|
||||
private var logDestinationPath: String? = null
|
||||
private var orderLogDirPath: String? = null
|
||||
private var normalLogDirPath: String? = null
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
private val logBuffer = StringBuilder()
|
||||
private val isWriting = AtomicBoolean(false)
|
||||
|
||||
fun init(context: Application) {
|
||||
this.context = context
|
||||
|
||||
logDestinationPath = AppFileManager.getLogPath(context).also { path ->
|
||||
createDirectoryIfNotExists(path)
|
||||
orderLogDirPath = "$path${File.separator}order_log".also { createDirectoryIfNotExists(it) }
|
||||
normalLogDirPath = "$path${File.separator}normal_log".also { createDirectoryIfNotExists(it) }
|
||||
}
|
||||
|
||||
initializeWorkManager(context)
|
||||
}
|
||||
|
||||
private fun createDirectoryIfNotExists(path: String) {
|
||||
File(path).apply { if (!exists()) mkdir() }
|
||||
}
|
||||
|
||||
private fun initializeWorkManager(context: Application) {
|
||||
if (!WorkManager.isInitialized()) {
|
||||
WorkManager.initialize(context, Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.INFO)
|
||||
.build())
|
||||
}
|
||||
|
||||
WorkManager.getInstance(context).apply {
|
||||
cancelAllWorkByTag("logWorkRequest")
|
||||
enqueue(PeriodicWorkRequest.Builder(LogTask::class.java, 20, TimeUnit.MINUTES)
|
||||
.addTag("logWorkRequest")
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
||||
fun print(tag: String, content: String) {
|
||||
val time = getCurrentTime()
|
||||
val logEntry = "$time---$tag---$content\n"
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
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 {
|
||||
val fileName = "normal_log.txt"
|
||||
val logFile = File("$normalLogDirPath${File.separator}$fileName")
|
||||
|
||||
logFile.parentFile?.mkdirs()
|
||||
|
||||
if (!logFile.exists()) {
|
||||
logFile.createNewFile()
|
||||
addLogHead(logFile, getCurrentTime())
|
||||
}
|
||||
|
||||
BufferedWriter(FileWriter(logFile, true)).use { writer ->
|
||||
writer.write(logContent)
|
||||
writer.flush()
|
||||
}
|
||||
|
||||
if (logFile.length() >= 8 * MemoryConstants.MB) {
|
||||
rotateLogFile(logFile)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e("LogUtil", "Error in flushBuffer: ${e.message}")
|
||||
} catch (e: Exception) {
|
||||
Log.e("LogUtil", "Error in flushBuffer: ${e.message}")
|
||||
} finally {
|
||||
isWriting.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rotateLogFile(file: File) {
|
||||
if (!file.exists()) return
|
||||
|
||||
val newFileName = buildString {
|
||||
append(AppUtils.getAppVersionCode())
|
||||
append("_")
|
||||
append(GlobalData.vehicleInfo?.vehicleName ?: "unknown")
|
||||
append("_")
|
||||
append(GlobalData.driverInfo?.userName ?: "unknown")
|
||||
append("_")
|
||||
append(TimeUtils.getNowString())
|
||||
append(".txt")
|
||||
}
|
||||
|
||||
val newFile = File("$normalLogDirPath${File.separator}$newFileName")
|
||||
|
||||
try {
|
||||
if (file.renameTo(newFile)) {
|
||||
compressAndUploadLog(newFile)
|
||||
// 创建新的日志文件
|
||||
file.createNewFile()
|
||||
addLogHead(file, getCurrentTime())
|
||||
} else {
|
||||
print("LogUtil", "Failed to rename log file")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
print("LogUtil", "Error during log rotation: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun compressAndUploadLog(logFile: File) = coroutineScope.launch {
|
||||
try {
|
||||
val compressedFile = File("${logFile.absolutePath}.7z")
|
||||
compress(logFile, compressedFile.absolutePath)
|
||||
upload(logFile, compressedFile)
|
||||
} catch (e: Exception) {
|
||||
print("LogUtil", e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteLog(file: File) {
|
||||
try {
|
||||
FileUtils.delete(file.absolutePath)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateNormalLog() {
|
||||
thread {
|
||||
if (GlobalData.token.isNullOrBlank()) {
|
||||
return@thread
|
||||
}
|
||||
val fileName = "normal_log.txt"
|
||||
val file = File("$normalLogDirPath${File.separator}$fileName")
|
||||
val reName = "${AppUtils.getAppVersionCode()}_${GlobalData.vehicleInfo?.vehicleName}_${GlobalData.driverInfo?.userName}_${TimeUtils.getNowString()}.txt"
|
||||
val reNamePath = "$normalLogDirPath${File.separator}$reName"
|
||||
file.renameTo(File(reNamePath))
|
||||
normalLogDirPath?.let { it ->
|
||||
File(it).listFiles()?.forEach {
|
||||
if (it.length() / MemoryConstants.MB >= 10) {
|
||||
deleteLog(it)
|
||||
return@thread
|
||||
}
|
||||
if (it.exists() && !it.name.contains("normal_log")) {
|
||||
if (it.name.contains("7z")) {
|
||||
upload(null, desFile = it)
|
||||
} else {
|
||||
val zipNamePath = it.absolutePath + ".7z"
|
||||
val zipFile = File(zipNamePath)
|
||||
if (!zipFile.exists()) {
|
||||
try {
|
||||
zipFile.createNewFile()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
compress(it, zipNamePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun compress(srcFile: File, desFilePath: String) {
|
||||
try {
|
||||
val out = XZCompressorOutputStream(FileOutputStream(desFilePath))
|
||||
addToArchiveCompression(out, srcFile, File(desFilePath))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addToArchiveCompression(
|
||||
sevenZOutputFile: XZCompressorOutputStream,
|
||||
srcFile: File, desFile: File
|
||||
) {
|
||||
if (srcFile.isFile) {
|
||||
var inputStream: FileInputStream? = null
|
||||
try {
|
||||
inputStream = FileInputStream(srcFile)
|
||||
val b = ByteArray(2048)
|
||||
var count: Int
|
||||
while (inputStream.read(b).also { count = it } != -1) {
|
||||
sevenZOutputFile.write(b, 0, count)
|
||||
}
|
||||
sevenZOutputFile.close()
|
||||
inputStream.close()
|
||||
upload(srcFile, desFile)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
try {
|
||||
sevenZOutputFile.close()
|
||||
inputStream!!.close()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun upload(srcFile: File?, desFile: File) {
|
||||
val requestBody: RequestBody = desFile.asRequestBody("multipart/form-data".toMediaType())
|
||||
val part = MultipartBody.Part.createFormData("file", desFile.name, requestBody)
|
||||
|
||||
val disposable = LogRetrofitHelper.getDefaultService()
|
||||
.uploadLog(part, desFile.name, "rescue-app")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe({ it ->
|
||||
if (it.code == 200) {
|
||||
deleteLog(desFile)
|
||||
}
|
||||
srcFile?.let {
|
||||
deleteLog(it)
|
||||
}
|
||||
}, {
|
||||
}, {})
|
||||
}
|
||||
|
||||
private fun addLogHead(file: File, time: String) {
|
||||
file.appendBytes("${time}---应用版本---${AppUtils.getAppVersionName()}".toByteArray())
|
||||
file.appendBytes("\n".toByteArray())
|
||||
file.appendBytes("$time---系统版本---Android${DeviceUtils.getSDKVersionName()} ${DeviceUtils.getSDKVersionCode()}".toByteArray())
|
||||
file.appendBytes("\n".toByteArray())
|
||||
file.appendBytes("$time---ROM---${DeviceUtils.getManufacturer()} ${DeviceUtils.getModel()}".toByteArray())
|
||||
file.appendBytes("\n".toByteArray())
|
||||
file.appendBytes("$time---build---${AppUtils.getAppVersionCode()}".toByteArray())
|
||||
file.appendBytes("\n".toByteArray())
|
||||
file.appendBytes("$time---APP名称---中道救援-司机端".toByteArray())
|
||||
file.appendBytes("\n".toByteArray())
|
||||
file.appendBytes("$time---车辆名称---${GlobalData.vehicleInfo?.vehicleName}".toByteArray())
|
||||
file.appendBytes("\n".toByteArray())
|
||||
file.appendBytes("$time---司机名称---${GlobalData.driverInfo?.userName ?: GlobalData.vehicleInfo?.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) : Worker(appContext, workerParams) {
|
||||
override fun doWork(): Result {
|
||||
updateNormalLog()
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
}
|
82
servicing/src/main/java/com/za/common/util/AppFileManager.kt
Normal file
82
servicing/src/main/java/com/za/common/util/AppFileManager.kt
Normal file
@ -0,0 +1,82 @@
|
||||
package com.za.common.util
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import com.za.base.Const
|
||||
import java.io.File
|
||||
|
||||
object AppFileManager {
|
||||
|
||||
//获取人脸大头照存储地址
|
||||
fun getFaceDir(context: Context): File {
|
||||
val path = context.cacheDir?.path + File.separator + "faceVerify"
|
||||
val file = File(path)
|
||||
if (!file.exists()) {
|
||||
file.mkdir()
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
/**
|
||||
* 师傅签名位置保存
|
||||
*/
|
||||
fun getDriverSignDirPath(context : Context) : String {
|
||||
val tempPath = context.cacheDir.toString() + File.separator + "driver_sign"
|
||||
val tempFile = File(tempPath)
|
||||
if (! tempFile.exists()) {
|
||||
tempFile.mkdir()
|
||||
}
|
||||
return tempFile.absolutePath
|
||||
}
|
||||
|
||||
/**
|
||||
* 师傅签名位置保存
|
||||
*/
|
||||
fun getDriverSignPath(context : Context) : String {
|
||||
return getDriverSignDirPath(context) + File.separator + Const.driverSighName
|
||||
}
|
||||
|
||||
//人脸大头照存储地址
|
||||
fun getFaceFilePath(context: Context): String {
|
||||
return getFaceDir(context).absolutePath + File.separator + "avatar.jpg"
|
||||
}
|
||||
|
||||
/**
|
||||
* app日志路径
|
||||
*/
|
||||
fun getLogPath(context: Context?): String {
|
||||
val path = "${context?.filesDir?.absolutePath}${File.separator}Log"
|
||||
if (!File(path).exists()) {
|
||||
File(path).mkdir()
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
|
||||
fun orderWaterMarkerPath(context: Context): String {
|
||||
val path = context.cacheDir?.path + File.separator + "water_marker"
|
||||
if (!File(path).exists()) {
|
||||
File(path).mkdir()
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
/**
|
||||
* 拍摄的照片的保存位置
|
||||
*/
|
||||
fun getTakePictureParentPath(): String {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
return Environment.DIRECTORY_PICTURES
|
||||
} else {
|
||||
val dstPath =
|
||||
Environment.getExternalStorageDirectory().absolutePath + File.separator + Environment.DIRECTORY_PICTURES + File.separator + "中道救援"
|
||||
val file = File(dstPath)
|
||||
if (!file.exists()) {
|
||||
file.mkdir()
|
||||
}
|
||||
return dstPath
|
||||
}
|
||||
}
|
||||
|
||||
}
|
20
servicing/src/main/java/com/za/common/util/ClickProxy.java
Normal file
20
servicing/src/main/java/com/za/common/util/ClickProxy.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.za.common.util;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public class ClickProxy implements View.OnClickListener {
|
||||
private long lastClick = 0;
|
||||
private final View.OnClickListener onClickListener;
|
||||
|
||||
public ClickProxy(View.OnClickListener onClickListener) {
|
||||
this.onClickListener = onClickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (System.currentTimeMillis() - lastClick >= 1000) {
|
||||
onClickListener.onClick(v);
|
||||
lastClick = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
76
servicing/src/main/java/com/za/common/util/DeviceUtil.java
Normal file
76
servicing/src/main/java/com/za/common/util/DeviceUtil.java
Normal file
@ -0,0 +1,76 @@
|
||||
package com.za.common.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.media.MediaDrm;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.blankj.utilcode.util.SPUtils;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 设备唯一标识
|
||||
* 顺序为:Android_ID->DRM_ID->UUID
|
||||
*/
|
||||
public class DeviceUtil {
|
||||
public static String getAndroidId(Context context) {
|
||||
String deviceId = SPUtils.getInstance().getString("deviceId");
|
||||
if (deviceId == null || deviceId.isEmpty()) {
|
||||
@SuppressLint("HardwareIds") String temp = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
if (temp == null || temp.isEmpty() || temp.replace("0", "").isEmpty()) {
|
||||
temp = getDrmId();
|
||||
}
|
||||
SPUtils.getInstance().put("deviceId", temp);
|
||||
return temp;
|
||||
}
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
private static String getDrmId() {
|
||||
String sRet = "";
|
||||
UUID WIDEVINE_UUID = new UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L);
|
||||
MediaDrm mediaDrm = null;
|
||||
try {
|
||||
mediaDrm = new MediaDrm(WIDEVINE_UUID);
|
||||
byte[] widevineId = mediaDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID);
|
||||
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(widevineId);
|
||||
|
||||
sRet = bytesToHex(md.digest()); //we convert byte[] to hex for our purposes
|
||||
} catch (Exception e) {
|
||||
//WIDEVINE is not available
|
||||
} finally {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (null != mediaDrm) {
|
||||
mediaDrm.close();
|
||||
}
|
||||
} else {
|
||||
if (null != mediaDrm) {
|
||||
mediaDrm.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sRet.isEmpty()) {
|
||||
sRet = WIDEVINE_UUID.toString().replace("-", "");
|
||||
}
|
||||
return sRet;
|
||||
}
|
||||
|
||||
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
char[] hexChars = new char[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
}
|
||||
|
489
servicing/src/main/java/com/za/common/util/ImageUtil.kt
Normal file
489
servicing/src/main/java/com/za/common/util/ImageUtil.kt
Normal file
@ -0,0 +1,489 @@
|
||||
package com.za.common.util
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.createBitmap
|
||||
import com.amap.api.maps.model.BitmapDescriptor
|
||||
import com.amap.api.maps.model.BitmapDescriptorFactory
|
||||
import com.za.common.log.LogUtil
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 图片工具类
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
object ImageUtil {
|
||||
/**
|
||||
* 设置水印图片在左上角
|
||||
*
|
||||
* @param context
|
||||
* @param src
|
||||
* @param watermark
|
||||
* @param paddingLeft
|
||||
* @param paddingTop
|
||||
* @return
|
||||
*/
|
||||
fun createWaterMaskLeftTop(context : Context,
|
||||
src : Bitmap?,
|
||||
watermark : Bitmap,
|
||||
paddingLeft : Int,
|
||||
paddingTop : Int) : Bitmap? {
|
||||
return createWaterMaskBitmap(src,
|
||||
watermark,
|
||||
paddingLeft,
|
||||
dp2px(context, paddingTop.toFloat()))
|
||||
}
|
||||
|
||||
private fun createWaterMaskBitmap(src : Bitmap?,
|
||||
watermark : Bitmap,
|
||||
paddingLeft : Int,
|
||||
paddingTop : Int) : Bitmap? {
|
||||
if (src == null) {
|
||||
return null
|
||||
}
|
||||
val width = src.width
|
||||
val height = src.height //创建一个bitmap
|
||||
val newb = Bitmap.createBitmap(width,
|
||||
height,
|
||||
Bitmap.Config.ARGB_8888) // 创建一个新的和SRC长度宽度一样的位图 //将该图片作为画布
|
||||
val canvas = Canvas(newb) //在画布 0,0坐标上开始绘制原始图片
|
||||
canvas.drawBitmap(src, 0f, 0f, null) //在画布上绘制水印图片
|
||||
canvas.drawBitmap(watermark, paddingLeft.toFloat(), paddingTop.toFloat(), null) // 保存
|
||||
canvas.save() // 存储
|
||||
canvas.restore()
|
||||
return newb
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置水印图片在右下角
|
||||
*
|
||||
* @param context
|
||||
* @param src
|
||||
* @param watermark
|
||||
* @param paddingRight
|
||||
* @param paddingBottom
|
||||
* @return
|
||||
*/
|
||||
fun createWaterMaskRightBottom(context : Context?,
|
||||
src : Bitmap,
|
||||
watermark : Bitmap,
|
||||
paddingRight : Int,
|
||||
paddingBottom : Int) : Bitmap? {
|
||||
return createWaterMaskBitmap(src,
|
||||
watermark,
|
||||
src.width - watermark.width - paddingRight,
|
||||
src.height - watermark.height - paddingBottom)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置水印图片到右上角
|
||||
*
|
||||
* @param context
|
||||
* @param src
|
||||
* @param watermark
|
||||
* @param paddingRight
|
||||
* @param paddingTop
|
||||
* @return
|
||||
*/
|
||||
fun createWaterMaskRightTop(context : Context?,
|
||||
src : Bitmap,
|
||||
watermark : Bitmap,
|
||||
paddingRight : Int,
|
||||
paddingTop : Int) : Bitmap? {
|
||||
return createWaterMaskBitmap(src,
|
||||
watermark,
|
||||
src.width - watermark.width - paddingRight,
|
||||
paddingTop)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置水印图片到左下角
|
||||
*
|
||||
* @param context
|
||||
* @param src
|
||||
* @param watermark
|
||||
* @param paddingLeft
|
||||
* @param paddingBottom
|
||||
* @return
|
||||
*/
|
||||
fun createWaterMaskLeftBottom(context : Context?,
|
||||
src : Bitmap,
|
||||
watermark : Bitmap,
|
||||
paddingLeft : Int,
|
||||
paddingBottom : Int) : Bitmap? {
|
||||
return createWaterMaskBitmap(src,
|
||||
watermark,
|
||||
paddingLeft,
|
||||
src.height - watermark.height - paddingBottom)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置水印图片到中间
|
||||
*
|
||||
* @param src
|
||||
* @param watermark
|
||||
* @return
|
||||
*/
|
||||
fun createWaterMaskCenter(src : Bitmap, watermark : Bitmap) : Bitmap? {
|
||||
return createWaterMaskBitmap(src,
|
||||
watermark,
|
||||
(src.width - watermark.width) / 2,
|
||||
(src.height - watermark.height) / 2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 给图片添加文字到左上角
|
||||
*
|
||||
* @param context
|
||||
* @param bitmap
|
||||
* @param text
|
||||
* @return
|
||||
*/
|
||||
fun drawTextToLeftTop(context : Context,
|
||||
bitmap : Bitmap,
|
||||
text : String,
|
||||
size : Int,
|
||||
color : Int,
|
||||
paddingLeft : Int,
|
||||
paddingTop : Int) : Bitmap {
|
||||
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
paint.color = color
|
||||
paint.textSize = size.toFloat()
|
||||
paint.setTypeface(Typeface.createFromAsset(context.assets, "fonts/song.ttf"))
|
||||
paint.isFakeBoldText = true
|
||||
val bounds = Rect()
|
||||
paint.getTextBounds(text, 0, text.length, bounds)
|
||||
return drawTextToBitmap(context,
|
||||
bitmap,
|
||||
text,
|
||||
paint,
|
||||
bounds,
|
||||
paddingLeft,
|
||||
paddingTop + bounds.height())
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制文字到右下角
|
||||
*
|
||||
* @param context
|
||||
* @param bitmap
|
||||
* @param text
|
||||
* @param size
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
fun drawTextToRightBottom(context : Context,
|
||||
bitmap : Bitmap,
|
||||
text : String,
|
||||
size : Int,
|
||||
color : Int,
|
||||
paddingRight : Int,
|
||||
paddingBottom : Int) : Bitmap {
|
||||
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
paint.color = color
|
||||
paint.textSize = size.toFloat()
|
||||
paint.setTypeface(Typeface.createFromAsset(context.assets, "fonts/song.ttf"))
|
||||
paint.isFakeBoldText = true
|
||||
val bounds = Rect()
|
||||
paint.getTextBounds(text, 0, text.length, bounds)
|
||||
return drawTextToBitmap(context,
|
||||
bitmap,
|
||||
text,
|
||||
paint,
|
||||
bounds,
|
||||
bitmap.width - bounds.width() - paddingRight,
|
||||
bitmap.height - paddingBottom)
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制文字到右上方
|
||||
*
|
||||
* @param context
|
||||
* @param bitmap
|
||||
* @param text
|
||||
* @param size
|
||||
* @param color
|
||||
* @param paddingRight
|
||||
* @param paddingTop
|
||||
* @return
|
||||
*/
|
||||
fun drawTextToRightTop(context : Context,
|
||||
bitmap : Bitmap,
|
||||
text : String,
|
||||
size : Int,
|
||||
color : Int,
|
||||
paddingRight : Int,
|
||||
paddingTop : Int) : Bitmap {
|
||||
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
paint.color = color
|
||||
paint.textSize = size.toFloat()
|
||||
paint.setTypeface(Typeface.createFromAsset(context.assets, "fonts/song.ttf"))
|
||||
paint.isFakeBoldText = true
|
||||
val bounds = Rect()
|
||||
paint.getTextBounds(text, 0, text.length, bounds)
|
||||
return drawTextToBitmap(context,
|
||||
bitmap,
|
||||
text,
|
||||
paint,
|
||||
bounds,
|
||||
bitmap.width - bounds.width() - paddingRight,
|
||||
paddingTop + bounds.height())
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制文字到左下方
|
||||
*
|
||||
* @param context
|
||||
* @param bitmap
|
||||
* @param text
|
||||
* @param size
|
||||
* @param color
|
||||
* @param paddingLeft
|
||||
* @param paddingBottom
|
||||
* @return
|
||||
*/
|
||||
fun drawTextToLeftBottom(context : Context,
|
||||
bitmap : Bitmap,
|
||||
text : String,
|
||||
size : Int,
|
||||
color : Int,
|
||||
paddingLeft : Int,
|
||||
paddingBottom : Int) : Bitmap {
|
||||
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
paint.color = color
|
||||
paint.textSize = size.toFloat()
|
||||
paint.setTypeface(Typeface.createFromAsset(context.assets, "fonts/song.ttf"))
|
||||
paint.isFakeBoldText = true
|
||||
val bounds = Rect()
|
||||
paint.getTextBounds(text, 0, text.length, bounds)
|
||||
return drawTextToBitmap(context,
|
||||
bitmap,
|
||||
text,
|
||||
paint,
|
||||
bounds,
|
||||
paddingLeft,
|
||||
bitmap.height - paddingBottom)
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制文字到中间
|
||||
*
|
||||
* @param context
|
||||
* @param bitmap
|
||||
* @param text
|
||||
* @param size
|
||||
* @param color
|
||||
* @return
|
||||
*/
|
||||
fun drawTextToCenter(context : Context,
|
||||
bitmap : Bitmap,
|
||||
text : String,
|
||||
size : Int,
|
||||
color : Int) : Bitmap {
|
||||
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
paint.color = color
|
||||
paint.textSize = size.toFloat()
|
||||
paint.setTypeface(Typeface.createFromAsset(context.assets, "fonts/song.ttf"))
|
||||
paint.isFakeBoldText = true
|
||||
val bounds = Rect()
|
||||
paint.getTextBounds(text, 0, text.length, bounds)
|
||||
return drawTextToBitmap(context,
|
||||
bitmap,
|
||||
text,
|
||||
paint,
|
||||
bounds,
|
||||
(bitmap.width - bounds.width()) / 2,
|
||||
(bitmap.height + bounds.height()) / 2)
|
||||
}
|
||||
|
||||
//图片上绘制文字
|
||||
private fun drawTextToBitmap(context : Context,
|
||||
myBitmap : Bitmap,
|
||||
text : String,
|
||||
paint : Paint,
|
||||
bounds : Rect,
|
||||
paddingLeft : Int,
|
||||
paddingTop : Int) : Bitmap {
|
||||
|
||||
val bitmapConfig = myBitmap.config
|
||||
paint.isDither = true // 获取跟清晰的图像采样
|
||||
paint.isFilterBitmap = true // 过滤一些
|
||||
val bitmap = myBitmap.copy(bitmapConfig !!, true)
|
||||
val canvas = Canvas(bitmap)
|
||||
canvas.drawText(text, paddingLeft.toFloat(), paddingTop.toFloat(), paint)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放图片
|
||||
*
|
||||
* @param src
|
||||
* @param w
|
||||
* @param h
|
||||
* @return
|
||||
*/
|
||||
fun scaleWithWH(src : Bitmap?, w : Double, h : Double) : Bitmap? {
|
||||
if (w == 0.0 || h == 0.0 || src == null) {
|
||||
return src
|
||||
} else { // 记录src的宽高
|
||||
val width = src.width
|
||||
val height = src.height // 创建一个matrix容器
|
||||
val matrix = Matrix() // 计算缩放比例
|
||||
val scaleWidth = (w / width).toFloat()
|
||||
val scaleHeight = (h / height).toFloat() // 开始缩放
|
||||
matrix.postScale(scaleWidth, scaleHeight) // 创建缩放后的图片
|
||||
return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dip转pix
|
||||
*
|
||||
* @param context
|
||||
* @param dp
|
||||
* @return
|
||||
*/
|
||||
private fun dp2px(context : Context, dp : Float) : Int {
|
||||
val scale = context.resources.displayMetrics.density
|
||||
return (dp * scale + 0.5f).toInt()
|
||||
}
|
||||
|
||||
|
||||
// view转bitmap
|
||||
fun createBitmapFromView(view : View) : Bitmap? { //是ImageView直接获取
|
||||
if (view is ImageView) {
|
||||
val drawable = view.drawable
|
||||
if (drawable is BitmapDrawable) {
|
||||
return drawable.bitmap
|
||||
}
|
||||
}
|
||||
view.clearFocus()
|
||||
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
view.draw(canvas)
|
||||
canvas.setBitmap(null)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
// 保存图片到相册
|
||||
fun saveImage(context : Context, bitmap : Bitmap?) {
|
||||
if (bitmap == null) {
|
||||
return
|
||||
}
|
||||
val isSaveSuccess = if (Build.VERSION.SDK_INT < 29) {
|
||||
saveImageToGallery(context, bitmap)
|
||||
} else {
|
||||
saveImageToGallery1(context, bitmap)
|
||||
}
|
||||
if (isSaveSuccess) {
|
||||
LogUtil.print("照片保存到相册成功", "success")
|
||||
} else {
|
||||
LogUtil.print("照片保存到相册失败", "failed")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* android 10 以下版本
|
||||
*/
|
||||
private fun saveImageToGallery(context : Context, image : Bitmap) : Boolean { // 首先保存图片
|
||||
val storePath =
|
||||
Environment.getExternalStorageDirectory().absolutePath + File.separator + "中道救援"
|
||||
|
||||
val appDir = File(storePath)
|
||||
if (! appDir.exists()) {
|
||||
appDir.mkdir()
|
||||
}
|
||||
val fileName = System.currentTimeMillis().toString() + ".jpg"
|
||||
val file = File(appDir, fileName)
|
||||
try {
|
||||
val fos = FileOutputStream(file) // 通过io流的方式来压缩保存图片
|
||||
val isSuccess = image.compress(Bitmap.CompressFormat.JPEG, 60, fos)
|
||||
fos.flush()
|
||||
fos.close()
|
||||
|
||||
// 保存图片后发送广播通知更新数据库
|
||||
val uri = Uri.fromFile(file)
|
||||
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))
|
||||
return isSuccess
|
||||
} catch (e : IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* android 10 以上版本
|
||||
*/
|
||||
private fun saveImageToGallery1(context : Context, image : Bitmap) : Boolean {
|
||||
val mImageTime = System.currentTimeMillis()
|
||||
val imageDate =
|
||||
SimpleDateFormat("yyyyMMdd-HHmmss", Locale.getDefault()).format(Date(mImageTime))
|
||||
val SCREENSHOT_FILE_NAME_TEMPLATE = "zd_%s.png" //图片名称,以"zd"+时间戳命名
|
||||
val mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate)
|
||||
|
||||
val values = ContentValues()
|
||||
values.put(MediaStore.MediaColumns.RELATIVE_PATH,
|
||||
Environment.DIRECTORY_PICTURES + File.separator + "中道救援") //Environment.DIRECTORY_SCREENSHOTS:截图,图库中显示的文件夹名
|
||||
values.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName)
|
||||
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
|
||||
values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000)
|
||||
values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000)
|
||||
values.put(MediaStore.MediaColumns.DATE_EXPIRES,
|
||||
(mImageTime + DateUtils.DAY_IN_MILLIS) / 1000)
|
||||
values.put(MediaStore.MediaColumns.IS_PENDING, 1)
|
||||
|
||||
val resolver = context.contentResolver
|
||||
val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
||||
try { // First, write the actual data for our screenshot
|
||||
resolver.openOutputStream(uri !!).use { out ->
|
||||
if (! image.compress(Bitmap.CompressFormat.PNG, 100, out !!)) {
|
||||
return false
|
||||
}
|
||||
} // Everything went well above, publish it!
|
||||
values.clear()
|
||||
values.put(MediaStore.MediaColumns.IS_PENDING, 0)
|
||||
values.putNull(MediaStore.MediaColumns.DATE_EXPIRES)
|
||||
resolver.update(uri, values, null, null)
|
||||
} catch (e : IOException) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
resolver.delete(uri !!, null)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
fun vectorToBitmap(context : Context, vectorResId : Int) : BitmapDescriptor {
|
||||
val vectorDrawable = ContextCompat.getDrawable(context, vectorResId) as VectorDrawable
|
||||
val bitmap = createBitmap(vectorDrawable.intrinsicWidth, vectorDrawable.intrinsicHeight)
|
||||
val canvas = Canvas(bitmap)
|
||||
vectorDrawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||
vectorDrawable.draw(canvas)
|
||||
return BitmapDescriptorFactory.fromBitmap(bitmap)
|
||||
}
|
||||
}
|
||||
|
101
servicing/src/main/java/com/za/common/util/MapUtil.kt
Normal file
101
servicing/src/main/java/com/za/common/util/MapUtil.kt
Normal file
@ -0,0 +1,101 @@
|
||||
package com.za.common.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import com.amap.api.maps.model.LatLng
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.ln
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
object MapUtil {
|
||||
|
||||
const val PN_GAODE_MAP: String = "com.autonavi.minimap" // 高德地图包名
|
||||
const val PN_BAIDU_MAP: String = "com.baidu.BaiduMap" // 百度地图包名
|
||||
const val PN_TENCENT_MAP: String = "com.tencent.map" // 百度地图包名
|
||||
|
||||
/**
|
||||
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
|
||||
* 即谷歌、高德 转 百度
|
||||
*
|
||||
* @param latLng
|
||||
* @returns
|
||||
*/
|
||||
fun gcj02ToBD09(latLng: LatLng): LatLng {
|
||||
val xPI = 3.141592653589793 * 3000.0 / 180.0
|
||||
val z = sqrt(latLng.longitude * latLng.longitude + latLng.latitude * latLng.latitude) + 0.00002 * sin(latLng.latitude * xPI)
|
||||
val theta = atan2(latLng.latitude, latLng.longitude) + 0.000003 * cos(latLng.longitude * xPI)
|
||||
val bdLat = z * sin(theta) + 0.006
|
||||
val bdLng = z * cos(theta) + 0.0065
|
||||
return LatLng(bdLat, bdLng)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查应用是否安装
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fun isGdMapInstalled(context: Context): Boolean {
|
||||
return isInstallPackage(context, PN_GAODE_MAP)
|
||||
}
|
||||
|
||||
fun isBaiduMapInstalled(context: Context): Boolean {
|
||||
return isInstallPackage(context, PN_BAIDU_MAP)
|
||||
}
|
||||
|
||||
fun isTencentInstalled(context: Context): Boolean {
|
||||
return isInstallPackage(context, PN_TENCENT_MAP)
|
||||
}
|
||||
|
||||
private fun isInstallPackage(context: Context, packageName: String): Boolean {
|
||||
try {
|
||||
val info = context.packageManager
|
||||
.getApplicationInfo(packageName,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES)
|
||||
return true
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//开启高德导航
|
||||
fun startNavigationGd(context: Context, lat: Double?, lng: Double?, address: String?) {
|
||||
val stringBuffer = "androidamap://route?sourceApplication=" + "amap" +
|
||||
"&dlat=" + lat +
|
||||
"&dlon=" + lng +
|
||||
"&dname=" + address +
|
||||
"&dev=" + 0 +
|
||||
"&t=" + 0
|
||||
val intent = Intent("android.intent.action.VIEW", Uri.parse(stringBuffer))
|
||||
intent.setPackage("com.autonavi.minimap")
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
//开启高德导航
|
||||
fun startNavigationBd(context: Context, lat: Double?, lng: Double?, address: String?) {
|
||||
val i1 = Intent("android.intent.action.VIEW")
|
||||
i1.setData(Uri.parse("baidumap://map/navi?query=${address}&mode=driving&location=${lat},${lng}&coord_type=gcj02&src=com.za.servicing"))
|
||||
context.startActivity(i1)
|
||||
}
|
||||
|
||||
//开启高德导航
|
||||
fun startNavigationTencent(context: Context, lat: Double?, lng: Double?, address: String?) {
|
||||
val stringBuffer = "androidamap://route?sourceApplication=" + "amap" +
|
||||
"&dlat=" + lat +
|
||||
"&dlon=" + lng +
|
||||
"&dname=" + address +
|
||||
"&dev=" + 0 +
|
||||
"&t=" + 0
|
||||
val intent = Intent("android.intent.action.VIEW", Uri.parse(stringBuffer))
|
||||
intent.setPackage("com.autonavi.minimap")
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.za.common.util
|
||||
|
||||
object NotificationUtil {
|
||||
const val CHANNEL_ID = "1001"
|
||||
}
|
92
servicing/src/main/java/com/za/common/util/QRCodeUtil.kt
Normal file
92
servicing/src/main/java/com/za/common/util/QRCodeUtil.kt
Normal file
@ -0,0 +1,92 @@
|
||||
package com.za.common.util
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.text.TextUtils
|
||||
import androidx.annotation.ColorInt
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.WriterException
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import java.util.Hashtable
|
||||
|
||||
/**
|
||||
* @ClassName: QRCodeUtil
|
||||
* @Description: 二维码工具类
|
||||
*/
|
||||
object QRCodeUtil {
|
||||
/**
|
||||
* 创建二维码位图 (支持自定义配置和自定义样式)
|
||||
*
|
||||
* @param content 字符串内容
|
||||
* @param width 位图宽度,要求>=0(单位:px)
|
||||
* @param height 位图高度,要求>=0(单位:px)
|
||||
* @param character_set 字符集/字符转码格式 (支持格式:[CharacterSetECI])。传null时,zxing源码默认使用 "ISO-8859-1"
|
||||
* @param error_correction 容错级别 (支持级别:[ErrorCorrectionLevel])。传null时,zxing源码默认使用 "L"
|
||||
* @param margin 空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"4"。
|
||||
* @param color_black 黑色色块的自定义颜色值
|
||||
* @param color_white 白色色块的自定义颜色值
|
||||
* @return
|
||||
*/
|
||||
/**
|
||||
* 创建二维码位图
|
||||
*
|
||||
* @param content 字符串内容(支持中文)
|
||||
* @param width 位图宽度(单位:px)
|
||||
* @param height 位图高度(单位:px)
|
||||
* @return
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun createQRCodeBitmap(content: String?, width: Int, height: Int,
|
||||
characterSet: String? = "UTF-8", errorCorrection: String? = "H", margin: String? = "2",
|
||||
@ColorInt colorBlack: Int = Color.BLACK, @ColorInt colorWhite: Int = Color.WHITE): Bitmap? {
|
||||
/** 1.参数合法性判断 */
|
||||
|
||||
if (TextUtils.isEmpty(content)) { // 字符串内容判空
|
||||
return null
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0) { // 宽和高都需要>=0
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
/** 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */
|
||||
val hints = Hashtable<EncodeHintType, String?>()
|
||||
|
||||
if (!TextUtils.isEmpty(characterSet)) {
|
||||
hints[EncodeHintType.CHARACTER_SET] = characterSet // 字符转码格式设置
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(errorCorrection)) {
|
||||
hints[EncodeHintType.ERROR_CORRECTION] = errorCorrection // 容错级别设置
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(margin)) {
|
||||
hints[EncodeHintType.MARGIN] = margin // 空白边距设置
|
||||
}
|
||||
val bitMatrix = QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints)
|
||||
|
||||
/** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */
|
||||
val pixels = IntArray(width * height)
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
if (bitMatrix[x, y]) {
|
||||
pixels[y * width + x] = colorBlack // 黑色色块像素设置
|
||||
} else {
|
||||
pixels[y * width + x] = colorWhite // 白色色块像素设置
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回Bitmap对象 */
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
bitmap.setPixels(pixels, 0, width, 0, 0, width, height)
|
||||
return bitmap
|
||||
} catch (e: WriterException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.za.common.util
|
||||
|
||||
import android.content.Context
|
||||
import android.media.MediaPlayer
|
||||
import com.za.servicing.R
|
||||
|
||||
object ServicingSpeechManager {
|
||||
private var mediaPlayer: MediaPlayer? = null
|
||||
|
||||
// 提醒客户签字
|
||||
fun playOrderCustomSign(mContext: Context) {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.custom_sign)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
|
||||
// 提醒接车人签字
|
||||
fun playOrderAcceptSign(mContext: Context) {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.accept_sign)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
// 车主签字
|
||||
fun playCarOwnerSign(mContext: Context) {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.car_onwer_sign)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
// 提醒五星好评
|
||||
fun playOrderGoodService(mContext: Context) {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.good_star)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
//提醒客户不需要再上传纸质工单
|
||||
fun playNoUploadEleOrderWork(mContext: Context) {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.no_upload_ele_order_work)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
|
||||
// 发车前提示
|
||||
fun playStartTip(mContext: Context) {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.start_tip)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
// 验车前提示
|
||||
fun playCheckTip(mContext: Context) {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.check_tip)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
}
|
33
servicing/src/main/java/com/za/common/util/Tools.kt
Normal file
33
servicing/src/main/java/com/za/common/util/Tools.kt
Normal file
@ -0,0 +1,33 @@
|
||||
package com.za.common.util
|
||||
|
||||
import android.util.Base64
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/**
|
||||
* @author DoggieX
|
||||
* @create 2021/3/22 16:17
|
||||
* @mail coldpuppy@163.com
|
||||
*/
|
||||
object Tools {
|
||||
/**
|
||||
* @param text 要签名的文本
|
||||
* @param secretKey 阿里云MQ secretKey
|
||||
* @return 加密后的字符串
|
||||
* @throws InvalidKeyException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
@Throws(InvalidKeyException::class, NoSuchAlgorithmException::class)
|
||||
fun macSignature(text: String, secretKey: String): String {
|
||||
val charset = StandardCharsets.UTF_8
|
||||
val algorithm = "HmacSHA1"
|
||||
val mac = Mac.getInstance(algorithm)
|
||||
mac.init(SecretKeySpec(secretKey.toByteArray(charset), algorithm))
|
||||
val bytes = mac.doFinal(text.toByteArray(charset))
|
||||
// android的base64编码注意换行符情况, 使用NO_WRAP
|
||||
return String(Base64.encode(bytes, Base64.NO_WRAP), charset)
|
||||
}
|
||||
}
|
53
servicing/src/main/java/com/za/ext/ActivityExt.kt
Normal file
53
servicing/src/main/java/com/za/ext/ActivityExt.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package com.za.ext
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
|
||||
|
||||
fun ComponentActivity.navigationActivity(destActivity: Class<*>) {
|
||||
ActivityCompat.startActivity(this, Intent(this, destActivity), null)
|
||||
}
|
||||
|
||||
fun ComponentActivity.navigationActivity(destActivity: Class<*>, requestCode: Int) {
|
||||
ActivityCompat.startActivityForResult(this, Intent(this, destActivity), requestCode, null)
|
||||
}
|
||||
|
||||
fun ComponentActivity.navigationActivity(intent: Intent, requestCode: Int) {
|
||||
ActivityCompat.startActivityForResult(this, intent, requestCode, null)
|
||||
}
|
||||
|
||||
fun ComponentActivity.navigationActivity(destActivity: Class<*>, isFinish: Boolean? = false) {
|
||||
ActivityCompat.startActivity(this, Intent(this, destActivity), null)
|
||||
if (isFinish == true) {
|
||||
this.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun ComponentActivity.navigationActivity(destActivity: Class<*>, key: String, value: String?) {
|
||||
val intent = Intent(this, destActivity)
|
||||
intent.putExtra(key, value)
|
||||
ActivityCompat.startActivity(this, intent, null)
|
||||
}
|
||||
|
||||
fun ComponentActivity.callPhone(phone: String?) {
|
||||
if (phone.isNullOrBlank()) {
|
||||
ToastUtils.showShort("号码错误!")
|
||||
// LogUtil.print("call phone","号码错误=$phone")
|
||||
return
|
||||
}
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_DENIED) {
|
||||
ToastUtils.showShort("请先允许通话权限!")
|
||||
// LogUtil.print("call phone","拨打电话权限未允许")
|
||||
return
|
||||
}
|
||||
|
||||
val intentPhone = Intent(Intent.ACTION_DIAL, Uri.parse("tel:$phone"))
|
||||
ActivityCompat.startActivity(this, intentPhone, null)
|
||||
}
|
||||
|
98
servicing/src/main/java/com/za/ext/ContextExt.kt
Normal file
98
servicing/src/main/java/com/za/ext/ContextExt.kt
Normal file
@ -0,0 +1,98 @@
|
||||
package com.za.ext
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import com.za.call.CallLogManager
|
||||
import com.za.call.ContactRecordBean
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
|
||||
fun Context.navigationActivity(destActivity : Class<*>) {
|
||||
if (this !is Activity) {
|
||||
return
|
||||
}
|
||||
ActivityCompat.startActivity(this, Intent(this, destActivity), null)
|
||||
}
|
||||
|
||||
fun Context.navigationActivity(destActivity : Class<*>, requestCode : Int) {
|
||||
if (this !is Activity) {
|
||||
return
|
||||
}
|
||||
ActivityCompat.startActivityForResult(this, Intent(this, destActivity), requestCode, null)
|
||||
}
|
||||
|
||||
fun Context.navigationActivity(intent : Intent, requestCode : Int) {
|
||||
if (this !is Activity) {
|
||||
return
|
||||
}
|
||||
ActivityCompat.startActivityForResult(this, intent, requestCode, null)
|
||||
}
|
||||
|
||||
fun Context.navigationActivity(destActivity : Class<*>, isFinish : Boolean? = false) {
|
||||
if (this !is Activity) {
|
||||
return
|
||||
}
|
||||
ActivityCompat.startActivity(this, Intent(this, destActivity), null)
|
||||
if (isFinish == true) {
|
||||
this.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.navigationActivity(destActivity : Class<*>, key : String, value : String?) {
|
||||
if (this !is Activity) {
|
||||
return
|
||||
}
|
||||
val intent = Intent(this, destActivity)
|
||||
intent.putExtra(key, value)
|
||||
ActivityCompat.startActivity(this, intent, null)
|
||||
}
|
||||
|
||||
fun Context.finish() {
|
||||
if (this !is Activity) {
|
||||
return
|
||||
}
|
||||
this.finish()
|
||||
}
|
||||
|
||||
|
||||
fun AppCompatActivity.navigationActivity(destActivity : Class<*>, key : String, value : String?) {
|
||||
val intent = Intent(this, destActivity)
|
||||
intent.putExtra(key, value)
|
||||
ActivityCompat.startActivity(this, intent, null)
|
||||
}
|
||||
|
||||
fun Context.callPhone(phone : String?) {
|
||||
if (this !is Activity) {
|
||||
return
|
||||
}
|
||||
if (phone.isNullOrBlank()) {
|
||||
ToastUtils.showShort("号码错误!")
|
||||
LogUtil.print("call phone", "号码错误=$phone")
|
||||
return
|
||||
}
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_DENIED
|
||||
) {
|
||||
ToastUtils.showShort("请先允许通话权限!")
|
||||
LogUtil.print("call phone", "拨打电话权限未允许")
|
||||
return
|
||||
}
|
||||
|
||||
//如果是联系客户,则记录通话记录
|
||||
if (GlobalData.currentOrder != null && GlobalData.currentOrder?.customerPhone == phone) {
|
||||
CallLogManager.phoneCallContactBean =
|
||||
ContactRecordBean(taskId = GlobalData.currentOrder?.taskId,
|
||||
taskCode = GlobalData.currentOrder?.taskCode,
|
||||
phone = phone)
|
||||
}
|
||||
val intentPhone = Intent(Intent.ACTION_DIAL, Uri.parse("tel:$phone"))
|
||||
ActivityCompat.startActivity(this, intentPhone, null)
|
||||
}
|
22
servicing/src/main/java/com/za/ext/ModifierExt.kt
Normal file
22
servicing/src/main/java/com/za/ext/ModifierExt.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package com.za.ext
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.za.base.Const
|
||||
|
||||
@Composable
|
||||
fun Modifier.noDoubleClick(onClick: () -> Unit): Modifier {
|
||||
var lastClickTime by remember { mutableLongStateOf(value = 0L) }
|
||||
return clickable(enabled = true) {
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
if (currentTimeMillis - Const.DoubleClickTime >= lastClickTime) {
|
||||
onClick()
|
||||
lastClickTime = currentTimeMillis
|
||||
}
|
||||
}
|
||||
}
|
139
servicing/src/main/java/com/za/ext/OrderInfoExt.kt
Normal file
139
servicing/src/main/java/com/za/ext/OrderInfoExt.kt
Normal file
@ -0,0 +1,139 @@
|
||||
package com.za.ext
|
||||
|
||||
import android.content.Context
|
||||
import com.alibaba.fastjson.JSON
|
||||
import com.blankj.utilcode.util.EncodeUtils
|
||||
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.room.RoomHelper
|
||||
import com.za.ui.servicing.check_vehicle.CheckVehicleActivity
|
||||
import com.za.ui.servicing.departure_photo.DeparturePhotoActivity
|
||||
import com.za.ui.servicing.destination_photo.DestinationPhotoActivity
|
||||
import com.za.ui.servicing.go_accident.GoAccidentSiteActivity
|
||||
import com.za.ui.servicing.go_to_destination.GoToDestinationActivity
|
||||
import com.za.ui.servicing.operation.InOperationActivity
|
||||
import com.za.ui.servicing.order_confirm.OrderConfirmActivity
|
||||
import com.za.ui.servicing.verify.VerifyOrderActivity
|
||||
import com.za.ui.servicing.wait_to_start.WaitToStartActivity
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
fun Any?.toJson() : String? {
|
||||
if (this == null) {
|
||||
return null
|
||||
}
|
||||
return JSON.toJSON(this).toString()
|
||||
}
|
||||
|
||||
fun OrderInfo.goStatusPage(context : Context) {
|
||||
when (this.taskState) { //等待发车
|
||||
"START" -> {
|
||||
if (! RoomHelper.db?.photoTemplateDao()
|
||||
?.getOrderPhotoTemplateFromTaskNode(10100, userOrderId ?: 0).isNullOrEmpty()
|
||||
) {
|
||||
context.navigationActivity(DeparturePhotoActivity::class.java)
|
||||
return
|
||||
}
|
||||
context.navigationActivity(WaitToStartActivity::class.java)
|
||||
}
|
||||
|
||||
"GOTO" -> context.navigationActivity(GoAccidentSiteActivity::class.java) //验证服务资格
|
||||
"VERIFY" -> context.navigationActivity(VerifyOrderActivity::class.java) //检查车辆
|
||||
"EXAMINE" -> context.navigationActivity(CheckVehicleActivity::class.java) //作业中
|
||||
"OPERATION" -> context.navigationActivity(InOperationActivity::class.java) //前往目的地
|
||||
"SENDTO" -> context.navigationActivity(GoToDestinationActivity::class.java) //目的地照片
|
||||
"DESTPHOTO" -> context.navigationActivity(DestinationPhotoActivity::class.java) //结算中
|
||||
"SETTLEMENT" -> context.navigationActivity(OrderConfirmActivity::class.java) //任务结束
|
||||
"FINISH" -> context.navigationActivity(WaitToStartActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun OrderInfo.getNextStatus() : String? {
|
||||
val flowStr : String?
|
||||
val flowArray = flow?.split(",")
|
||||
if (flowArray.isNullOrEmpty()) {
|
||||
LogUtil.print("getNextStatus", "订单流程为空")
|
||||
return null
|
||||
}
|
||||
flowStr = flowArray[flowArray.indexOf(this.taskState) + 1]
|
||||
LogUtil.print("getNextStatus flowStr", "$flowStr")
|
||||
return flowStr
|
||||
}
|
||||
|
||||
fun goNextPage(status : String?, context : Context) {
|
||||
when (status) { //等待发车
|
||||
"START" -> {
|
||||
if (! RoomHelper.db?.photoTemplateDao()?.getOrderPhotoTemplateFromTaskNode(10100,
|
||||
GlobalData.currentOrder?.userOrderId ?: 0).isNullOrEmpty()
|
||||
) {
|
||||
context.navigationActivity(DeparturePhotoActivity::class.java)
|
||||
return
|
||||
}
|
||||
context.navigationActivity(WaitToStartActivity::class.java)
|
||||
} //前往事发地
|
||||
"GOTO" -> context.navigationActivity(GoAccidentSiteActivity::class.java) //验证服务资格
|
||||
"VERIFY" -> context.navigationActivity(VerifyOrderActivity::class.java) //检查车辆
|
||||
"EXAMINE" -> context.navigationActivity(CheckVehicleActivity::class.java) //作业中
|
||||
"OPERATION" -> context.navigationActivity(InOperationActivity::class.java) //前往目的地
|
||||
"SENDTO" -> context.navigationActivity(GoToDestinationActivity::class.java) //目的地照片
|
||||
"DESTPHOTO" -> context.navigationActivity(DestinationPhotoActivity::class.java) //结算中
|
||||
"SETTLEMENT" -> context.navigationActivity(OrderConfirmActivity::class.java) //任务结束
|
||||
"FINISH" -> context.navigationActivity(WaitToStartActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun OrderInfo.getTaskNode() : Int { //防空的节点为 18100
|
||||
return when (this.taskState) { //检查车辆
|
||||
"EXAMINE" -> 13001 //作业中
|
||||
"OPERATION" -> 15001 //目的地照片
|
||||
"DESTPHOTO" -> 17001 //结算中
|
||||
"SETTLEMENT" -> 18001
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
fun OrderInfo.convertToFlowName() : String {
|
||||
return when (this.taskState) {
|
||||
"START" -> "等待发车"
|
||||
"GOTO" -> "前往事发地"
|
||||
"VERIFY" -> "验证服务资格"
|
||||
"EXAMINE" -> "检查车辆"
|
||||
"OPERATION" -> "作业中"
|
||||
"SENDTO", "SIMPLE_CARRYING" -> "前往目的地"
|
||||
"DESTPHOTO", "SIMPLE_DEST_PHOTO" -> "目的地照片"
|
||||
"SIMPLE_SETTLEMENT", "SETTLEMENT" -> "结算中"
|
||||
"FINISH" -> "任务结束"
|
||||
"SIMPLE_ARRIVE_PHOTO" -> "现场照片"
|
||||
"SIMPLE_CARRY_PRE" -> "开始拖车"
|
||||
"SIMPLE_DESP_PHOTO" -> "完成确认"
|
||||
|
||||
else -> "继续任务"
|
||||
}
|
||||
}
|
||||
|
||||
fun OrderInfo.getEleState() : Int {
|
||||
if (RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId ?: 0) == null) {
|
||||
LogUtil.print("作业中获取电子表单未空", "电子表单为空")
|
||||
return electronOrderState ?: 0
|
||||
}
|
||||
|
||||
if (electronOrderState == RoomHelper.db?.eleWorkOrderDao()
|
||||
?.getEleWorkOrder(taskId ?: 0)?.orderWorkStatus
|
||||
) {
|
||||
return electronOrderState ?: 0
|
||||
}
|
||||
|
||||
return (electronOrderState ?: 0).coerceAtLeast(RoomHelper.db?.eleWorkOrderDao()
|
||||
?.getEleWorkOrder(taskId ?: 0)?.orderWorkStatus ?: 0)
|
||||
}
|
||||
|
||||
fun OrderInfo.getEleOrderH5Url() : String? {
|
||||
if (GlobalData.currentOrder == null) {
|
||||
return null
|
||||
}
|
||||
val queryParams =
|
||||
"userOrderId=${GlobalData.currentOrder?.userOrderId}&userOrderCode=${GlobalData.currentOrder?.taskCode}&supplierId=${GlobalData.currentOrder?.userOrderId}"
|
||||
return AppConfig.Resource_URL + "/electronicWorkOrder/index.html?" + EncodeUtils.base64Encode2String(
|
||||
queryParams.toByteArray(StandardCharsets.UTF_8))
|
||||
}
|
27
servicing/src/main/java/com/za/ext/StringExt.kt
Normal file
27
servicing/src/main/java/com/za/ext/StringExt.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package com.za.ext
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Context.CLIPBOARD_SERVICE
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
|
||||
fun String.taskStateToFlow() : String {
|
||||
return when (this) {
|
||||
"13001" -> "EXAMINE"
|
||||
"18001" -> "FINISH"
|
||||
"15001" -> "OPERATION"
|
||||
"17001" -> "DESTPHOTO"
|
||||
"18100" -> "GIVEUP"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fun String?.copy(context : Context) {
|
||||
if (this.isNullOrBlank()) {
|
||||
return
|
||||
}
|
||||
val clipboardManager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboardManager.setPrimaryClip(ClipData.newPlainText("", this))
|
||||
ToastUtils.showLong("复制成功")
|
||||
}
|
123
servicing/src/main/java/com/za/net/AESUtils.java
Normal file
123
servicing/src/main/java/com/za/net/AESUtils.java
Normal file
@ -0,0 +1,123 @@
|
||||
package com.za.net;
|
||||
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class AESUtils {
|
||||
|
||||
private static final String cipherMode = "AES/ECB/PKCS5Padding";//算法/模式/补码方式
|
||||
|
||||
/* AES秘钥支持128bit/192bit/256bit三种长度的秘钥,一个字节等于8bit,
|
||||
* 因此支持生成的字符串的长度应该是 16/24/32
|
||||
* */
|
||||
private static final int keyLength = 16;
|
||||
|
||||
|
||||
// public static void main(String[] args) {
|
||||
//
|
||||
// /*构建一个随机密码*/
|
||||
// String key = getRandomKey(keyLength);
|
||||
// System.out.println("随机生成的key:" + key);
|
||||
//
|
||||
// String data = "{'fig':1,'message':'登录成功'}";
|
||||
//
|
||||
// try {
|
||||
// String encriptData = AESUtils.encrypt(data, key);
|
||||
// System.out.println("加密后的数据:" + encriptData);
|
||||
//
|
||||
// String decryptData = decrypt(encriptData, key);
|
||||
//
|
||||
// System.out.println("解密后的数据:" + decryptData);
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* @param length 需要生成的字符串长度
|
||||
* @return 随机生成的字符串
|
||||
*/
|
||||
public static String getRandomKey(int length) {
|
||||
|
||||
if (length != 16 && length != 24 && length != 32) {
|
||||
System.out.println("长度必须为16/24/32");
|
||||
return null;
|
||||
}
|
||||
|
||||
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
Random random = new Random();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int number = random.nextInt(62);
|
||||
stringBuilder.append(str.charAt(number));
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param data 需要加密的数据
|
||||
* @param key 加密使用的key
|
||||
* @return 加密后的数据(Base64编码)
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String encrypt(String data, String key) throws Exception {
|
||||
|
||||
int length = key.length();
|
||||
if (length != 16 && length != 24 && length != 32) {
|
||||
System.out.println("长度必须为16/24/32");
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] raw = key.getBytes(StandardCharsets.UTF_8);
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
|
||||
Cipher cipher = Cipher.getInstance(cipherMode);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
|
||||
byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
return Base64.encodeToString(encrypted, Base64.URL_SAFE | Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param data 需要解密的数据
|
||||
* @param key 解密用的key
|
||||
* @return 解密后的数据
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String decrypt(String data, String key) throws Exception {
|
||||
try {
|
||||
int length = key.length();
|
||||
if (length != 16 && length != 24 && length != 32) {
|
||||
System.out.println("长度必须为16/24/32");
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] raw = key.getBytes(StandardCharsets.UTF_8);
|
||||
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
|
||||
Cipher cipher = Cipher.getInstance(cipherMode);
|
||||
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
|
||||
byte[] encrypted = Base64.decode(data,Base64.URL_SAFE | Base64.NO_WRAP);//先用base64解密
|
||||
try {
|
||||
byte[] original = cipher.doFinal(encrypted);
|
||||
return new String(original, StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.toString());
|
||||
return null;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
System.out.println(ex.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
265
servicing/src/main/java/com/za/net/ApiService.kt
Normal file
265
servicing/src/main/java/com/za/net/ApiService.kt
Normal file
@ -0,0 +1,265 @@
|
||||
package com.za.net
|
||||
|
||||
import com.za.bean.BaseResponse
|
||||
import com.za.bean.BatteryCostQueryBean
|
||||
import com.za.bean.BatteryCostQueryRequest
|
||||
import com.za.bean.ChangeBatteryResponse
|
||||
import com.za.bean.DriverIdentityAuthWebBean
|
||||
import com.za.bean.DriverIdentityAuthWebRequest
|
||||
import com.za.bean.DriverInfo
|
||||
import com.za.bean.FetchChangeBatteryPhotoRequest
|
||||
import com.za.bean.GeneralInfo
|
||||
import com.za.bean.HistoryPhotoTemplates
|
||||
import com.za.bean.HistoryTaskBean
|
||||
import com.za.bean.ImageBean
|
||||
import com.za.bean.JpushBean
|
||||
import com.za.bean.LoginWithTaskBean
|
||||
import com.za.bean.LoginWithTaskRequest
|
||||
import com.za.bean.NewOrderRequestBean
|
||||
import com.za.bean.PaymentInfoBean
|
||||
import com.za.bean.ReportHistoryBean
|
||||
import com.za.bean.ReportHistoryRequest
|
||||
import com.za.bean.ReportInfoRequest
|
||||
import com.za.bean.ReportItem
|
||||
import com.za.bean.SettleInfoRequest
|
||||
import com.za.bean.TaskSettlementAndTraceBean
|
||||
import com.za.bean.UpdateVersionBean
|
||||
import com.za.bean.UpdateVersionRequest
|
||||
import com.za.bean.UploadChangeBatteryRequest
|
||||
import com.za.bean.VehicleInfo
|
||||
import com.za.bean.db.order.OrderInfo
|
||||
import com.za.bean.db.order.PhotoTemplateInfo
|
||||
import com.za.bean.request.AcceptOrderRequest
|
||||
import com.za.bean.request.CustomerPaymentCreateBean
|
||||
import com.za.bean.request.CustomerPaymentCreateRequest
|
||||
import com.za.bean.request.DriverFaceCompareBean
|
||||
import com.za.bean.request.DriverFaceCompareRequest
|
||||
import com.za.bean.request.ElectronOrderResponse
|
||||
import com.za.bean.request.FetchVehicleMaintenanceSubmitHistoryRequestBean
|
||||
import com.za.bean.request.GeneralInfoRequest
|
||||
import com.za.bean.request.GiveUpTaskRequest
|
||||
import com.za.bean.request.HistoryDetailRequest
|
||||
import com.za.bean.request.HistoryPhotoTemplateRequest
|
||||
import com.za.bean.request.HistoryTasksRequest
|
||||
import com.za.bean.request.LoginRequest
|
||||
import com.za.bean.request.OrderListRequest
|
||||
import com.za.bean.request.OrderPhotoOcrRecognizeRequest
|
||||
import com.za.bean.request.PaymentInfoRequest
|
||||
import com.za.bean.request.PaymentUpdateRequest
|
||||
import com.za.bean.request.PhotoTemplateRequest
|
||||
import com.za.bean.request.QueryEleOrderRequest
|
||||
import com.za.bean.request.RecognizeRefuelOcrRequestBean
|
||||
import com.za.bean.request.RecognizeRefuelTicketBean
|
||||
import com.za.bean.request.RecognizeRefuelTicketRequestBean
|
||||
import com.za.bean.request.RefuseOrderRequest
|
||||
import com.za.bean.request.SaveEleOrderRequest
|
||||
import com.za.bean.request.SwitchTaskRequest
|
||||
import com.za.bean.request.TaskFinishRequest
|
||||
import com.za.bean.request.TaskFinishResponse
|
||||
import com.za.bean.request.TodayMaintainRequest
|
||||
import com.za.bean.request.TodayMaintainUploadRequest
|
||||
import com.za.bean.request.TodayMaintainbean
|
||||
import com.za.bean.request.UpdateOrderConfirmTaskRequest
|
||||
import com.za.bean.request.UpdatePhotoRequest
|
||||
import com.za.bean.request.UpdateTaskBean
|
||||
import com.za.bean.request.UpdateTaskRequest
|
||||
import com.za.bean.request.UpdateVehicleStateRequest
|
||||
import com.za.bean.request.UploadGpsRequest
|
||||
import com.za.bean.request.UploadPhotoBean
|
||||
import com.za.bean.request.VehicleListRequest
|
||||
import com.za.bean.request.VehicleLogoutRequest
|
||||
import com.za.bean.request.VehicleMaintenanceHistoryBean
|
||||
import com.za.bean.request.VehicleMaintenanceSubmitRequest
|
||||
import com.za.bean.request.VehicleRepairBean
|
||||
import com.za.bean.request.VehicleRepairPointMatcherItem
|
||||
import com.za.bean.request.VehicleRepairPointMatcherListRequest
|
||||
import com.za.bean.request.VerifyCodeRequest
|
||||
import com.za.bean.request.VerifyCodeResponse
|
||||
import com.za.call.ContactRecordRequest
|
||||
import com.za.water_marker.bean.MyWaterMarkerTemplateBean
|
||||
import com.za.water_marker.bean.WaterMarkerRequestBean
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import okhttp3.MultipartBody
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
|
||||
interface ApiService {
|
||||
|
||||
//获取车辆列表
|
||||
@POST("/driverApp/supplier/listVehicleNew")
|
||||
fun getVehicleList(@Body info : VehicleListRequest) : Observable<BaseResponse<List<VehicleInfo>>>
|
||||
|
||||
//更改车辆状态
|
||||
@POST("/driverApp/task/switchVehicleStatus")
|
||||
fun newUpdateVehicleState(@Body info : UpdateVehicleStateRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
@POST("/driverApp/v2/user/generalInfo")
|
||||
fun generalInfo(@Body info : GeneralInfoRequest) : Observable<BaseResponse<GeneralInfo>>
|
||||
|
||||
@POST("/driverApp/base/appVersion")
|
||||
fun getUpdate(@Body versionRequest : UpdateVersionRequest) : Observable<BaseResponse<UpdateVersionBean>>
|
||||
|
||||
@POST("/driverApp/base/getVerifyCode")
|
||||
fun getVerifyCode(@Body verifyCodeRequest : VerifyCodeRequest) : Observable<BaseResponse<VerifyCodeResponse>>
|
||||
|
||||
@Multipart
|
||||
@POST("/order/uploadImage")
|
||||
fun uploadImage(@Part part : MultipartBody.Part) : Observable<ImageBean>
|
||||
|
||||
@POST("/driverApp/task/uploadContactRecord")
|
||||
fun uploadContactRecord(@Body contactRecordRequest : ContactRecordRequest?) : Observable<BaseResponse<String>>
|
||||
|
||||
@POST("/driverApp/task/login")
|
||||
fun login(@Body info : LoginRequest) : Observable<BaseResponse<DriverInfo?>>
|
||||
|
||||
@POST("/driverApp/task/loginWithTask")
|
||||
fun loginWithTask(@Body loginWithTaskRequest : LoginWithTaskRequest) : Observable<BaseResponse<LoginWithTaskBean>>
|
||||
|
||||
@POST("/driverApp/supplier/vehicleLogout")
|
||||
fun vehicleLogout(@Body info : VehicleLogoutRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
//当前订单列表
|
||||
@POST("/driverApp/v2/task/listCurrentTask")
|
||||
fun queryOrderList(@Body info : OrderListRequest) : Observable<BaseResponse<List<OrderInfo>?>>
|
||||
|
||||
@POST("/driverApp/task/getNewOrder")
|
||||
fun getNewOrder(@Body newOrderRequestBean : NewOrderRequestBean) : Observable<BaseResponse<JpushBean>>
|
||||
|
||||
@POST("/driverApp/supplier/uploadGps")
|
||||
fun uploadGps(@Body info : UploadGpsRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
//获取报备内容列表
|
||||
@POST("/driverApp/base/getReportTemplates")
|
||||
fun getReportTemplates() : Observable<BaseResponse<List<ReportItem>>>
|
||||
|
||||
//提交报备
|
||||
@POST("/driverApp/task/submitReport")
|
||||
fun submitReport(@Body reportInfoRequest : ReportInfoRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
//历史报备内容
|
||||
@POST("/driverApp/task/reportHistory")
|
||||
fun getReportHistory(@Body reportHistoryRequest : ReportHistoryRequest) : Observable<BaseResponse<List<ReportHistoryBean>>>
|
||||
|
||||
//拒绝任务
|
||||
@POST("/driverApp/task/refuseTask")
|
||||
fun refuseOrder(@Body request : RefuseOrderRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
//接受任务
|
||||
@POST("/driverApp/task/acceptTask")
|
||||
fun acceptOrder(@Body request : AcceptOrderRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
//更新任务
|
||||
@POST("/driverApp/v4/task/updateTask")
|
||||
fun updateTask(@Body request : UpdateTaskRequest) : Observable<BaseResponse<UpdateTaskBean>>
|
||||
|
||||
//更新任务
|
||||
@POST("/driverApp/v4/task/updateTask")
|
||||
fun submitOrderConfirmTask(@Body request : UpdateOrderConfirmTaskRequest) : Observable<BaseResponse<UpdateTaskBean>>
|
||||
|
||||
@POST("/driverApp/v2/supplier/photoTemplate")
|
||||
fun fetchPhotoTemplate(@Body request : PhotoTemplateRequest) : Observable<BaseResponse<List<PhotoTemplateInfo>>>
|
||||
|
||||
@POST("/driverApp/supplier/getWatermarkTemplateConfig")
|
||||
fun getWatermarkTemplateConfig(@Body request : WaterMarkerRequestBean) : Observable<BaseResponse<MyWaterMarkerTemplateBean>>
|
||||
|
||||
@POST("/driverApp/supplier/orderPhotoOcrRecognize")
|
||||
fun orderPhotoOcrRecognize(@Body info : OrderPhotoOcrRecognizeRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
@POST("/driverApp/v4/supplier/saveElectronOrder")
|
||||
fun saveElectronOrder(@Body info : SaveEleOrderRequest?) : Observable<BaseResponse<String>>
|
||||
|
||||
@POST("/driverApp/supplier/queryElectronOrder")
|
||||
fun queryElectronOrder(@Body info : QueryEleOrderRequest?) : Observable<BaseResponse<ElectronOrderResponse>>
|
||||
|
||||
//查询支付信息
|
||||
@POST("/driverApp/payment/paymentInfoQuery")
|
||||
fun paymentInfoQuery(@Body info : PaymentInfoRequest?) : Observable<BaseResponse<PaymentInfoBean>>
|
||||
|
||||
@POST("/driverApp/payment/customerPaymentCreate")
|
||||
fun customerPaymentCreate(@Body customerPaymentCreateRequest : CustomerPaymentCreateRequest) : Observable<BaseResponse<CustomerPaymentCreateBean>>
|
||||
|
||||
@POST("/driverApp/payment/paymentAmountUpdate")
|
||||
fun paymentAmountUpdate(@Body paymentUpdateRequest : PaymentUpdateRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
@POST("/driverApp/battery/saveReplaceBatteryPhoto")
|
||||
fun saveReplaceBatteryPhoto(@Body info : UploadChangeBatteryRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
@POST("/driverApp/supplier/getReplaceBatteryPhoto")
|
||||
fun getReplaceBatteryPhoto(@Body info : FetchChangeBatteryPhotoRequest) : Observable<BaseResponse<List<ChangeBatteryResponse>>>
|
||||
|
||||
//电瓶费用查询
|
||||
@POST("/driverApp/battery/batteryCostQuery")
|
||||
fun batteryCostQuery(@Body info : BatteryCostQueryRequest) : Observable<BaseResponse<BatteryCostQueryBean>>
|
||||
|
||||
@POST("/driverApp/v4/task/taskFinish")
|
||||
fun taskFinish(@Body info : TaskFinishRequest) : Observable<BaseResponse<TaskFinishResponse>>
|
||||
|
||||
@POST("/driverApp/task/giveUpTask")
|
||||
fun giveUpTask(@Body info : GiveUpTaskRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
//历史订单补传照片
|
||||
@POST("/driverApp/task/updatePhoto")
|
||||
fun addPhoto(@Body params : UpdatePhotoRequest) : Observable<BaseResponse<UploadPhotoBean>>
|
||||
|
||||
@POST("/driverApp/v2/task/switchTask")
|
||||
fun switchTask(@Body info : SwitchTaskRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
@POST("/driverApp/task/listHistoryTaskNew")
|
||||
fun getHistory(@Body info : HistoryTasksRequest) : Observable<BaseResponse<List<HistoryTaskBean>>>
|
||||
|
||||
@POST("/driverApp/task/getHistoryTaskPhotos")
|
||||
fun getHistoryTaskPhotos(@Body info : HistoryPhotoTemplateRequest) : Observable<BaseResponse<List<HistoryPhotoTemplates>>>
|
||||
|
||||
//历史结算单和轨迹
|
||||
@POST("/driverApp/task/getTaskSettlementAndTrace")
|
||||
fun getTaskSettlementAndTrace(@Body info : HistoryDetailRequest) : Observable<BaseResponse<TaskSettlementAndTraceBean>>
|
||||
|
||||
@POST("driverApp/task/updateSettleInfo")
|
||||
fun updateSettleInfo(@Body settleInfoRequest : SettleInfoRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
|
||||
//人脸比对
|
||||
@POST("driverApp/supplier/driverFaceCompare")
|
||||
fun driverFaceCompare(@Body driverFaceCompareRequest : DriverFaceCompareRequest) : Observable<BaseResponse<DriverFaceCompareBean>>
|
||||
|
||||
@POST("driverApp/supplier/driverIdentityAuthWeb")
|
||||
fun driverIdentityAuthWeb(@Body request : DriverIdentityAuthWebRequest) : Observable<BaseResponse<DriverIdentityAuthWebBean>>
|
||||
|
||||
//今日保养
|
||||
@POST("driverApp/supplier/getTodayMaintain")
|
||||
fun getTodayMaintain(@Body info : TodayMaintainRequest) : Observable<BaseResponse<List<TodayMaintainbean>>>
|
||||
|
||||
//今日保养
|
||||
@POST("driverApp/supplier/uploadTodayMaintain")
|
||||
fun uploadTodayMaintain(@Body params : TodayMaintainUploadRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
//加油小票识别
|
||||
@POST("driverApp/supplier/recognizeRefuelTicket")
|
||||
fun recognizeRefuelTicket(@Body bean : RecognizeRefuelOcrRequestBean?) : Observable<BaseResponse<RecognizeRefuelTicketBean>>
|
||||
|
||||
//提交加油记录
|
||||
@POST("driverApp/supplier/vehicleRefuelSubmit")
|
||||
fun vehicleRefuelSubmit(@Body info : RecognizeRefuelTicketRequestBean?) : Observable<BaseResponse<String>>
|
||||
|
||||
//获取车辆维保记录
|
||||
@POST("driverApp/supplier/v2/getVehicleMaintenanceSubmit")
|
||||
fun getVehicleMaintenanceSubmit(@Body info : FetchVehicleMaintenanceSubmitHistoryRequestBean) : Observable<BaseResponse<VehicleRepairBean>>
|
||||
|
||||
//获取车辆维保详细信息
|
||||
@POST("driverApp/supplier/v2/getVehicleMaintenanceDetail")
|
||||
fun getVehicleRepairDetail(@Body info : FetchVehicleMaintenanceSubmitHistoryRequestBean) : Observable<BaseResponse<VehicleRepairBean>>
|
||||
|
||||
//维修地点匹配
|
||||
@POST("driverApp/supplier/v2/vehicleRepairPointMatcherList")
|
||||
fun vehicleRepairPointMatcherList(@Body info : VehicleRepairPointMatcherListRequest) : Observable<BaseResponse<List<VehicleRepairPointMatcherItem>>>
|
||||
|
||||
//提交维保记录
|
||||
@POST("driverApp/supplier/v2/vehicleMaintenanceSubmit")
|
||||
fun vehicleMaintenanceSubmit(@Body info : VehicleMaintenanceSubmitRequest) : Observable<BaseResponse<String>>
|
||||
|
||||
//获取车辆维修历史
|
||||
@POST("/driverApp/supplier/v2/vehicleMaintenanceList")
|
||||
fun vehicleMaintenanceList(@Body info : FetchVehicleMaintenanceSubmitHistoryRequestBean) : Observable<BaseResponse<List<VehicleMaintenanceHistoryBean>>>
|
||||
}
|
127
servicing/src/main/java/com/za/net/BaseObserver.kt
Normal file
127
servicing/src/main/java/com/za/net/BaseObserver.kt
Normal file
@ -0,0 +1,127 @@
|
||||
package com.za.net
|
||||
|
||||
import android.net.ParseException
|
||||
import com.blankj.utilcode.util.ActivityUtils
|
||||
import com.blankj.utilcode.util.NetworkUtils
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import com.google.gson.JsonParseException
|
||||
import com.za.base.Const
|
||||
import com.za.bean.BaseResponse
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.service.location.ZdLocationManager
|
||||
import io.reactivex.rxjava3.core.Observer
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import org.json.JSONException
|
||||
import java.net.ConnectException
|
||||
import java.net.SocketTimeoutException
|
||||
import java.net.UnknownHostException
|
||||
import java.util.concurrent.TimeoutException
|
||||
import javax.net.ssl.SSLHandshakeException
|
||||
|
||||
/**
|
||||
* Created by DoggieX on 2017/7/26.
|
||||
*/
|
||||
abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// if (!NetworkUtils.isAvailable()) {
|
||||
// doFailure(999, "网络无法使用")
|
||||
// d.dispose()
|
||||
// }
|
||||
}
|
||||
|
||||
override fun onNext(tBaseResponse: BaseResponse<T>) {
|
||||
if (tBaseResponse.isOk) {
|
||||
doSuccess(tBaseResponse.result)
|
||||
} else {
|
||||
when (tBaseResponse.code) {
|
||||
3, 401 -> handlerTokenExpired()
|
||||
// 4 -> RsaRouter.navigate(context, "/page/Standby")
|
||||
}
|
||||
if (null != tBaseResponse.msg) {
|
||||
doFailure(tBaseResponse.code, tBaseResponse.msg)
|
||||
} else if (null != tBaseResponse.message) {
|
||||
doFailure(tBaseResponse.code, tBaseResponse.message)
|
||||
} else {
|
||||
doFailure(tBaseResponse.code, "error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
LogUtil.print("net error", e)
|
||||
when (e) {
|
||||
is JsonParseException -> {
|
||||
doFailure(1, "数据解析错误")
|
||||
}
|
||||
|
||||
is JSONException -> {
|
||||
doFailure(1, "数据解析错误")
|
||||
}
|
||||
|
||||
is ParseException -> {
|
||||
doFailure(1, "数据解析错误")
|
||||
}
|
||||
|
||||
is ConnectException -> {
|
||||
doFailure(Const.NetWorkException, "与服务器断开连接")
|
||||
}
|
||||
|
||||
is UnknownHostException -> {
|
||||
doFailure(Const.NetWorkException, "与服务器断开连接")
|
||||
}
|
||||
|
||||
is SSLHandshakeException -> {
|
||||
doFailure(1, "证书验证失败")
|
||||
LogUtil.print("SSLHandshakeException", e)
|
||||
}
|
||||
|
||||
is TimeoutException -> {
|
||||
doFailure(Const.NetWorkException, "网络连接超时1")
|
||||
LogUtil.print("TimeoutException", e)
|
||||
}
|
||||
|
||||
is SocketTimeoutException -> {
|
||||
doFailure(Const.NetWorkException, "网络连接超时2")
|
||||
LogUtil.print("SocketTimeoutException2", e)
|
||||
}
|
||||
else -> {
|
||||
doFailure(1, "error:" + e.message)
|
||||
LogUtil.print("other error", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
}
|
||||
|
||||
abstract fun doSuccess(it: T?)
|
||||
|
||||
abstract fun doFailure(code: Int, msg: String?)
|
||||
|
||||
private fun handlerTokenExpired() {
|
||||
ToastUtils.showShort("登陆信息已过期,请重新登录")
|
||||
try {
|
||||
GlobalData.clearUserCache()
|
||||
ZdLocationManager.stopContinuousLocation()
|
||||
ActivityUtils.finishAllActivities()
|
||||
// val oldLoginInfo: LoginInfo = DbManager.getInstance(context).queryLoginInfo()
|
||||
// val loginInfo: LoginInfo = LoginInfo()
|
||||
// loginInfo.setSupplierId(oldLoginInfo.getSupplierId())
|
||||
// loginInfo.setSupplierCode(oldLoginInfo.getSupplierCode())
|
||||
// loginInfo.setJobNumber(oldLoginInfo.getJobNumber())
|
||||
// loginInfo.setPassword(oldLoginInfo.getPassword())
|
||||
// DbManager.getInstance(context).deleteLoginInfo()
|
||||
// DbManager.getInstance(context).saveLoginInfo(loginInfo)
|
||||
// SPKit.saveString(context, "userId", "-1")
|
||||
// SPKit.saveString(context, "vehicleId", "-1")
|
||||
//
|
||||
// LBSManager.getInstance().cancel()
|
||||
// DaemonService.stopService()
|
||||
// //跳转
|
||||
// RsaRouter.navigateNoFlag(context, "/page/login")
|
||||
} catch (e: Exception) {
|
||||
LogUtil.print("handlerTokenExpired", e)
|
||||
}
|
||||
}
|
||||
}
|
405
servicing/src/main/java/com/za/net/CommonMethod.kt
Normal file
405
servicing/src/main/java/com/za/net/CommonMethod.kt
Normal file
@ -0,0 +1,405 @@
|
||||
package com.za.net
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.BitmapFactory
|
||||
import com.blankj.utilcode.util.ActivityUtils
|
||||
import com.blankj.utilcode.util.ImageUtils
|
||||
import com.za.base.Const
|
||||
import com.za.bean.DriverInfo
|
||||
import com.za.bean.GeneralInfo
|
||||
import com.za.bean.ImageBean
|
||||
import com.za.bean.JpushBean
|
||||
import com.za.bean.NewOrderRequestBean
|
||||
import com.za.bean.VehicleInfo
|
||||
import com.za.bean.db.ele.EleCarDamagePhotoBean
|
||||
import com.za.bean.db.ele.EleWorkOrderBean
|
||||
import com.za.bean.db.order.OrderInfo
|
||||
import com.za.bean.db.order.PhotoTemplateInfo
|
||||
import com.za.bean.request.ElectronOrderResponse
|
||||
import com.za.bean.request.GeneralInfoRequest
|
||||
import com.za.bean.request.OrderListRequest
|
||||
import com.za.bean.request.PhotoTemplateRequest
|
||||
import com.za.bean.request.QueryEleOrderRequest
|
||||
import com.za.bean.request.UpdateTaskBean
|
||||
import com.za.bean.request.UpdateTaskRequest
|
||||
import com.za.bean.request.UploadGpsRequest
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.common.util.DeviceUtil
|
||||
import com.za.ext.toJson
|
||||
import com.za.offline.OfflineManager
|
||||
import com.za.room.RoomHelper
|
||||
import com.za.ui.new_order.NewOrderActivity
|
||||
import com.za.ui.order_report.ReportFloatingManager
|
||||
import com.za.water_marker.WaterMarkerTemplateManager
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observer
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
|
||||
object CommonMethod {
|
||||
fun uploadGps(uploadGpsRequest : UploadGpsRequest,
|
||||
success : () -> Unit = {},
|
||||
failed : (String?) -> Unit = {}) {
|
||||
RetrofitHelper.getDefaultService().uploadGps(uploadGpsRequest).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread()).subscribe(object : BaseObserver<String>() {
|
||||
override fun doSuccess(it : String?) {
|
||||
success()
|
||||
LogUtil.print("uploadGps", uploadGpsRequest.toString())
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
failed(msg)
|
||||
LogUtil.print("uploadGps", "failed==$msg")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun uploadImage(file : File,
|
||||
preview : () -> Unit = {},
|
||||
success : (String?) -> Unit = {},
|
||||
failed : (String?) -> Unit = {}) {
|
||||
file.writeBytes(ImageUtils.compressByQuality(BitmapFactory.decodeStream(file.inputStream()),
|
||||
Const.Image_Max_length,
|
||||
true))
|
||||
val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull())
|
||||
val body = MultipartBody.Part.createFormData("file", file.name, requestFile)
|
||||
preview()
|
||||
RetrofitHelper.getDefaultService().uploadImage(body).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer<ImageBean> {
|
||||
override fun onSubscribe(d : Disposable) {
|
||||
|
||||
}
|
||||
|
||||
override fun onError(e : Throwable) {
|
||||
failed(e.message)
|
||||
LogUtil.print("uploadImage", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
|
||||
}
|
||||
|
||||
override fun onNext(t : ImageBean) {
|
||||
if (t.isOk()) {
|
||||
success(t.data)
|
||||
return
|
||||
}
|
||||
failed(t.msg)
|
||||
LogUtil.print("uploadImage failed", t.toJson() ?: "")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//更新订单状态
|
||||
fun updateTask(taskRequest : UpdateTaskRequest,
|
||||
success : (UpdateTaskBean?) -> Unit,
|
||||
failed : (String?, Int) -> Unit) {
|
||||
|
||||
RetrofitHelper.getDefaultService().updateTask(taskRequest).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<UpdateTaskBean>() {
|
||||
override fun doSuccess(it : UpdateTaskBean?) {
|
||||
success(it)
|
||||
LogUtil.print("updateTask", "success=$it request=${taskRequest.toJson()}")
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
failed(msg, code)
|
||||
LogUtil.print("updateTask", "doFailure=$msg request=${taskRequest.toJson()}")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getGenerateInfo(vehicleId : Int? = null,
|
||||
userId : Int? = null,
|
||||
success : (GeneralInfo) -> Unit = {},
|
||||
failed : (String?) -> Unit = {}) {
|
||||
val generalInfoRequest =
|
||||
GeneralInfoRequest(vehicleId = vehicleId ?: GlobalData.vehicleInfo?.vehicleId,
|
||||
driverId = userId ?: GlobalData.driverInfo?.userId,
|
||||
deviceId = DeviceUtil.getAndroidId(ActivityUtils.getTopActivity()))
|
||||
RetrofitHelper.getDefaultService().generalInfo(generalInfoRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<GeneralInfo>() {
|
||||
override fun doSuccess(it : GeneralInfo?) {
|
||||
if (it == null) {
|
||||
failed("获取车辆信息失败")
|
||||
return
|
||||
}
|
||||
if (GlobalData.vehicleInfo == null) {
|
||||
GlobalData.vehicleInfo = VehicleInfo(vehicleId = it.vehicleId,
|
||||
vehicleName = it.vehicleName,
|
||||
userName = it.userName,
|
||||
userPhone = it.userPhone,
|
||||
status = it.vehicleState,
|
||||
plateNumber = it.plateNumber,
|
||||
vehicleState = it.vehicleState,
|
||||
supplierType = it.supplierType)
|
||||
} else {
|
||||
GlobalData.vehicleInfo =
|
||||
GlobalData.vehicleInfo?.copy(vehicleId = it.vehicleId,
|
||||
vehicleName = it.vehicleName,
|
||||
userName = it.userName,
|
||||
userPhone = it.userPhone,
|
||||
status = it.vehicleState,
|
||||
plateNumber = it.plateNumber,
|
||||
vehicleState = it.vehicleState,
|
||||
supplierType = it.supplierType)
|
||||
}
|
||||
|
||||
if (GlobalData.driverInfo == null) {
|
||||
GlobalData.driverInfo = DriverInfo(userPortrait = it.userPortrait,
|
||||
userName = it.userName,
|
||||
userId = it.userId,
|
||||
userPhone = it.userPhone,
|
||||
supplierId = it.supplierId.toString(),
|
||||
supplierName = it.supplierName,
|
||||
authStatus = it.authStatus,
|
||||
supplierType = it.supplierType,
|
||||
serviceList = it.serviceList)
|
||||
} else {
|
||||
GlobalData.driverInfo =
|
||||
GlobalData.driverInfo?.copy(userPortrait = it.userPortrait,
|
||||
userName = it.userName,
|
||||
userId = it.userId,
|
||||
userPhone = it.userPhone,
|
||||
supplierId = it.supplierId.toString(),
|
||||
supplierName = it.supplierName,
|
||||
authStatus = it.authStatus,
|
||||
supplierType = it.supplierType,
|
||||
serviceList = it.serviceList)
|
||||
}
|
||||
|
||||
success(it)
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
failed(msg)
|
||||
LogUtil.print("getGenerateInfo",
|
||||
"doFailure=$msg request=${generalInfoRequest.toJson()}")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getNewOrder(context : Context) {
|
||||
val newOrderRequestBean = NewOrderRequestBean(GlobalData.vehicleInfo?.vehicleId)
|
||||
RetrofitHelper.getDefaultService().getNewOrder(newOrderRequestBean)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<JpushBean>() {
|
||||
override fun doSuccess(order : JpushBean?) {
|
||||
NewOrderActivity.goNewOrderActivity(context,
|
||||
jpushBean = JpushBean(taskId = order?.taskId,
|
||||
taskCode = order?.taskCode,
|
||||
customerName = order?.customerName,
|
||||
customerPhone = order?.customerPhone,
|
||||
carBrand = order?.carBrand,
|
||||
carModel = order?.carModel,
|
||||
carNo = order?.carNo,
|
||||
taskState = order?.taskState,
|
||||
address = order?.address,
|
||||
addressProperty = order?.addressProperty,
|
||||
hotline = order?.hotline,
|
||||
expectArriveTime = order?.expectArriveTime,
|
||||
serviceTypeName = order?.serviceTypeName,
|
||||
dispatchTime = order?.dispatchTime,
|
||||
lat = order?.lat,
|
||||
lng = order?.lng,
|
||||
distLat = order?.distLat,
|
||||
distLng = order?.distLng,
|
||||
importantTip = order?.importantTip,
|
||||
hasReplaceBatteryCapable = order?.hasReplaceBatteryCapable,
|
||||
distAddress = order?.distAddress,
|
||||
distAddressRemark = order?.distAddressRemark,
|
||||
addressRemark = order?.addressRemark,
|
||||
voiceType = order?.voiceType))
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun queryOrderList(context : Context? = null,
|
||||
success : (OrderInfo?, List<OrderInfo>?) -> Unit,
|
||||
failed : (String?) -> Unit) {
|
||||
RetrofitHelper.getDefaultService()
|
||||
.queryOrderList(OrderListRequest(vehicleId = GlobalData.vehicleInfo?.vehicleId,
|
||||
deviceId = DeviceUtil.getAndroidId(ActivityUtils.getTopActivity())))
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<List<OrderInfo>?>() {
|
||||
override fun doSuccess(it : List<OrderInfo>?) {
|
||||
LogUtil.print("queryOrderList", "${it.toJson()}")
|
||||
if (it.isNullOrEmpty()) {
|
||||
GlobalData.clearAllOrderCache()
|
||||
ReportFloatingManager.stopService()
|
||||
success(null, null)
|
||||
return
|
||||
}
|
||||
|
||||
val inServicingOrder = it.find { it.isCurrent == true }
|
||||
val waitServiceOrders = it.filter { it.isCurrent == false }
|
||||
|
||||
if (inServicingOrder == null) {
|
||||
context?.let { context ->
|
||||
val order = it[0]
|
||||
NewOrderActivity.goNewOrderActivity(context,
|
||||
jpushBean = JpushBean(taskId = order.taskId,
|
||||
taskCode = order.taskCode,
|
||||
customerName = order.customerName,
|
||||
customerPhone = order.customerPhone,
|
||||
carBrand = order.carBrand,
|
||||
carModel = order.carModel,
|
||||
carNo = order.carNo,
|
||||
taskState = order.taskState,
|
||||
address = order.address,
|
||||
addressProperty = order.addressProperty,
|
||||
hotline = order.hotline,
|
||||
expectArriveTime = order.expectArriveTime,
|
||||
serviceTypeName = order.serviceTypeName,
|
||||
dispatchTime = order.dispatchTime,
|
||||
lat = order.lat,
|
||||
lng = order.lng,
|
||||
distLat = order.distLat,
|
||||
distLng = order.distLng,
|
||||
importantTip = order.importantTip,
|
||||
hasReplaceBatteryCapable = order.hasReplaceBatteryCapable,
|
||||
distAddress = order.distAddress,
|
||||
distAddressRemark = order.distAddressRemark,
|
||||
addressRemark = order.addressRemark))
|
||||
}
|
||||
success(null, waitServiceOrders)
|
||||
return
|
||||
}
|
||||
|
||||
GlobalData.currentOrder = inServicingOrder
|
||||
it.forEach {
|
||||
if (RoomHelper.db?.orderDao()
|
||||
?.getOrderInfoFromTaskId(it.taskId ?: 0) != null
|
||||
) {
|
||||
RoomHelper.db?.orderDao()?.update(orderInfo = it)
|
||||
} else {
|
||||
RoomHelper.db?.orderDao()?.insertOrder(orderInfo = it)
|
||||
}
|
||||
}
|
||||
fetchPhotoTemplate(inServicingOrder)
|
||||
queryElectronOrder(orderInfo = inServicingOrder)
|
||||
WaterMarkerTemplateManager.fetchWaterTemplate(inServicingOrder.taskCode ?: "",
|
||||
taskId = inServicingOrder.taskId ?: 0,
|
||||
inServicingOrder.userOrderId ?: 0)
|
||||
OfflineManager.start(GlobalData.currentOrder?.taskId)
|
||||
|
||||
context?.let {
|
||||
ReportFloatingManager.startService(it)
|
||||
}
|
||||
|
||||
success(inServicingOrder, waitServiceOrders)
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
val list = RoomHelper.db?.orderDao()?.getAllOrder()
|
||||
val inServicingOrder = list?.find { it.isCurrent == true }
|
||||
val waitServiceOrders = list?.filter { it.isCurrent == false }
|
||||
GlobalData.currentOrder = inServicingOrder
|
||||
success(inServicingOrder, waitServiceOrders)
|
||||
OfflineManager.start(GlobalData.currentOrder?.taskId)
|
||||
LogUtil.print("queryOrderList", "queryOrderList failed 是用离线数据==$msg")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun fetchPhotoTemplate(orderInfo : OrderInfo?,
|
||||
success : () -> Unit = {},
|
||||
failed : (String?) -> Unit = {}) {
|
||||
val photoTemplateRequest = PhotoTemplateRequest(orderInfo?.userOrderId)
|
||||
RetrofitHelper.getDefaultService().fetchPhotoTemplate(photoTemplateRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<List<PhotoTemplateInfo>>() {
|
||||
override fun doSuccess(it : List<PhotoTemplateInfo>?) {
|
||||
if (it.isNullOrEmpty()) {
|
||||
LogUtil.print("fetchPhotoTemplate",
|
||||
"模板为null ${photoTemplateRequest.toJson()}")
|
||||
failed("未找到模版")
|
||||
return
|
||||
}
|
||||
|
||||
val listData = RoomHelper.db?.photoTemplateDao()
|
||||
?.queryCurrentOrderHasTaskNode(orderInfo?.userOrderId ?: 0)
|
||||
if (! listData.isNullOrEmpty()) {
|
||||
LogUtil.print("queryPhotoTemplate", "模板已经存在==${listData.toJson()}")
|
||||
success()
|
||||
return
|
||||
}
|
||||
|
||||
it.forEachIndexed { _, item ->
|
||||
val photoTemplateInfo = item.copy(userOrderId = orderInfo?.userOrderId,
|
||||
taskCode = orderInfo?.taskCode,
|
||||
myCustomPhotoType = Const.PhotoType.InServicing,
|
||||
advanceTime = orderInfo?.advanceTime,
|
||||
taskId = orderInfo?.taskId,
|
||||
needWaterMarker = orderInfo?.needWaterMarker,
|
||||
needShowPhoneBrand = orderInfo?.needShowPhoneBrand,
|
||||
photoSource = 0)
|
||||
RoomHelper.db?.photoTemplateDao()?.insert(photoTemplateInfo)
|
||||
}
|
||||
LogUtil.print("fetchPhotoTemplate", " ${it.toJson()}")
|
||||
success()
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
LogUtil.print("fetchPhotoTemplate", "err==$msg")
|
||||
failed(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun queryElectronOrder(orderInfo : OrderInfo?,
|
||||
success : (EleWorkOrderBean) -> Unit = {},
|
||||
failed : (String?) -> Unit = {}) {
|
||||
val ele = RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(taskId = orderInfo?.taskId ?: 0)
|
||||
if (ele != null) {
|
||||
success(ele)
|
||||
return
|
||||
}
|
||||
val queryEleOrderRequest = QueryEleOrderRequest(taskOrderId = orderInfo?.taskId,
|
||||
userOrderId = orderInfo?.userOrderId)
|
||||
RetrofitHelper.getDefaultService().queryElectronOrder(queryEleOrderRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<ElectronOrderResponse>() {
|
||||
override fun doSuccess(it : ElectronOrderResponse?) {
|
||||
val temp = EleWorkOrderBean(orderId = orderInfo?.taskId ?: 0,
|
||||
userOrderId = it?.userOrderId,
|
||||
date = it?.createTime,
|
||||
orderWorkStatus = it?.state,
|
||||
hasBad = it?.hasDamage == 1,
|
||||
serverCustomSignPath = it?.customerSignPath,
|
||||
carNO = it?.plateNumberBean?.value,
|
||||
carVin = it?.vinNo,
|
||||
serviceContent = it?.serviceTerm,
|
||||
serverAcceptCarSignPath = it?.recipientSignPath,
|
||||
serverServicePeopleSignPath = it?.waitstaffSignPath,
|
||||
orderType = it?.serviceName)
|
||||
it?.damageFileList?.forEach { item ->
|
||||
val damagePhotoBean = EleCarDamagePhotoBean(userOrderId = it.userOrderId,
|
||||
orderId = orderInfo?.taskId,
|
||||
isPhoto = true,
|
||||
uploadStatus = 1,
|
||||
serverPath = item)
|
||||
RoomHelper.db?.eleCarDamagePhotoDao()?.insert(damagePhotoBean)
|
||||
}
|
||||
RoomHelper.db?.eleWorkOrderDao()?.insertEleWorkOrder(temp)
|
||||
success(temp)
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
failed(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
347
servicing/src/main/java/com/za/net/RSAUtils.java
Normal file
347
servicing/src/main/java/com/za/net/RSAUtils.java
Normal file
@ -0,0 +1,347 @@
|
||||
package com.za.net;
|
||||
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class RSAUtils {
|
||||
|
||||
/**
|
||||
* 加密算法RSA
|
||||
*/
|
||||
private static final String KEY_ALGORITHM = "RSA";
|
||||
|
||||
/**
|
||||
* 签名算法
|
||||
*/
|
||||
// public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
|
||||
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
|
||||
|
||||
/**
|
||||
* 获取公钥的key
|
||||
*/
|
||||
private static final String PUBLIC_KEY = "RSAPublicKey";
|
||||
|
||||
|
||||
/**
|
||||
* 获取私钥的key
|
||||
*/
|
||||
private static final String PRIVATE_KEY = "RSAPrivateKey";
|
||||
|
||||
|
||||
/**
|
||||
* RSA最大加密明文大小
|
||||
*/
|
||||
private static final int MAX_ENCRYPT_BLOCK = 117;
|
||||
|
||||
|
||||
/**
|
||||
* RSA最大解密密文大小
|
||||
*/
|
||||
private static final int MAX_DECRYPT_BLOCK = 256;
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
// try {
|
||||
// genKeyPair(2048);
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
/*RSA 2048*/
|
||||
// String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAichGTEP0QFswnvn+ZAQrgGHM8VeDZLJuezGhgxh4d9SyRUfnIW/zefT71rwS4bZUs1MPxJwavOyxABJOHLuckdHXknCsGEWz78gsA6D0+O+9dl1gCZR29nnN/NlzmNbSjFnzvsTJYBlS88qSr35RXFE+6DM7uPsS8Fm2I+65FteJ8p2yMvpSg72QkIX8xvI1F1uwXrciIB+4u7uTozxIplMOo4a6uhAm3W+Kjpz3ni2btjGqHRbqb3ebSZyl+nFfnjQaBe3XyVxAWDSanjgFj/wbqbeug9FBs+nQFVPIZR9z0aE5Ndi5o3eSkV7HFmWpkxaiPZ0BLRK3XHMaBtuSpwIDAQAB";
|
||||
// String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCJyEZMQ/RAWzCe+f5kBCuAYczxV4Nksm57MaGDGHh31LJFR+chb/N59PvWvBLhtlSzUw/EnBq87LEAEk4cu5yR0deScKwYRbPvyCwDoPT47712XWAJlHb2ec382XOY1tKMWfO+xMlgGVLzypKvflFcUT7oMzu4+xLwWbYj7rkW14nynbIy+lKDvZCQhfzG8jUXW7BetyIgH7i7u5OjPEimUw6jhrq6ECbdb4qOnPeeLZu2MaodFupvd5tJnKX6cV+eNBoF7dfJXEBYNJqeOAWP/Bupt66D0UGz6dAVU8hlH3PRoTk12Lmjd5KRXscWZamTFqI9nQEtErdccxoG25KnAgMBAAECggEBAIPz1b88ZTMtIgdejA7lH3Q4Nbn8gc1yRPSet3uBd/3rKT/IeMZBHQBzaqxgOgUIRV3n8nXsun6sf2b+IOjLlErimH2agnZMauL85YokH/g4QU6WZl9GXBf41xmMd3SsZ8AadaEBfYoXNqZcHtcLNogfFwvx5QRnD+A3SoRnH8OLBeVvOEe4AqHLT2xEZ9TeCf3fJe0Rf0fUIbw7I5ioiRZV/ir0L1VM7+1k2JODUkdC2Luj5Tl3nl1Eg6EmkYCmGE1bip1NAatsfjPBLMF7XdPNjLboiffjgKVBOjb7Y9vL18BCoLtWeTT2GkMpi5Sr94T1te1Ox77dF4BP33Xn7eECgYEA1TNUrAQsh14NbbkwFtUHXS8/YXt81p9wbSpFBymIawF2Lkk0913TB4CHSun45LhYXjdZZxK/TgqC5EIq5v2RA0jY3cSxoqVe6RZKB04E8wszeJHiEJPdu2vFnpZh9iAyhswiM5FmuKZKoWsVc2SZrBXAI02smSn3lXYok1VBS3sCgYEApXEZS6gjUu4o7ZL53Ur1HDfi/nxpkxqrPh+D1HVYjzjT+4vTeZwtLXt2VCInPWNXH+f11mzhxIrLkI0jMcSCah81DuU8aFXnqvPuyFvt9uaQBYlVWBtkcGZyeaxHFrbfCyeu0jm7SfwmiIg12hKlIHtPTjEZQUX+kkWr8cdaZ8UCgYEAh0Pl+K09QzVc97yC0jmeTnTnlYWvksvdnKUw3nZvYtSukndH75nLhfr524HOs+5xwnUDd+3hCjaJDSEd7yf5lUfmr+1XdoXNTb0igrfxU/JLWbfU4geuqnaaDyACTxHmfLePC4C413ZJ61fxaCDvjsrN+JgTZanGt0EcRT3WC3kCgYEAgf5/GMJxlw0JXbs515a5R8Xl9358Whj/at3KcRsPTeIiNqnkrc54dR9ol60KViMDZ0+VDDobn5pLXzZ26/jzXD1PLHgU4gp18Q6glhAdx/3cNm11gLhtUCA/XLlwVjm0wggZRpgUQIr/IBKe9c3mr8IUS2Uq6e38nKRf+adhst0CgYAM4tvl+U1MPbbz3YzDv8QPepZ7Pglgdfxqfr5OkXA7jNhqTZjSq10B6oClGvirBo1m6f26F02iUKk1n67AuiLlTP/RRZHi1cfq6P9IaXl23PcxJfUMvIxQDS0U+UTFpNXryTw/qNAkSfufN48YzKdGvc8vHrYJyaeemaVlbdJOCw==";
|
||||
//
|
||||
//
|
||||
// try {
|
||||
//
|
||||
// String data = "测试提交数据";
|
||||
//
|
||||
// byte[] publicEncryptBytes = RSAUtils.encryptByPublicKey(data.getBytes(), publicKey);
|
||||
// System.out.println("公钥加密后的数据:" + Base64.encodeToString(publicEncryptBytes, Base64.URL_SAFE | Base64.NO_WRAP));
|
||||
// byte[] privatDecryptBytes = RSAUtils.decryptByPrivateKey(publicEncryptBytes, privateKey);
|
||||
// System.out.println("私钥解密后的数据:" + new String(privatDecryptBytes));
|
||||
//
|
||||
//
|
||||
// System.out.println("--------------------");
|
||||
//
|
||||
// byte[] privateKeyEncryptBytes = RSAUtils.encryptByPrivateKey(data.getBytes(), privateKey);
|
||||
// System.out.println("私钥加密后的数据:" + Base64.encodeToString(privateKeyEncryptBytes, Base64.URL_SAFE | Base64.NO_WRAP));
|
||||
//
|
||||
// String singnData = RSAUtils.sign(data.getBytes(), privateKey);
|
||||
// System.out.println("私钥签名后的数据:" + singnData);
|
||||
//
|
||||
//
|
||||
// byte[] publicDecryptBytes = RSAUtils.decryptByPublicKey(privateKeyEncryptBytes, publicKey);
|
||||
// System.out.println("公钥解密后的数据:" + new String(publicDecryptBytes));
|
||||
//
|
||||
// boolean isSign = RSAUtils.verify(data.getBytes(), publicKey, singnData);
|
||||
// System.out.println("签名是否正确:" + isSign);
|
||||
//
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param keySize 生成的秘钥长度 一般为1024或2048
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Map<String, Object> genKeyPair(int keySize) throws Exception {
|
||||
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
|
||||
keyPairGen.initialize(keySize);
|
||||
KeyPair keyPair = keyPairGen.generateKeyPair();
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
Map<String, Object> keyMap = new HashMap<String, Object>(2);
|
||||
keyMap.put(PUBLIC_KEY, publicKey);
|
||||
keyMap.put(PRIVATE_KEY, privateKey);
|
||||
|
||||
// Log.d("DoggieX","publicKey:" + Base64.encodeToString(publicKey.getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP));
|
||||
// Log.d("DoggieX","privateKey:" + Base64.encodeToString(privateKey.getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP));
|
||||
Log.d("DoggieX","publicKey:" + Base64.encodeToString(publicKey.getEncoded(), Base64.NO_WRAP));
|
||||
Log.d("DoggieX","privateKey:" + Base64.encodeToString(privateKey.getEncoded(), Base64.NO_WRAP));
|
||||
|
||||
return keyMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对已加密数据进行签名
|
||||
*
|
||||
* @param data 已加密的数据
|
||||
* @param privateKey 私钥
|
||||
* @return 对已加密数据生成的签名
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
public static String sign(byte[] data, String privateKey) throws Exception {
|
||||
byte[] keyBytes = Base64.decode(privateKey, Base64.URL_SAFE | Base64.NO_WRAP);
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
|
||||
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
|
||||
signature.initSign(privateK);
|
||||
signature.update(data);
|
||||
return Base64.encodeToString(signature.sign(), Base64.URL_SAFE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验签
|
||||
*
|
||||
* @param data 签名之前的数据
|
||||
* @param publicKey 公钥
|
||||
* @param sign 签名之后的数据
|
||||
* @return 验签是否成功
|
||||
* @throws Exception
|
||||
*/
|
||||
public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
|
||||
byte[] keyBytes = Base64.decode(publicKey, Base64.URL_SAFE | Base64.NO_WRAP);
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
PublicKey publicK = keyFactory.generatePublic(keySpec);
|
||||
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
|
||||
signature.initVerify(publicK);
|
||||
signature.update(data);
|
||||
return signature.verify(Base64.decode(sign, Base64.URL_SAFE));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 用私钥对数据进行解密
|
||||
*
|
||||
* @param encryptedData 使用公钥加密过的数据
|
||||
* @param privateKey 私钥
|
||||
* @return 解密后的数据
|
||||
* @throws Exception
|
||||
*/
|
||||
public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
|
||||
byte[] keyBytes = Base64.decode(privateKey, Base64.NO_WRAP);
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
|
||||
//Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding","BC");
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateK);
|
||||
Log.d("DoggieX","provider:"+cipher.getProvider().getClass().getName());
|
||||
|
||||
int inputLen = encryptedData.length;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int offSet = 0;
|
||||
byte[] cache;
|
||||
int i = 0;
|
||||
// 对数据分段解密
|
||||
while (inputLen - offSet > 0) {
|
||||
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
|
||||
} else {
|
||||
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
|
||||
}
|
||||
out.write(cache, 0, cache.length);
|
||||
i++;
|
||||
offSet = i * MAX_DECRYPT_BLOCK;
|
||||
}
|
||||
byte[] decryptedData = out.toByteArray();
|
||||
out.close();
|
||||
|
||||
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥解密
|
||||
*
|
||||
* @param encryptedData 使用私钥加密过的数据
|
||||
* @param publicKey 公钥
|
||||
* @return 解密后的数据
|
||||
* @throws Exception
|
||||
*/
|
||||
public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
|
||||
byte[] keyBytes = Base64.decode(publicKey, Base64.URL_SAFE | Base64.NO_WRAP);
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
Key publicK = keyFactory.generatePublic(x509KeySpec);
|
||||
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
|
||||
cipher.init(Cipher.DECRYPT_MODE, publicK);
|
||||
int inputLen = encryptedData.length;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int offSet = 0;
|
||||
byte[] cache;
|
||||
int i = 0;
|
||||
// 对数据分段解密
|
||||
while (inputLen - offSet > 0) {
|
||||
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
|
||||
} else {
|
||||
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
|
||||
}
|
||||
out.write(cache, 0, cache.length);
|
||||
i++;
|
||||
offSet = i * MAX_DECRYPT_BLOCK;
|
||||
}
|
||||
byte[] decryptedData = out.toByteArray();
|
||||
out.close();
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param data 需要加密的数据
|
||||
* @param publicKey 公钥
|
||||
* @return 使用公钥加密后的数据
|
||||
* @throws Exception
|
||||
*/
|
||||
public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
|
||||
byte[] keyBytes = Base64.decode(publicKey, Base64.NO_WRAP);
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
Key publicK = keyFactory.generatePublic(x509KeySpec);
|
||||
// 对数据加密
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding","BC");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicK);
|
||||
Log.d("DoggieX",cipher.getProvider().getClass().toString());
|
||||
int inputLen = data.length;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int offSet = 0;
|
||||
byte[] cache;
|
||||
int i = 0;
|
||||
// 对数据分段加密
|
||||
while (inputLen - offSet > 0) {
|
||||
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
|
||||
} else {
|
||||
cache = cipher.doFinal(data, offSet, inputLen - offSet);
|
||||
}
|
||||
out.write(cache, 0, cache.length);
|
||||
i++;
|
||||
offSet = i * MAX_ENCRYPT_BLOCK;
|
||||
}
|
||||
byte[] encryptedData = out.toByteArray();
|
||||
out.close();
|
||||
return encryptedData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 私钥加密
|
||||
*
|
||||
* @param data 待加密的数据
|
||||
* @param privateKey 私钥
|
||||
* @return 使用私钥加密后的数据
|
||||
* @throws Exception
|
||||
*/
|
||||
public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
|
||||
byte[] keyBytes = Base64.decode(privateKey, Base64.URL_SAFE | Base64.NO_WRAP);
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
|
||||
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
|
||||
cipher.init(Cipher.ENCRYPT_MODE, privateK);
|
||||
int inputLen = data.length;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int offSet = 0;
|
||||
byte[] cache;
|
||||
int i = 0;
|
||||
// 对数据分段加密
|
||||
while (inputLen - offSet > 0) {
|
||||
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
|
||||
} else {
|
||||
cache = cipher.doFinal(data, offSet, inputLen - offSet);
|
||||
}
|
||||
out.write(cache, 0, cache.length);
|
||||
i++;
|
||||
offSet = i * MAX_ENCRYPT_BLOCK;
|
||||
}
|
||||
byte[] encryptedData = out.toByteArray();
|
||||
out.close();
|
||||
return encryptedData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取私钥
|
||||
*
|
||||
* @param keyMap 生成的秘钥对
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
|
||||
Key key = (Key) keyMap.get(PRIVATE_KEY);
|
||||
return Base64.encodeToString(key.getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取公钥
|
||||
*
|
||||
* @param keyMap 生成的秘钥对
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
|
||||
Key key = (Key) keyMap.get(PUBLIC_KEY);
|
||||
return Base64.encodeToString(key.getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP);
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package com.za.net;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.za.common.GlobalData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okio.Buffer;
|
||||
|
||||
/**
|
||||
* @author DoggieX
|
||||
* @create 2021/8/23 15:00
|
||||
* @mail coldpuppy@163.com
|
||||
*/
|
||||
public class RequestEncryptInterceptor implements Interceptor {
|
||||
|
||||
static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7UM6zdWdBuO0DZZVkxVfJioawUe6qH1p5Uz/qR9zbawl2oWyxxcBfxQyPS+HxOej/ZnyS4bu7qhh99alDqkJzk6g9oGZWs+jEF5GRWt9nChlfUsjvHQwuF2TSQMTdPtDPCByF/QgMFCAfbCqTrNmOETrZ/2GFy1Re0BTlhh6X/XzpzqtK+enikEMlQ5fIM5ljdXgyCnvDou9ptWqzw8Zmsat6LeA0UKz+bgpJAbw6KfK+8lPMqUpNFfkmJuEd5+JQOG9McH7j9pBagohkC6k3Cn92dAf9iD6NSDKSNgt1vxXhaNnfAbYJ5pqeSGy6QMSVO0TXYj4asln5OutD/284QIDAQAB";
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Response intercept(@NonNull Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
String method = request.method().toLowerCase().trim();
|
||||
HttpUrl url = request.url();
|
||||
boolean skipEncrypt = url.encodedPath().endsWith("appVersion")
|
||||
|| url.encodedPath().endsWith("getVerifyCode")
|
||||
|| url.encodedPath().endsWith("listVehicleNew")
|
||||
|| url.encodedPath().endsWith("getExitMileage")
|
||||
|| url.encodedPath().endsWith("uploadImage")
|
||||
|| url.encodedPath().endsWith("upload")
|
||||
|| url.encodedPath().endsWith("matchAppUploadTime");
|
||||
|
||||
if (method.equals("post") && !skipEncrypt) {
|
||||
RequestBody body = request.body();
|
||||
if (null != body) {
|
||||
Buffer buffer = new Buffer();
|
||||
body.writeTo(buffer);
|
||||
String data = URLDecoder.decode(buffer.readString(StandardCharsets.UTF_8), "utf-8");
|
||||
JSONObject jsonObject = JSON.parseObject(data);
|
||||
if (null == jsonObject) {
|
||||
jsonObject = new JSONObject();
|
||||
}
|
||||
jsonObject.put("requestTime", System.currentTimeMillis());
|
||||
data = jsonObject.toJSONString();
|
||||
|
||||
String aesKey;
|
||||
try {
|
||||
if (url.encodedPath().endsWith("login")
|
||||
|| url.encodedPath().endsWith("fastLogin")) {
|
||||
// 登录接口生成新key
|
||||
aesKey = AESUtils.getRandomKey(16);
|
||||
GlobalData.INSTANCE.setAesKey(aesKey);
|
||||
} else {
|
||||
|
||||
aesKey = GlobalData.INSTANCE.getAesKey();
|
||||
if (null == aesKey && url.encodedPath().endsWith("loginWithTask")) {
|
||||
aesKey = AESUtils.getRandomKey(16);
|
||||
GlobalData.INSTANCE.setAesKey(aesKey);
|
||||
} else if (null == aesKey) {
|
||||
throw new IOException("缺失加密密钥");
|
||||
} else {
|
||||
aesKey = GlobalData.INSTANCE.getAesKey();
|
||||
}
|
||||
}
|
||||
if (!url.encodedPath().endsWith("login")
|
||||
&& !url.encodedPath().endsWith("fastLogin")
|
||||
&& !url.encodedPath().endsWith("loginWithTask")) {
|
||||
|
||||
data = AESUtils.encrypt(data, aesKey);
|
||||
}
|
||||
RequestBody requestBody = RequestBody.create(body.contentType(), data);
|
||||
Request.Builder requestBuilder = request.newBuilder().post(requestBody);
|
||||
|
||||
if (url.encodedPath().endsWith("login")
|
||||
|| url.encodedPath().endsWith("fastLogin")
|
||||
|| url.encodedPath().endsWith("loginWithTask")) {
|
||||
|
||||
byte[] bytes = RSAUtils.encryptByPublicKey(aesKey.getBytes(StandardCharsets.UTF_8), PUBLIC_KEY);
|
||||
String tokenSecret = Base64.encodeToString(bytes, Base64.NO_WRAP);
|
||||
requestBuilder.addHeader("secret", tokenSecret);
|
||||
} else {
|
||||
String token = GlobalData.INSTANCE.getToken();
|
||||
if (null == token) {
|
||||
throw new IOException("缺失token");
|
||||
} else {
|
||||
requestBuilder.addHeader("token", token);
|
||||
}
|
||||
}
|
||||
request = requestBuilder.build();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
83
servicing/src/main/java/com/za/net/RetrofitHelper.kt
Normal file
83
servicing/src/main/java/com/za/net/RetrofitHelper.kt
Normal file
@ -0,0 +1,83 @@
|
||||
package com.za.net
|
||||
|
||||
import android.util.Log
|
||||
import com.za.base.AppConfig
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLDecoder
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object RetrofitHelper {
|
||||
private var retrofit: Retrofit? = null
|
||||
private var apiService: ApiService? = null
|
||||
private val loggerInterceptor = HttpLoggingInterceptor {
|
||||
try {
|
||||
|
||||
if (it.contains("image/*") || it.contains("form-data; name=\"file\"")
|
||||
|| it.startsWith("Content-Type")
|
||||
|| it.startsWith("Content-Length")
|
||||
|| it.startsWith("Server")
|
||||
|| it.startsWith("Date")
|
||||
|| it.startsWith("Transfer-Encoding")
|
||||
|| it.startsWith("Connection")
|
||||
|| it.startsWith("X-")
|
||||
|| it.startsWith("token")
|
||||
|| it.startsWith("Cache-Control")
|
||||
|| it.startsWith("Expires")
|
||||
|| it.startsWith("Accept")
|
||||
|| it.startsWith("Vary")
|
||||
|| it.isEmpty()
|
||||
|| it.startsWith("Pragma")
|
||||
) {
|
||||
return@HttpLoggingInterceptor
|
||||
}
|
||||
|
||||
if (it.contains("name=\"file\"; filename")) {
|
||||
return@HttpLoggingInterceptor
|
||||
}
|
||||
Log.e(
|
||||
"--network--",
|
||||
URLDecoder.decode(it.replace(Regex("%(?![0-9a-fA-F]{2})"), ""), "utf-8")
|
||||
)
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}.setLevel(HttpLoggingInterceptor.Level.BODY)
|
||||
|
||||
fun getDefaultService(): ApiService {
|
||||
return if (apiService == null) {
|
||||
apiService = getDefaultRetrofit().create(ApiService::class.java)
|
||||
apiService!!
|
||||
} else {
|
||||
apiService!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultRetrofit(): Retrofit {
|
||||
return if (retrofit == null) {
|
||||
retrofit = Retrofit.Builder().baseUrl(AppConfig.BASE_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
|
||||
.client(getOkHttpClient())
|
||||
.build()
|
||||
retrofit!!
|
||||
} else {
|
||||
retrofit!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOkHttpClient(): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(60, TimeUnit.SECONDS)
|
||||
.readTimeout(60, TimeUnit.SECONDS)
|
||||
.addInterceptor(RequestEncryptInterceptor())
|
||||
.addInterceptor(loggerInterceptor)
|
||||
.build()
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.za.offline
|
||||
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||
import androidx.lifecycle.setViewTreeViewModelStoreOwner
|
||||
import androidx.savedstate.SavedStateRegistry
|
||||
import androidx.savedstate.SavedStateRegistryController
|
||||
import androidx.savedstate.SavedStateRegistryOwner
|
||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||
|
||||
class MyOfflineServiceViewLifecycleOwner : LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
|
||||
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
|
||||
private val savedStateRegistryController = SavedStateRegistryController.create(this)
|
||||
private val store = ViewModelStore()
|
||||
|
||||
override val lifecycle: Lifecycle
|
||||
get() = lifecycleRegistry
|
||||
override val savedStateRegistry: SavedStateRegistry
|
||||
get() = savedStateRegistryController.savedStateRegistry
|
||||
override val viewModelStore: ViewModelStore
|
||||
get() = store
|
||||
|
||||
|
||||
fun onCreate() {
|
||||
savedStateRegistryController.performRestore(null)
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
|
||||
}
|
||||
|
||||
fun onStart() {
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
|
||||
}
|
||||
|
||||
fun onResume() {
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
}
|
||||
|
||||
fun onPause() {
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
||||
}
|
||||
|
||||
fun onStop() {
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||
}
|
||||
|
||||
fun onDestroy() {
|
||||
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
||||
store.clear()
|
||||
}
|
||||
|
||||
fun attachToDecorView(decorView: View?) {
|
||||
decorView?.let {
|
||||
it.setViewTreeViewModelStoreOwner(this)
|
||||
it.setViewTreeLifecycleOwner(this)
|
||||
it.setViewTreeSavedStateRegistryOwner(this)
|
||||
} ?: return
|
||||
}
|
||||
}
|
40
servicing/src/main/java/com/za/offline/OfflineDao.kt
Normal file
40
servicing/src/main/java/com/za/offline/OfflineDao.kt
Normal file
@ -0,0 +1,40 @@
|
||||
package com.za.offline
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
|
||||
@Dao
|
||||
interface OfflineDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOfflineTask(offlineUpdateTaskBean: OfflineUpdateTaskBean)
|
||||
|
||||
@Query("select * from offline_update_task_bean where taskId =:taskId")
|
||||
fun getOfflineTaskFromTaskId(taskId: Int): List<OfflineUpdateTaskBean>?
|
||||
|
||||
@Query("select * from offline_update_task_bean where primaryId =:primaryId")
|
||||
fun getOfflineTaskFromPrimaryId(primaryId: Int): OfflineUpdateTaskBean?
|
||||
|
||||
|
||||
@Query("select * from offline_update_task_bean where taskId =:taskId and offlineType=1")
|
||||
fun getRecentOfflineTask(taskId: Int): List<OfflineUpdateTaskBean?>?
|
||||
|
||||
@Query("select * from offline_update_task_bean where taskId =:taskId and offlineType=3")
|
||||
fun getRecentOfflineEle(taskId: Int): List<OfflineUpdateTaskBean?>?
|
||||
|
||||
@Query("delete from offline_update_task_bean where primaryId =:primaryId")
|
||||
fun deleteOfflineTaskFromId(primaryId: Int)
|
||||
|
||||
@Query("delete from offline_update_task_bean where taskId =:taskId")
|
||||
fun deleteOfflineTaskFromTaskId(taskId: Int)
|
||||
|
||||
@Query("delete from offline_update_task_bean")
|
||||
fun deleteAllOfflineTask()
|
||||
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun update(offlineUpdateTaskBean: OfflineUpdateTaskBean)
|
||||
|
||||
}
|
514
servicing/src/main/java/com/za/offline/OfflineManager.kt
Normal file
514
servicing/src/main/java/com/za/offline/OfflineManager.kt
Normal file
@ -0,0 +1,514 @@
|
||||
package com.za.offline
|
||||
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.net.toUri
|
||||
import com.alibaba.fastjson.JSONObject
|
||||
import com.amap.api.services.core.LatLonPoint
|
||||
import com.amap.api.services.geocoder.GeocodeResult
|
||||
import com.amap.api.services.geocoder.GeocodeSearch
|
||||
import com.amap.api.services.geocoder.GeocodeSearch.OnGeocodeSearchListener
|
||||
import com.amap.api.services.geocoder.RegeocodeQuery
|
||||
import com.amap.api.services.geocoder.RegeocodeResult
|
||||
import com.blankj.utilcode.util.ActivityUtils
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import com.za.bean.request.SaveEleOrderRequest
|
||||
import com.za.bean.request.TaskFinishRequest
|
||||
import com.za.bean.request.TaskFinishResponse
|
||||
import com.za.bean.request.UpdateTaskBean
|
||||
import com.za.bean.request.UpdateTaskRequest
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.ext.toJson
|
||||
import com.za.net.BaseObserver
|
||||
import com.za.net.CommonMethod
|
||||
import com.za.net.RetrofitHelper
|
||||
import com.za.room.RoomHelper
|
||||
import com.za.water_marker.PhotoMarkerManager
|
||||
import com.za.water_marker.bean.PhotoMarkerInfo
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import java.io.File
|
||||
|
||||
interface IOfflineListener {
|
||||
fun start()
|
||||
fun stop()
|
||||
fun uploadSuccess()
|
||||
fun uploadFailure(msg : String?)
|
||||
fun currentTask(offlineUpdateTaskBean : OfflineUpdateTaskBean)
|
||||
}
|
||||
|
||||
object OfflineManager {
|
||||
private var isUploading = false
|
||||
private var uploadDispose : Disposable? = null
|
||||
private var offlineListener : IOfflineListener? = null
|
||||
private var currentTaskId : Int? = null
|
||||
|
||||
fun addOfflineListener(offlineListener : IOfflineListener) {
|
||||
this.offlineListener = offlineListener
|
||||
}
|
||||
|
||||
|
||||
fun start(taskId : Int?) {
|
||||
if (checkOfflineIsComplete(taskId)) {
|
||||
return
|
||||
}
|
||||
if (currentTaskId != null && taskId == currentTaskId && isUploading) {
|
||||
return
|
||||
}
|
||||
|
||||
if (! Settings.canDrawOverlays(GlobalData.application)) {
|
||||
openSystemAlertPermissionDialog()
|
||||
return
|
||||
}
|
||||
|
||||
stop()
|
||||
OfflineService.addListener()
|
||||
offlineListener?.start()
|
||||
uploadDispose = Observable.interval(0, 5, java.util.concurrent.TimeUnit.SECONDS)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe {
|
||||
if (! isUploading) {
|
||||
val list =
|
||||
RoomHelper.db?.offlineTaskDao()?.getOfflineTaskFromTaskId(taskId ?: 0)
|
||||
if (list.isNullOrEmpty()) {
|
||||
LogUtil.print("离线任务", "离线任务为空!!")
|
||||
stop()
|
||||
ToastUtils.showLong("离线任务上传完成")
|
||||
return@subscribe
|
||||
}
|
||||
doUpload(list[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stop() {
|
||||
uploadDispose?.dispose()
|
||||
uploadDispose = null
|
||||
isUploading = false
|
||||
currentTaskId = null
|
||||
offlineListener?.stop()
|
||||
offlineListener = null
|
||||
}
|
||||
|
||||
|
||||
private fun checkOfflineIsComplete(taskId : Int?) : Boolean {
|
||||
val list = RoomHelper.db?.offlineTaskDao()?.getOfflineTaskFromTaskId(taskId ?: 0)
|
||||
LogUtil.print("离线任务列表", "${list.toJson()}")
|
||||
return list.isNullOrEmpty()
|
||||
}
|
||||
|
||||
private fun doUpload(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
offlineListener?.currentTask(offlineUpdateTaskBean)
|
||||
currentTaskId = offlineUpdateTaskBean.taskId
|
||||
when (offlineUpdateTaskBean.offlineType) {
|
||||
1 -> uploadTask(offlineUpdateTaskBean)
|
||||
2 -> uploadOrderImage(offlineUpdateTaskBean)
|
||||
3 -> uploadEleWorkOrder(offlineUpdateTaskBean)
|
||||
4 -> uploadEleDamageImage(offlineUpdateTaskBean)
|
||||
5 -> uploadCustomerSignImage(offlineUpdateTaskBean)
|
||||
6 -> uploadAcceptPeopleSignImage(offlineUpdateTaskBean)
|
||||
7 -> uploadServicePeopleSignImage(offlineUpdateTaskBean)
|
||||
8 -> taskFinish(offlineUpdateTaskBean)
|
||||
}
|
||||
}
|
||||
|
||||
private fun uploadTask(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
isUploading = true
|
||||
val updateTaskRequest = UpdateTaskRequest(type = offlineUpdateTaskBean.type,
|
||||
taskId = offlineUpdateTaskBean.taskId,
|
||||
userId = offlineUpdateTaskBean.userId,
|
||||
vehicleId = offlineUpdateTaskBean.vehicleId,
|
||||
currentState = offlineUpdateTaskBean.currentState,
|
||||
operateTime = offlineUpdateTaskBean.operateTime,
|
||||
lat = offlineUpdateTaskBean.updateTaskLat,
|
||||
lng = offlineUpdateTaskBean.updateTaskLng,
|
||||
address = offlineUpdateTaskBean.updateTaskAddress,
|
||||
offlineMode = offlineUpdateTaskBean.offlineMode,
|
||||
flowType = offlineUpdateTaskBean.flowType,
|
||||
success = offlineUpdateTaskBean.success,
|
||||
templatePhotoInfoList = offlineUpdateTaskBean.templatePhotoInfoList)
|
||||
|
||||
RetrofitHelper.getDefaultService().updateTask(updateTaskRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<UpdateTaskBean>() {
|
||||
override fun doSuccess(it : UpdateTaskBean?) {
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务", "success=$it request=${updateTaskRequest.toJson()}")
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
isUploading = false
|
||||
offlineListener?.uploadFailure(msg)
|
||||
LogUtil.print("离线任务",
|
||||
"uploadTask failed==${offlineUpdateTaskBean.toJson()}")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun uploadOrderImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
isUploading = true
|
||||
var file = File(offlineUpdateTaskBean.photoLocalWaterMarkerPath ?: "")
|
||||
if (! file.exists()) {
|
||||
offlineListener?.uploadFailure("图片未找到!")
|
||||
LogUtil.print("离线任务 uploadOrderImage",
|
||||
"uploadTask failed==${offlineUpdateTaskBean.toJson()}")
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
return
|
||||
}
|
||||
|
||||
if (offlineUpdateTaskBean.imageAddress.isNullOrBlank() && (offlineUpdateTaskBean.imageLat != null && offlineUpdateTaskBean.imageLat != 0f && offlineUpdateTaskBean.imageLng != null && offlineUpdateTaskBean.imageLng != 0f)) {
|
||||
geoCoder(offlineUpdateTaskBean.imageLat.toDouble(),
|
||||
offlineUpdateTaskBean.imageLng.toDouble(),
|
||||
success = { it ->
|
||||
val photoMarkerInfo = PhotoMarkerInfo(offlineUpdateTaskBean.needWater,
|
||||
needShowPhoneBrand = offlineUpdateTaskBean.needPhoneBrand,
|
||||
path = offlineUpdateTaskBean.photoLocalWaterMarkerPath,
|
||||
from = "(离线)",
|
||||
lat = offlineUpdateTaskBean.imageLat.toDouble(),
|
||||
lng = offlineUpdateTaskBean.imageLng.toDouble(),
|
||||
address = it,
|
||||
time = offlineUpdateTaskBean.time,
|
||||
driverName = GlobalData.driverInfo?.userName,
|
||||
taskCode = offlineUpdateTaskBean.taskCode)
|
||||
val offlineTemp = offlineUpdateTaskBean.copy(imageAddress = it)
|
||||
file = File(PhotoMarkerManager.addPhotoMarker(ActivityUtils.getTopActivity(),
|
||||
photoMarkerInfo))
|
||||
CommonMethod.uploadImage(file = file, success = {
|
||||
val item = RoomHelper.db?.offlineTaskDao()
|
||||
?.getRecentOfflineTask(offlineTemp.taskId ?: 0)?.get(0)
|
||||
if (item == null) {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
return@uploadImage
|
||||
}
|
||||
LogUtil.print("离线任务 getRecentOfflineTask", "success=${item.toJson()}")
|
||||
val list = item.templatePhotoInfoList?.toMutableList()
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject["realTakePhotoTime"] = offlineTemp.realTakePhotoTime ?: ""
|
||||
jsonObject["photoSource"] = offlineTemp.photoSource
|
||||
jsonObject["path"] = it
|
||||
jsonObject["time"] = offlineTemp.time
|
||||
jsonObject["lat"] = offlineTemp.imageLat
|
||||
jsonObject["lng"] = offlineTemp.imageLng
|
||||
jsonObject["address"] = offlineTemp.imageAddress
|
||||
list?.set(offlineTemp.imageIndex ?: 0, jsonObject.toJSONString())
|
||||
val temp = item.copy(templatePhotoInfoList = list?.toList())
|
||||
RoomHelper.db?.offlineTaskDao()?.update(temp)
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineTemp.primaryId ?: 0)
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务 逆地理 uploadOrderImage",
|
||||
"success=$it request=${offlineUpdateTaskBean.toJson()}")
|
||||
}, failed = {
|
||||
RoomHelper.db?.offlineTaskDao()?.update(offlineTemp)
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
})
|
||||
},
|
||||
failed = {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
})
|
||||
} else {
|
||||
CommonMethod.uploadImage(file = file, success = {
|
||||
val item = RoomHelper.db?.offlineTaskDao()
|
||||
?.getRecentOfflineTask(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
|
||||
if (item == null) {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
return@uploadImage
|
||||
}
|
||||
LogUtil.print("离线任务 getRecentOfflineTask", "success=${item.toJson()}")
|
||||
val list = item.templatePhotoInfoList?.toMutableList()
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject["realTakePhotoTime"] = offlineUpdateTaskBean.realTakePhotoTime ?: ""
|
||||
jsonObject["photoSource"] = offlineUpdateTaskBean.photoSource
|
||||
jsonObject["path"] = it
|
||||
jsonObject["time"] = offlineUpdateTaskBean.time
|
||||
jsonObject["lat"] = offlineUpdateTaskBean.imageLat
|
||||
jsonObject["lng"] = offlineUpdateTaskBean.imageLng
|
||||
jsonObject["address"] = offlineUpdateTaskBean.imageAddress
|
||||
list?.set(offlineUpdateTaskBean.imageIndex ?: 0, jsonObject.toJSONString())
|
||||
val temp = item.copy(templatePhotoInfoList = list?.toList())
|
||||
|
||||
RoomHelper.db?.offlineTaskDao()?.update(temp)
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务 uploadOrderImage",
|
||||
"success=$it request=${offlineUpdateTaskBean.toJson()}")
|
||||
}, failed = {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun uploadEleDamageImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
isUploading = true
|
||||
val file = File(offlineUpdateTaskBean.imageLocalPath ?: "")
|
||||
if (! file.exists()) {
|
||||
offlineListener?.uploadFailure("图片未找到!")
|
||||
LogUtil.print("离线任务 uploadEleDamageImage",
|
||||
"failed==${offlineUpdateTaskBean.toJson()}")
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
return
|
||||
}
|
||||
CommonMethod.uploadImage(file = file, success = {
|
||||
val item = RoomHelper.db?.offlineTaskDao()
|
||||
?.getRecentOfflineEle(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
|
||||
if (item == null) {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
return@uploadImage
|
||||
}
|
||||
LogUtil.print("离线任务 getRecentOfflineEle", "success=${item.toJson()}")
|
||||
val list = item.damageFileList?.toMutableList()
|
||||
list?.set(offlineUpdateTaskBean.imageIndex ?: 0, it)
|
||||
val temp = item.copy(templatePhotoInfoList = list?.toList())
|
||||
|
||||
RoomHelper.db?.offlineTaskDao()?.update(temp)
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务 uploadEleDamageImage",
|
||||
"success=$it request=${offlineUpdateTaskBean.toJson()}")
|
||||
}, failed = {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
})
|
||||
}
|
||||
|
||||
private fun uploadEleWorkOrder(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
val saveEleOrderRequest = SaveEleOrderRequest(state = offlineUpdateTaskBean.eleState,
|
||||
userOrderId = offlineUpdateTaskBean.userOrderId,
|
||||
taskOrderId = offlineUpdateTaskBean.taskId,
|
||||
damageFileList = offlineUpdateTaskBean.damageFileList,
|
||||
hasDamage = offlineUpdateTaskBean.hasDamage,
|
||||
hasSuccess = offlineUpdateTaskBean.hasSuccess,
|
||||
customerSignPath = offlineUpdateTaskBean.customerSignPath,
|
||||
recipientSignPath = offlineUpdateTaskBean.recipientSignPath,
|
||||
waitstaffSignPath = offlineUpdateTaskBean.waitstaffSignPath,
|
||||
offlineMode = 1,
|
||||
userOrderCode = offlineUpdateTaskBean.userOrderCode,
|
||||
lat = offlineUpdateTaskBean.eleLat,
|
||||
lng = offlineUpdateTaskBean.eleLng,
|
||||
tyreNumber = offlineUpdateTaskBean.tyreNumber,
|
||||
isFinish = offlineUpdateTaskBean.isFinish)
|
||||
LogUtil.print("离线数据 eleSign_check request", saveEleOrderRequest.toJson() ?: "")
|
||||
RetrofitHelper.getDefaultService().saveElectronOrder(saveEleOrderRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<String>() {
|
||||
override fun doSuccess(it : String?) {
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务 uploadEleWorkOrder",
|
||||
"success=$it request=${offlineUpdateTaskBean.toJson()}")
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
offlineListener?.uploadFailure(msg)
|
||||
isUploading = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun uploadCustomerSignImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
isUploading = true
|
||||
val file = File(offlineUpdateTaskBean.imageLocalPath ?: "")
|
||||
if (! file.exists()) {
|
||||
offlineListener?.uploadFailure("图片未找到!")
|
||||
LogUtil.print("离线任务 uploadCustomerSignImage",
|
||||
"failed==${offlineUpdateTaskBean.toJson()}")
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
return
|
||||
}
|
||||
CommonMethod.uploadImage(file = file, success = { it ->
|
||||
val item = RoomHelper.db?.offlineTaskDao()
|
||||
?.getRecentOfflineEle(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
|
||||
if (item == null) {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
return@uploadImage
|
||||
}
|
||||
LogUtil.print("离线任务 uploadCustomerSignImage getRecentOfflineEle",
|
||||
"success=${item.toJson()}")
|
||||
RoomHelper.db?.offlineTaskDao()?.update(item.copy(customerSignPath = it))
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(offlineUpdateTaskBean.taskId ?: 0)
|
||||
?.copy(serverCustomSignPath = it)?.let {
|
||||
RoomHelper.db?.eleWorkOrderDao()?.update(it)
|
||||
}
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务 uploadCustomerSignImage",
|
||||
"success=$it request=${offlineUpdateTaskBean.toJson()}")
|
||||
}, failed = {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
})
|
||||
}
|
||||
|
||||
private fun uploadAcceptPeopleSignImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
isUploading = true
|
||||
val file = File(offlineUpdateTaskBean.imageLocalPath ?: "")
|
||||
if (! file.exists()) {
|
||||
offlineListener?.uploadFailure("图片未找到!")
|
||||
LogUtil.print("离线任务 uploadCustomerSignImage",
|
||||
"failed==${offlineUpdateTaskBean.toJson()}")
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
return
|
||||
}
|
||||
CommonMethod.uploadImage(file = file, success = { it ->
|
||||
val item = RoomHelper.db?.offlineTaskDao()
|
||||
?.getRecentOfflineEle(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
|
||||
if (item == null) {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
return@uploadImage
|
||||
}
|
||||
LogUtil.print("离线任务 uploadAcceptPeopleSignImage getRecentOfflineEle",
|
||||
"success=${item.toJson()}")
|
||||
RoomHelper.db?.offlineTaskDao()?.update(item.copy(recipientSignPath = it))
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(offlineUpdateTaskBean.taskId ?: 0)
|
||||
?.copy(serverAcceptCarSignPath = it)?.let {
|
||||
RoomHelper.db?.eleWorkOrderDao()?.update(it)
|
||||
}
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务 uploadAcceptPeopleSignImage",
|
||||
"success=$it request=${offlineUpdateTaskBean.toJson()}")
|
||||
}, failed = {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
})
|
||||
}
|
||||
|
||||
private fun uploadServicePeopleSignImage(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
isUploading = true
|
||||
val file = File(offlineUpdateTaskBean.imageLocalPath ?: "")
|
||||
if (! file.exists()) {
|
||||
offlineListener?.uploadFailure("图片未找到!")
|
||||
LogUtil.print("离线任务 uploadCustomerSignImage",
|
||||
"failed==${offlineUpdateTaskBean.toJson()}")
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
return
|
||||
}
|
||||
CommonMethod.uploadImage(file = file, success = { it ->
|
||||
val item = RoomHelper.db?.offlineTaskDao()
|
||||
?.getRecentOfflineEle(offlineUpdateTaskBean.taskId ?: 0)?.get(0)
|
||||
if (item == null) {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
return@uploadImage
|
||||
}
|
||||
LogUtil.print("离线任务 uploadCustomerSignImage getRecentOfflineEle",
|
||||
"success=${item.toJson()}")
|
||||
RoomHelper.db?.offlineTaskDao()?.update(item.copy(waitstaffSignPath = it))
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
RoomHelper.db?.eleWorkOrderDao()?.getEleWorkOrder(offlineUpdateTaskBean.taskId ?: 0)
|
||||
?.copy(serverServicePeopleSignPath = it)?.let {
|
||||
RoomHelper.db?.eleWorkOrderDao()?.update(it)
|
||||
}
|
||||
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务 uploadCustomerSignImage",
|
||||
"success=$it request=${offlineUpdateTaskBean.toJson()}")
|
||||
}, failed = {
|
||||
offlineListener?.uploadFailure(it)
|
||||
isUploading = false
|
||||
})
|
||||
}
|
||||
|
||||
private fun taskFinish(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
isUploading = true
|
||||
val taskFinishRequest = TaskFinishRequest(
|
||||
operateTime = offlineUpdateTaskBean.operateTime?.toLong(),
|
||||
lat = offlineUpdateTaskBean.updateTaskLat,
|
||||
userOrderId = offlineUpdateTaskBean.userOrderId,
|
||||
lng = offlineUpdateTaskBean.updateTaskLng,
|
||||
)
|
||||
RetrofitHelper.getDefaultService().taskFinish(taskFinishRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<TaskFinishResponse>() {
|
||||
override fun doSuccess(it : TaskFinishResponse?) {
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.deleteOfflineTaskFromId(offlineUpdateTaskBean.primaryId ?: 0)
|
||||
isUploading = false
|
||||
offlineListener?.uploadSuccess()
|
||||
LogUtil.print("离线任务", "success=$it request=${taskFinishRequest.toJson()}")
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
isUploading = false
|
||||
offlineListener?.uploadFailure(msg)
|
||||
LogUtil.print("离线任务",
|
||||
"uploadTask failed==${offlineUpdateTaskBean.toJson()}")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//打开系统悬浮窗权限
|
||||
private fun openSystemAlertPermissionDialog() {
|
||||
val context = ActivityUtils.getTopActivity()
|
||||
val alertdialog = AlertDialog.Builder(ActivityUtils.getTopActivity()).setTitle("提示")
|
||||
.setMessage("当前应用缺少【悬浮窗】必要权限。\n\n请点击\"设置\"-\"权限\"-打开所需权限。")
|
||||
.setCancelable(false).setPositiveButton("设置") { _, _ ->
|
||||
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
"package:${context.packageName}".toUri())
|
||||
intent.data = ("package:" + ActivityUtils.getTopActivity().packageName).toUri()
|
||||
ActivityUtils.getTopActivity().startActivity(intent)
|
||||
}
|
||||
alertdialog.show()
|
||||
}
|
||||
|
||||
//逆地址编码
|
||||
private fun geoCoder(lat : Double,
|
||||
lng : Double,
|
||||
success : (String) -> Unit,
|
||||
failed : (String) -> Unit = {}) {
|
||||
val geocoderSearch = GeocodeSearch(ActivityUtils.getTopActivity().application)
|
||||
val query = RegeocodeQuery(LatLonPoint(lat, lng), 200f, GeocodeSearch.AMAP)
|
||||
geocoderSearch.getFromLocationAsyn(query)
|
||||
geocoderSearch.setOnGeocodeSearchListener(object : OnGeocodeSearchListener {
|
||||
override fun onRegeocodeSearched(regeocodeResult : RegeocodeResult, i : Int) {
|
||||
if (i == 1000) {
|
||||
success(regeocodeResult.regeocodeAddress.formatAddress)
|
||||
LogUtil.print("离线任务 singleLocation 逆地理编码",
|
||||
regeocodeResult.regeocodeAddress.formatAddress)
|
||||
} else {
|
||||
failed("地址获取失败")
|
||||
LogUtil.print("singleLocation 逆地理失败 ", regeocodeResult.toString())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGeocodeSearched(geocodeResult : GeocodeResult, i : Int) {}
|
||||
})
|
||||
}
|
270
servicing/src/main/java/com/za/offline/OfflineService.kt
Normal file
270
servicing/src/main/java/com/za/offline/OfflineService.kt
Normal file
@ -0,0 +1,270 @@
|
||||
package com.za.offline
|
||||
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.PixelFormat
|
||||
import android.os.Build
|
||||
import android.view.GestureDetector
|
||||
import android.view.GestureDetector.OnGestureListener
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.blankj.utilcode.util.ActivityUtils
|
||||
import com.za.base.theme.headBgColor
|
||||
import com.za.room.RoomHelper
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object OfflineService : IOfflineListener {
|
||||
private var viewcompose : View? = null
|
||||
private var lifecycleOwner : MyOfflineServiceViewLifecycleOwner? = null
|
||||
private var windowManager : WindowManager? = null
|
||||
private var layoutParams : WindowManager.LayoutParams? = null
|
||||
private var currentOfflineUpdateTaskBean = mutableStateOf<OfflineUpdateTaskBean?>(null)
|
||||
private var state = mutableIntStateOf(0) //0 初始化 1 准备上传 2 结束上传 3上传中 4 上传失败 5 上传完成
|
||||
|
||||
private var onGestureListener =
|
||||
GestureDetector(ActivityUtils.getTopActivity().application, object : OnGestureListener {
|
||||
var xDown : Int = 0
|
||||
var yDown : Int = 0
|
||||
override fun onDown(motionEvent : MotionEvent) : Boolean {
|
||||
xDown = motionEvent.rawX.toInt()
|
||||
yDown = motionEvent.rawY.toInt()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onShowPress(e : MotionEvent) {
|
||||
|
||||
}
|
||||
|
||||
override fun onSingleTapUp(e : MotionEvent) : Boolean {
|
||||
val context = ActivityUtils.getTopActivity()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScroll(e1 : MotionEvent?,
|
||||
motionEvent : MotionEvent,
|
||||
distanceX : Float,
|
||||
distanceY : Float) : Boolean {
|
||||
val nowX = motionEvent.rawX.toInt()
|
||||
val nowY = motionEvent.rawY.toInt()
|
||||
val movedX = nowX - xDown
|
||||
val movedY = nowY - yDown
|
||||
xDown = nowX
|
||||
yDown = nowY
|
||||
layoutParams?.apply {
|
||||
x += movedX
|
||||
y += movedY
|
||||
} //更新悬浮球控件位置
|
||||
updateView()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onLongPress(e : MotionEvent) {
|
||||
|
||||
}
|
||||
|
||||
override fun onFling(e1 : MotionEvent?,
|
||||
e2 : MotionEvent,
|
||||
velocityX : Float,
|
||||
velocityY : Float) : Boolean {
|
||||
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
init {
|
||||
windowManager =
|
||||
ActivityUtils.getTopActivity().getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
layoutParams = WindowManager.LayoutParams()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
layoutParams?.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
||||
} else {
|
||||
layoutParams?.type = WindowManager.LayoutParams.TYPE_PHONE
|
||||
}
|
||||
layoutParams?.format = PixelFormat.RGBA_8888
|
||||
layoutParams?.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
layoutParams?.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
layoutParams?.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
}
|
||||
|
||||
fun addListener() {
|
||||
OfflineManager.addOfflineListener(this)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun showWindow() {
|
||||
destroyView()
|
||||
viewcompose = ComposeView(ActivityUtils.getTopActivity().application).apply {
|
||||
setContent {
|
||||
MyOfflineServiceView(currentOfflineUpdateTaskBean.value, state = state.intValue)
|
||||
}
|
||||
}.rootView
|
||||
|
||||
viewcompose?.setOnTouchListener { _, event ->
|
||||
onGestureListener.onTouchEvent(event)
|
||||
if (event.action == MotionEvent.ACTION_UP) {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
lifecycleOwner = MyOfflineServiceViewLifecycleOwner().also {
|
||||
it.onCreate() // 注意
|
||||
it.onStart()
|
||||
it.attachToDecorView(viewcompose)
|
||||
}
|
||||
windowManager?.addView(viewcompose, layoutParams)
|
||||
}
|
||||
|
||||
private fun destroyView() {
|
||||
viewcompose?.let {
|
||||
windowManager?.removeViewImmediate(viewcompose)
|
||||
}
|
||||
viewcompose = null
|
||||
lifecycleOwner?.onDestroy()
|
||||
lifecycleOwner = null
|
||||
}
|
||||
|
||||
private fun updateView() {
|
||||
windowManager?.updateViewLayout(viewcompose, layoutParams)
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
showWindow()
|
||||
state.intValue = 1
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
destroyView()
|
||||
}
|
||||
|
||||
override fun uploadSuccess() {
|
||||
state.intValue = 4
|
||||
}
|
||||
|
||||
override fun uploadFailure(msg : String?) {
|
||||
state.intValue = 3
|
||||
}
|
||||
|
||||
override fun currentTask(offlineUpdateTaskBean : OfflineUpdateTaskBean) {
|
||||
state.intValue = 2
|
||||
currentOfflineUpdateTaskBean.value = offlineUpdateTaskBean
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//0 初始化 1 准备上传 2 上传中 3 上传失败 4 上传完成
|
||||
@Composable
|
||||
fun MyOfflineServiceView(offlineUpdateTaskBean : OfflineUpdateTaskBean?, state : Int) {
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = Color.White, shape = RoundedCornerShape(8.dp))
|
||||
.border(border = BorderStroke(width = 1.dp, color = Color.Red),
|
||||
shape = RoundedCornerShape(8.dp))
|
||||
.padding(10.dp)) {
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("离线订单:", color = Color.Gray, fontSize = 15.sp)
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
Text(offlineUpdateTaskBean?.taskCode ?: "",
|
||||
color = Color.Black,
|
||||
fontWeight = FontWeight.Bold)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("剩余任务:", color = Color.Gray, fontSize = 15.sp)
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
Text("${
|
||||
RoomHelper.db?.offlineTaskDao()
|
||||
?.getOfflineTaskFromTaskId(offlineUpdateTaskBean?.taskId ?: 0)?.size
|
||||
}", color = Color.Black, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Row(modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Start) {
|
||||
Text("当前节点:", color = Color.Gray, fontSize = 15.sp)
|
||||
Spacer(Modifier.width(5.dp))
|
||||
Text(offlineUpdateTaskBean?.offlineTitle ?: "",
|
||||
color = Color.Black,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 15.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis)
|
||||
|
||||
when (state) {
|
||||
1 -> {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text("准备上传",
|
||||
color = headBgColor,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold)
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
}
|
||||
|
||||
2 -> {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text("上传中",
|
||||
color = headBgColor,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold)
|
||||
Spacer(modifier = Modifier.width(3.dp))
|
||||
CircularProgressIndicator(modifier = Modifier.size(12.dp), color = headBgColor)
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
}
|
||||
|
||||
3 -> {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text("上传失败",
|
||||
color = Color.Red,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis)
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
}
|
||||
|
||||
4 -> {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text("上传完成",
|
||||
color = headBgColor,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis)
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package com.za.offline
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverter
|
||||
import androidx.room.TypeConverters
|
||||
import com.alibaba.fastjson.JSON
|
||||
|
||||
@TypeConverters(InspectionConverter::class)
|
||||
@Entity(tableName = "offline_update_task_bean")
|
||||
data class OfflineUpdateTaskBean(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val primaryId: Int? = null,
|
||||
|
||||
//以下为上传状态的参数
|
||||
val type: String? = null,
|
||||
val taskId: Int? = null,
|
||||
val userId: Int? = null,
|
||||
val vehicleId: Int? = null,
|
||||
val currentState: String? = null,
|
||||
val operateTime: String? = null,
|
||||
val updateTaskLat: Double? = null,
|
||||
val updateTaskLng: Double? = null,
|
||||
val updateTaskAddress: String? = null,
|
||||
val offlineMode: Int? = null, //0 正常 1 离线
|
||||
val flowType: Int? = null,
|
||||
val success: Int? = null, //作业是否完成 0 成功 1不成功 拖车默认成功
|
||||
val templatePhotoInfoList: List<String?>? = null, //照片路径,使用@分隔符分开
|
||||
|
||||
//以下为上传图片的参数
|
||||
val realTakePhotoTime: String? = null,//真实拍照时间
|
||||
val photoSource: Int? = null,//1相机 2 相册 3真实位置
|
||||
val time: String? = null,// 拍照时间
|
||||
val imageLat: Float? = null,
|
||||
val imageLng: Float? = null,
|
||||
val imageAddress: String? = null,
|
||||
|
||||
val imageIndex: Int? = null,
|
||||
val imageLocalPath: String? = null,
|
||||
val imageUploadPath: String? = null,
|
||||
val advanceTime: Long? = null,
|
||||
val needWater:Boolean?=null,
|
||||
val needPhoneBrand:Boolean?=null,
|
||||
val photoLocalWaterMarkerPath: String? = null,
|
||||
|
||||
|
||||
//以下为电子工单状态
|
||||
val eleState: Int? = null,
|
||||
val hasDamage: Int? = null, //是否有算上 1 有
|
||||
val hasSuccess: Int? = null, // 作业是否完成 1成功 0失败
|
||||
val customerSignPath: String? = null, //客户签名照片
|
||||
val recipientSignPath: String? = null, //接车人签名
|
||||
val waitstaffSignPath: String? = null, //服务人员签名
|
||||
val damageFileList: List<String?>? = null, //损伤文件列表 //照片路径,使用,分隔符分开
|
||||
val userOrderCode: String? = null, //仅在历史补传电子工单时传
|
||||
val tyreNumber: Int? = null, //小轮个数
|
||||
val eleLng: Double? = null,
|
||||
val eleLat: Double? = null,
|
||||
val isFinish: Boolean = false, //订单是否完成进入历史 true 完成
|
||||
|
||||
|
||||
//以下为自定义字段
|
||||
val userOrderId: Int? = null,
|
||||
val taskCode: String? = null,
|
||||
val isUpload: Int = 0,//是否已经上传 0 未上传 1 已经上传
|
||||
val offlineTime: Long = System.currentTimeMillis(), //添加离线的时间
|
||||
val offlineTitle: String? = null,
|
||||
val offlineTimeStr: String? = com.blankj.utilcode.util.TimeUtils.getNowString(),
|
||||
val offlineType: Int? = null,// 1 订单状态 2 订单图片 3 电子工单状态 4 电子工单损伤照片 5客户签名照片 6接车人签名照片 7服务人员签名照片 8 任务完成
|
||||
)
|
||||
|
||||
class InspectionConverter {
|
||||
@TypeConverter
|
||||
fun stringToObject(value: String?): List<String?>? {
|
||||
if (value.isNullOrBlank()) {
|
||||
return null
|
||||
}
|
||||
val str = JSON.parseArray(value, String::class.java)
|
||||
return str
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun objectToString(list: List<String?>?): String? {
|
||||
if (list.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
val str = JSON.toJSONString(list)
|
||||
return str
|
||||
}
|
||||
}
|
74
servicing/src/main/java/com/za/room/RoomHelper.kt
Normal file
74
servicing/src/main/java/com/za/room/RoomHelper.kt
Normal file
@ -0,0 +1,74 @@
|
||||
package com.za.room
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import androidx.room.Room.databaseBuilder
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.za.base.Const
|
||||
import com.za.room.db.GlobalRoom
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object RoomHelper {
|
||||
const val VERSION: Int = 35
|
||||
private lateinit var mContext: Context
|
||||
var db: GlobalRoom? = null
|
||||
|
||||
//ele_work_order 表中新增userOrderId字段
|
||||
//ele_car_damage_photo 表中新增userOrderId
|
||||
private var MIGRATION_2_3: Migration = object : Migration(2, 3) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("ALTER TABLE ele_work_order" + " ADD COLUMN userOrderId INTEGER NOT NULL DEFAULT 0")
|
||||
db.execSQL("ALTER TABLE ele_car_damage_photo" + " ADD COLUMN userOrderId INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
|
||||
//增加水印模版
|
||||
private var MIGRATION_3_4: Migration = object : Migration(3, 4) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("CREATE TABLE water_template(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + "templateName TEXT," + "taskCode TEXT," + "templateId INTEGER NOT NULL DEFAULT 0)")
|
||||
db.execSQL("CREATE TABLE water_marker_item(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + "waterTemplateId TEXT," + "taskCode TEXT," + "water_left DECIMAL," + "water_right DECIMAL," + "top DECIMAL," + "bottom DECIMAL," + "content TEXT," + "color TEXT," + "wideFont TEXT," + "highFont TEXT)")
|
||||
}
|
||||
}
|
||||
|
||||
fun init(context: Context) {
|
||||
this.mContext = context
|
||||
db = databaseBuilder(mContext, GlobalRoom::class.java, "zd_db")
|
||||
.allowMainThreadQueries().addMigrations(MIGRATION_2_3, MIGRATION_3_4)
|
||||
.fallbackToDestructiveMigration() //出现异常问题,重新构建表,同时数据会丢失
|
||||
.setJournalMode(RoomDatabase.JournalMode.AUTOMATIC).build()
|
||||
}
|
||||
|
||||
fun clearAll() {
|
||||
db?.orderDao()?.deleteAllOrder()
|
||||
db?.photoTemplateDao()?.deleteAllPhotoTemplate()
|
||||
db?.changeBatteryDao()?.deleteAll()
|
||||
db?.eleWorkOrderDao()?.deleteTable()
|
||||
db?.offlineTaskDao()?.deleteAllOfflineTask()
|
||||
val temp = db?.waterMarkerDao()?.queryAll()
|
||||
temp?.forEach {
|
||||
if (it.waterMarkerTemplateBean?.taskCode != Const.NormalWaterMarker) {
|
||||
db?.waterMarkerDao()?.deleteWaterMarkerItem(it.waterMarkerTemplateBean?.taskCode
|
||||
?: "")
|
||||
db?.waterMarkerDao()?.deleteWaterTemplate(it.waterMarkerTemplateBean?.taskCode
|
||||
?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearOrderFromTaskCode(taskId: Int) {
|
||||
db?.orderDao()?.deleteOrderFromTaskId(taskId)
|
||||
db?.photoTemplateDao()?.deleteOrderPhotoTemplateFromTaskId(taskId)
|
||||
db?.changeBatteryDao()?.deleteFromTaskId(taskId)
|
||||
db?.eleWorkOrderDao()?.deleteOrder(taskId)
|
||||
val temp = db?.waterMarkerDao()?.queryAll()
|
||||
temp?.forEach {
|
||||
if (it.waterMarkerTemplateBean?.taskCode != Const.NormalWaterMarker && it.waterMarkerTemplateBean?.taskId == taskId) {
|
||||
db?.waterMarkerDao()?.deleteWaterMarkerItem(it.waterMarkerTemplateBean.taskCode
|
||||
?: "")
|
||||
db?.waterMarkerDao()?.deleteWaterTemplate(it.waterMarkerTemplateBean.taskCode ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
servicing/src/main/java/com/za/room/db/GlobalRoom.kt
Normal file
49
servicing/src/main/java/com/za/room/db/GlobalRoom.kt
Normal file
@ -0,0 +1,49 @@
|
||||
package com.za.room.db
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import com.za.bean.db.ChangeBatteryPhoto
|
||||
import com.za.bean.db.NewPhotoTemplateBean
|
||||
import com.za.bean.db.ele.EleCarDamagePhotoBean
|
||||
import com.za.bean.db.ele.EleWorkOrderBean
|
||||
import com.za.bean.db.order.OrderInfo
|
||||
import com.za.bean.db.order.PhotoTemplateInfo
|
||||
import com.za.bean.db.water_marker.WaterMarkerItemBean
|
||||
import com.za.bean.db.water_marker.WaterMarkerTemplateBean
|
||||
import com.za.offline.InspectionConverter
|
||||
import com.za.offline.OfflineDao
|
||||
import com.za.offline.OfflineUpdateTaskBean
|
||||
import com.za.room.RoomHelper
|
||||
import com.za.room.db.ele_work.EleCarDamagePhotoDao
|
||||
import com.za.room.db.ele_work.EleWorkOrderDao
|
||||
import com.za.room.db.order.ChangeBatteryDao
|
||||
import com.za.room.db.order.OrderDao
|
||||
import com.za.room.db.order.PhotoTemplateDao
|
||||
import com.za.room.db.water_marker.WaterMarkerDao
|
||||
|
||||
|
||||
@Database(entities = [EleWorkOrderBean::class,
|
||||
EleCarDamagePhotoBean::class,
|
||||
WaterMarkerTemplateBean::class,
|
||||
WaterMarkerItemBean::class,
|
||||
ChangeBatteryPhoto::class,
|
||||
NewPhotoTemplateBean::class,
|
||||
OrderInfo::class,
|
||||
OfflineUpdateTaskBean::class,
|
||||
PhotoTemplateInfo::class], version = RoomHelper.VERSION, exportSchema = false)
|
||||
abstract class GlobalRoom : RoomDatabase() {
|
||||
abstract fun eleWorkOrderDao(): EleWorkOrderDao
|
||||
|
||||
abstract fun eleCarDamagePhotoDao(): EleCarDamagePhotoDao
|
||||
|
||||
abstract fun waterMarkerDao(): WaterMarkerDao
|
||||
|
||||
abstract fun changeBatteryDao(): ChangeBatteryDao
|
||||
|
||||
abstract fun orderDao(): OrderDao
|
||||
|
||||
abstract fun photoTemplateDao(): PhotoTemplateDao
|
||||
|
||||
abstract fun offlineTaskDao(): OfflineDao
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.za.room.db.ele_work
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.za.bean.db.ele.EleCarDamagePhotoBean
|
||||
|
||||
@Dao
|
||||
interface EleCarDamagePhotoDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE, entity = EleCarDamagePhotoBean::class)
|
||||
fun insert(eleCarDamagePhotoBean: EleCarDamagePhotoBean)
|
||||
|
||||
@Query("DELETE FROM `ele_car_damage_photo` WHERE orderId =:orderId")
|
||||
fun deleteAll(orderId: Int)
|
||||
|
||||
@Query("DELETE FROM `ele_car_damage_photo`")
|
||||
fun deleteTable()
|
||||
|
||||
@Delete(entity = EleCarDamagePhotoBean::class)
|
||||
fun delete(eleCarDamagePhotoBean: EleCarDamagePhotoBean)
|
||||
|
||||
@Query("SELECT * FROM ele_car_damage_photo WHERE orderId =:orderId")
|
||||
fun getEleCarDamagePhotos(orderId: Int): List<EleCarDamagePhotoBean>?
|
||||
|
||||
@Query("SELECT * FROM ele_car_damage_photo WHERE id =:id")
|
||||
fun getEleCarDamagePhoto(id: Long): EleCarDamagePhotoBean?
|
||||
|
||||
//更新
|
||||
@Update(entity = EleCarDamagePhotoBean::class, onConflict = OnConflictStrategy.REPLACE)
|
||||
fun update(eleCarDamagePhotoBean: EleCarDamagePhotoBean)
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.za.room.db.ele_work
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.za.bean.db.ele.EleWorkOrderBean
|
||||
|
||||
@Dao
|
||||
interface EleWorkOrderDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE, entity = EleWorkOrderBean::class)
|
||||
fun insertEleWorkOrder(eleWorkOrderBean: EleWorkOrderBean)
|
||||
|
||||
@Query("DELETE FROM `ele_work_order` WHERE orderId =:taskId")
|
||||
fun deleteOrder(taskId: Int)
|
||||
|
||||
@Query("DELETE FROM `ele_work_order`")
|
||||
fun deleteTable()
|
||||
|
||||
@Query("SELECT * FROM ele_work_order WHERE orderId =:taskId")
|
||||
fun getEleWorkOrder(taskId: Int): EleWorkOrderBean?
|
||||
|
||||
//更新
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE, entity = EleWorkOrderBean::class)
|
||||
fun update(eleWorkOrderBean: EleWorkOrderBean)
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.za.room.db.order
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.za.base.Const
|
||||
import com.za.bean.db.order.PhotoTemplateInfo
|
||||
|
||||
@Dao
|
||||
interface ChangeBatteryDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE, entity = PhotoTemplateInfo::class)
|
||||
fun insert(photoTemplateInfo: PhotoTemplateInfo)
|
||||
|
||||
//获取当前节点的图片模板
|
||||
@Query("select * from photo_template_bean where taskCode =:taskCode and myCustomPhotoType=${Const.PhotoType.ChangeBattery}")
|
||||
fun getAllChangeBatteryPhoto(taskCode: String): List<PhotoTemplateInfo>?
|
||||
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun update(photoTemplateInfo: PhotoTemplateInfo)
|
||||
|
||||
@Query("delete from photo_template_bean where taskId=:taskId and myCustomPhotoType=${Const.PhotoType.ChangeBattery}")
|
||||
fun deleteFromTaskId(taskId: Int)
|
||||
|
||||
@Query("delete from photo_template_bean where myCustomPhotoType=${Const.PhotoType.ChangeBattery}")
|
||||
fun deleteAll()
|
||||
}
|
39
servicing/src/main/java/com/za/room/db/order/OrderDao.kt
Normal file
39
servicing/src/main/java/com/za/room/db/order/OrderDao.kt
Normal file
@ -0,0 +1,39 @@
|
||||
package com.za.room.db.order
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.za.bean.db.order.OrderInfo
|
||||
|
||||
@Dao
|
||||
interface OrderDao {
|
||||
//更换电瓶照片
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOrder(orderInfo: OrderInfo)
|
||||
|
||||
@Query("select * from order_info where taskId =:taskId")
|
||||
fun getOrderInfoFromTaskId(taskId: Int): OrderInfo?
|
||||
|
||||
//获取当前正在执行的订单
|
||||
@Query("select * from order_info where isCurrent ==1")
|
||||
fun getCurrentOrder(): OrderInfo?
|
||||
|
||||
//获取不是当前正在执行的订单
|
||||
@Query("select * from order_info where isCurrent !=1")
|
||||
fun getAllNotCurrentOrder(): List<OrderInfo>?
|
||||
|
||||
//获取所有的订单
|
||||
@Query("select * from order_info")
|
||||
fun getAllOrder(): List<OrderInfo>?
|
||||
|
||||
@Query("delete from order_info")
|
||||
fun deleteAllOrder()
|
||||
|
||||
@Query("delete from order_info where taskId =:taskId")
|
||||
fun deleteOrderFromTaskId(taskId: Int)
|
||||
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun update(orderInfo: OrderInfo)
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.za.room.db.order
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import com.za.base.Const
|
||||
import com.za.bean.db.order.PhotoTemplateInfo
|
||||
|
||||
@Dao
|
||||
interface PhotoTemplateDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE, entity = PhotoTemplateInfo::class)
|
||||
fun insert(photoTemplateInfo: PhotoTemplateInfo)
|
||||
|
||||
//获取当前节点的图片模板
|
||||
@Query("select * from photo_template_bean where myCustomPhotoType=${Const.PhotoType.InServicing}")
|
||||
fun getAll(): List<PhotoTemplateInfo>?
|
||||
|
||||
//获取当前节点的图片模板
|
||||
@Query("select * from photo_template_bean where taskNode =:taskNode and userOrderId=:userOrderId " +
|
||||
"and myCustomPhotoType=${Const.PhotoType.InServicing}")
|
||||
fun getOrderPhotoTemplateFromTaskNode(taskNode: Int, userOrderId: Int): List<PhotoTemplateInfo>?
|
||||
|
||||
//获取当前节点的图片模板
|
||||
@Query("select * from photo_template_bean where taskNode =:taskNode and taskId=:taskId " +
|
||||
"and myCustomPhotoType=${Const.PhotoType.InServicing}")
|
||||
fun getOrderPhotoTemplateFromTaskNodeAndTaskId(taskNode: Int, taskId: Int): List<PhotoTemplateInfo>?
|
||||
|
||||
//获取当前节点的图片模板
|
||||
@Query("select * from photo_template_bean where userOrderId =:userOrderId " +
|
||||
"and myCustomPhotoType=${Const.PhotoType.InServicing}")
|
||||
fun queryCurrentOrderHasTaskNode(userOrderId: Int): List<PhotoTemplateInfo>?
|
||||
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun update(photoTemplateInfo: PhotoTemplateInfo)
|
||||
|
||||
@Query("delete from photo_template_bean where myCustomPhotoType=${Const.PhotoType.InServicing}")
|
||||
fun deleteAllPhotoTemplate()
|
||||
|
||||
@Query("delete from photo_template_bean where taskId =:taskId and myCustomPhotoType=${Const.PhotoType.InServicing}")
|
||||
fun deleteOrderPhotoTemplateFromTaskId(taskId: Int)
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.za.room.db.water_marker
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import com.za.bean.db.water_marker.WaterMarkerItemBean
|
||||
import com.za.bean.db.water_marker.WaterMarkerTemplateAndItemRef
|
||||
import com.za.bean.db.water_marker.WaterMarkerTemplateBean
|
||||
|
||||
@Dao
|
||||
interface WaterMarkerDao {
|
||||
@Transaction
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertWaterMarkerTemplate(waterMarkerTemplateBean: WaterMarkerTemplateBean)
|
||||
|
||||
@Transaction
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertWaterMarkerItem(waterMarkerItemBean: WaterMarkerItemBean)
|
||||
|
||||
@Transaction
|
||||
@Query("delete from water_template where taskCode=:taskCode")
|
||||
fun deleteWaterTemplate(taskCode: String)
|
||||
|
||||
@Transaction
|
||||
@Query("delete from water_marker_item where taskCode=:taskCode")
|
||||
fun deleteWaterMarkerItem(taskCode: String)
|
||||
|
||||
@Transaction
|
||||
@Query("select * from water_template where taskCode=:taskCode")
|
||||
fun query(taskCode: String): WaterMarkerTemplateAndItemRef?
|
||||
|
||||
@Transaction
|
||||
@Query("select * from water_template")
|
||||
fun queryAll(): List<WaterMarkerTemplateAndItemRef>?
|
||||
|
||||
@Query("delete from water_template")
|
||||
fun deleteWaterTemplate()
|
||||
|
||||
@Query("delete from water_marker_item")
|
||||
fun deleteWaterTemplateItem()
|
||||
}
|
222
servicing/src/main/java/com/za/service/ServiceManager.kt
Normal file
222
servicing/src/main/java/com/za/service/ServiceManager.kt
Normal file
@ -0,0 +1,222 @@
|
||||
package com.za.service
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.media.RingtoneManager
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import cn.jiguang.api.utils.JCollectionAuth
|
||||
import cn.jpush.android.api.JPushInterface
|
||||
import com.google.gson.Gson
|
||||
import com.za.bean.JpushBean
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.common.util.DeviceUtil
|
||||
import com.za.service.mqtt.MyMqttClient
|
||||
import com.za.servicing.R
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
interface PushListener {
|
||||
fun newOrderMsg(jpushBean : JpushBean)
|
||||
fun giveUpOrder(jpushBean : JpushBean)
|
||||
fun revokeOrder(jpushBean : JpushBean)
|
||||
fun reDispatchOrder(jpushBean : JpushBean)
|
||||
fun broadcast(string : String)
|
||||
fun importantTip(jpushBean : JpushBean)
|
||||
}
|
||||
|
||||
data class LastJPushBean(val msg : String, val time : Long = System.currentTimeMillis())
|
||||
|
||||
object ServiceManager {
|
||||
private val pushListener = AtomicReference<PushListener>()
|
||||
private var lastJPushBean : LastJPushBean? = null
|
||||
|
||||
// Initialize SharedPreferences
|
||||
fun initialize(context : Context) {
|
||||
LogUtil.print("ServiceManager", "Initializing ServiceManager")
|
||||
jpushRegister(context)
|
||||
MyMqttClient.initialize(deviceId = DeviceUtil.getAndroidId(context))
|
||||
}
|
||||
|
||||
// Register JPush
|
||||
private fun jpushRegister(context : Context) {
|
||||
try {
|
||||
JCollectionAuth.setAuth(context, true)
|
||||
JPushInterface.setDebugMode(false)
|
||||
JPushInterface.init(context)
|
||||
LogUtil.print("ServiceManager", "JPush initialized successfully")
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("ServiceManager", "JPush initialization failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Register push listener
|
||||
fun registerPushListener(listener : PushListener?) {
|
||||
listener?.let {
|
||||
pushListener.set(it)
|
||||
LogUtil.print("ServiceManager", "Registered push listener: ${it.javaClass.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
// Handle incoming push messages
|
||||
fun handlerPushMsg(msg : String) {
|
||||
LogUtil.print("JpushMessage", "Received push message: $msg")
|
||||
if (msg.startsWith("broadcast:")) {
|
||||
handleBroadcast(msg)
|
||||
return
|
||||
}
|
||||
try {
|
||||
val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java)
|
||||
if (lastJPushBean != null && (System.currentTimeMillis() - lastJPushBean !!.time < 3000) && lastJPushBean !!.msg == jpushOrderInfoBean.taskCode) {
|
||||
LogUtil.print("MessageHandler", "Duplicate message: " + lastJPushBean?.msg)
|
||||
return
|
||||
}
|
||||
|
||||
jpushOrderInfoBean?.taskCode?.let {
|
||||
lastJPushBean = LastJPushBean(it)
|
||||
}
|
||||
sendSystemNotificationFromMessage(jpushOrderInfoBean)
|
||||
when (jpushOrderInfoBean.pushType) {
|
||||
0 -> newOrderMsg(jpushOrderInfoBean)
|
||||
1 -> handleTypeOneMessage(jpushOrderInfoBean)
|
||||
3 -> importantTip(jpushOrderInfoBean)
|
||||
else -> LogUtil.print("JpushMessage",
|
||||
"Unknown push type: ${jpushOrderInfoBean.pushType}")
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
if (msg.startsWith("broadcast:")) {
|
||||
handleBroadcast(msg)
|
||||
}
|
||||
LogUtil.print("JpushMessage", "Error handling message: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Handle broadcast messages
|
||||
private fun handleBroadcast(msg : String) {
|
||||
try {
|
||||
val content = msg.substring(10)
|
||||
pushListener.get()?.broadcast(content)
|
||||
sendNotification(GlobalData.application, content)
|
||||
LogUtil.print("JpushMessage", "Broadcast content: $content")
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("JpushMessage", "Broadcast failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Handle type one messages
|
||||
private fun handleTypeOneMessage(jpushOrderBean : JpushBean) {
|
||||
when (jpushOrderBean.typeDesc) {
|
||||
"giveUp" -> giveUpOrder(jpushOrderBean)
|
||||
"revoke" -> revokeOrder(jpushOrderBean)
|
||||
"reDispatch" -> reDispatchOrder(jpushOrderBean)
|
||||
else -> LogUtil.print("JpushMessage", "Unknown typeDesc: ${jpushOrderBean.typeDesc}")
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new order messages
|
||||
private fun newOrderMsg(jpushOrderBean : JpushBean) {
|
||||
try {
|
||||
pushListener.get()?.newOrderMsg(jpushOrderBean)
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("JpushMessage", "Failed to handle new order message: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Handle give up order messages
|
||||
private fun giveUpOrder(jpushOrderBean : JpushBean) {
|
||||
pushListener.get()?.giveUpOrder(jpushOrderBean)
|
||||
}
|
||||
|
||||
// Handle revoke order messages
|
||||
private fun revokeOrder(jpushOrderBean : JpushBean) {
|
||||
pushListener.get()?.revokeOrder(jpushOrderBean)
|
||||
}
|
||||
|
||||
// Handle re-dispatch order messages
|
||||
private fun reDispatchOrder(jpushOrderBean : JpushBean) {
|
||||
pushListener.get()?.reDispatchOrder(jpushOrderBean)
|
||||
}
|
||||
|
||||
// Handle important tip messages
|
||||
private fun importantTip(jpushOrderBean : JpushBean) {
|
||||
pushListener.get()?.importantTip(jpushOrderBean)
|
||||
}
|
||||
|
||||
// Disconnect from JPush and MQTT
|
||||
fun disconnect(context : Context) {
|
||||
try {
|
||||
JPushInterface.stopPush(context) // Stop JPush
|
||||
MyMqttClient.disconnect() // Disconnect MQTT
|
||||
LogUtil.print("ServiceManager", "Disconnected from JPush and MQTT successfully")
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("ServiceManager", "Error during disconnection: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private const val CHANNEL_ID = "ImportantMessagesChannel"
|
||||
private const val NOTIFICATION_ID = 1003
|
||||
|
||||
// Initialize notification channel
|
||||
private fun createNotificationChannel(context : Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(CHANNEL_ID,
|
||||
"订单通知",
|
||||
NotificationManager.IMPORTANCE_HIGH).apply {
|
||||
description = "用于接收重要消息通知"
|
||||
setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION),
|
||||
Notification.AUDIO_ATTRIBUTES_DEFAULT)
|
||||
enableVibration(true)
|
||||
}
|
||||
val notificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendSystemNotificationFromMessage(jpushOrderInfoBean : JpushBean) {
|
||||
when (jpushOrderInfoBean.pushType) {
|
||||
0 -> sendNotification(GlobalData.application,
|
||||
"您有新的 ${jpushOrderInfoBean.addressProperty ?: ""} ${jpushOrderInfoBean.serviceTypeName ?: ""}: ${jpushOrderInfoBean.taskCode ?: ""}")
|
||||
|
||||
1 -> {
|
||||
when (jpushOrderInfoBean.typeDesc) {
|
||||
"giveUp" -> sendNotification(GlobalData.application,
|
||||
"订单:${jpushOrderInfoBean.taskCode ?: ""}已被放弃!")
|
||||
|
||||
"revoke" -> sendNotification(GlobalData.application,
|
||||
"订单:${jpushOrderInfoBean.taskCode ?: ""}已被撤回!")
|
||||
|
||||
"reDispatch" -> sendNotification(GlobalData.application,
|
||||
"订单:${jpushOrderInfoBean.taskCode ?: ""}被改派!")
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
3 -> sendNotification(GlobalData.application,
|
||||
"重要提醒:${jpushOrderInfoBean.tipContent ?: ""}")
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
// Send notification
|
||||
private fun sendNotification(context : Context, message : String) {
|
||||
createNotificationChannel(context)
|
||||
val notification =
|
||||
NotificationCompat.Builder(context, CHANNEL_ID).setContentTitle("重要通知")
|
||||
.setContentText(message).setSmallIcon(R.mipmap.ic_launcher) // 替换为你的应用图标
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH).setAutoCancel(true) // 点击后自动取消通知
|
||||
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
|
||||
.setVibrate(longArrayOf(0, 100, 200, 300)).build()
|
||||
|
||||
val notificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.za.service.jpush
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import cn.jpush.android.api.JPushInterface
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.service.ServiceManager
|
||||
|
||||
/**
|
||||
* Created by zhangj on 2019/4/4.
|
||||
*/
|
||||
class JPushReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val bundle = intent.extras
|
||||
LogUtil.print("JpushMessage ", "onReceive==" + "action:" + intent.action)
|
||||
if (intent.action == null || bundle == null) { return }
|
||||
when (intent.action) {
|
||||
JPushInterface.ACTION_REGISTRATION_ID -> {
|
||||
val regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID)
|
||||
LogUtil.print("JpushMessage ", "Registration successful: $regId")
|
||||
}
|
||||
|
||||
JPushInterface.ACTION_MESSAGE_RECEIVED -> {
|
||||
val msg = bundle.getString(JPushInterface.EXTRA_MESSAGE)
|
||||
if (msg.isNullOrBlank()) { return }
|
||||
ServiceManager.handlerPushMsg(msg)
|
||||
}
|
||||
|
||||
JPushInterface.ACTION_NOTIFICATION_RECEIVED -> {
|
||||
val title = bundle.getString(JPushInterface.EXTRA_NOTIFICATION_TITLE)
|
||||
LogUtil.print("JPushReceiver:", "ACTION_NOTIFICATION_RECEIVED title: $title")
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package com.za.service.jpush
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import cn.jpush.android.api.CmdMessage
|
||||
import cn.jpush.android.api.JPushMessage
|
||||
import cn.jpush.android.service.JPushMessageReceiver
|
||||
import com.za.common.log.LogUtil
|
||||
|
||||
/**
|
||||
* 自定义JPush message 接收器,包括操作tag/alias的结果返回(仅仅包含tag/alias新接口部分)
|
||||
*/
|
||||
class MyJPushMessageReceiver : JPushMessageReceiver() {
|
||||
override fun onRegister(context: Context, s: String) {
|
||||
super.onRegister(context, s)
|
||||
LogUtil.print("JPushConnected onRegister", "REGID:$s")
|
||||
}
|
||||
|
||||
override fun onConnected(context: Context, b: Boolean) {
|
||||
super.onConnected(context, b)
|
||||
LogUtil.print("JPushConnected", "极光连接情况:$b")
|
||||
// Global.isJpushConnected = b;
|
||||
// if (b && null != Global.VEHICLE_INFO && !Global.isAliasSuccess)
|
||||
// JPushInterface.setAlias(context, 5, AppUrlConfig.isRelease ? (Global.VEHICLE_INFO.vehicleId + "") : (Global.JPUSH_PREFIX + Global.VEHICLE_INFO.vehicleId));
|
||||
}
|
||||
|
||||
override fun onTagOperatorResult(context: Context, jPushMessage: JPushMessage) {
|
||||
// TagAliasOperatorHelper.getInstance().onTagOperatorResult(context, jPushMessage);
|
||||
super.onTagOperatorResult(context, jPushMessage)
|
||||
}
|
||||
|
||||
override fun onCheckTagOperatorResult(context: Context, jPushMessage: JPushMessage) {
|
||||
// TagAliasOperatorHelper.getInstance().onCheckTagOperatorResult(context, jPushMessage);
|
||||
super.onCheckTagOperatorResult(context, jPushMessage)
|
||||
}
|
||||
|
||||
override fun onAliasOperatorResult(context: Context, jPushMessage: JPushMessage) {
|
||||
// LogUtil.getInstance().print("极光 alias", "0->" + JSON.toJSONString(jPushMessage));
|
||||
// TagAliasOperatorHelper.getInstance().onAliasOperatorResult(context, jPushMessage);
|
||||
super.onAliasOperatorResult(context, jPushMessage)
|
||||
}
|
||||
|
||||
override fun onMobileNumberOperatorResult(context: Context, jPushMessage: JPushMessage) {
|
||||
// TagAliasOperatorHelper.getInstance().onMobileNumberOperatorResult(context, jPushMessage);
|
||||
super.onMobileNumberOperatorResult(context, jPushMessage)
|
||||
}
|
||||
|
||||
override fun onCommandResult(context: Context, cmdMessage: CmdMessage) {
|
||||
//注册失败+三方厂商注册回调
|
||||
LogUtil.print("极光 厂商", "[onCommandResult] $cmdMessage");
|
||||
//cmd为10000时说明为厂商token回调
|
||||
if (cmdMessage.cmd == 10000 && cmdMessage.extra != null) {
|
||||
val token = cmdMessage.extra.getString("token")
|
||||
val platform = cmdMessage.extra.getInt("platform")
|
||||
var deviceName = "unkown"
|
||||
when (platform) {
|
||||
1 -> deviceName = "小米"
|
||||
2 -> deviceName = "华为"
|
||||
3 -> deviceName = "魅族"
|
||||
4 -> deviceName = "OPPO"
|
||||
5 -> {
|
||||
deviceName = "VIVO"
|
||||
Log.e("厂商", "[onCommandResult] vivo:$token")
|
||||
}
|
||||
|
||||
8 -> deviceName = "FCM"
|
||||
}
|
||||
LogUtil.print("极光 厂商", "获取到 $deviceName 的token:$token");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.za.service.jpush
|
||||
|
||||
import cn.jpush.android.service.JCommonService
|
||||
|
||||
|
||||
class PushService : JCommonService()
|
@ -0,0 +1,492 @@
|
||||
package com.za.service.location
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.location.Geocoder
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import com.amap.api.location.AMapLocation
|
||||
import com.amap.api.location.AMapLocationClient
|
||||
import com.amap.api.location.AMapLocationClientOption
|
||||
import com.amap.api.location.AMapLocationListener
|
||||
import com.amap.api.services.core.LatLonPoint
|
||||
import com.amap.api.services.geocoder.GeocodeResult
|
||||
import com.amap.api.services.geocoder.GeocodeSearch
|
||||
import com.amap.api.services.geocoder.GeocodeSearch.OnGeocodeSearchListener
|
||||
import com.amap.api.services.geocoder.RegeocodeQuery
|
||||
import com.amap.api.services.geocoder.RegeocodeResult
|
||||
import com.blankj.utilcode.util.NetworkUtils
|
||||
import com.za.bean.request.UploadGpsRequest
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.ext.toJson
|
||||
import com.za.net.CommonMethod
|
||||
import com.za.service.mqtt.MyMqttClient
|
||||
import java.util.Locale
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object ZdLocationManager : AMapLocationListener {
|
||||
private const val TAG = "ZdLocationManager"
|
||||
private var aMapLocationClient : AMapLocationClient? = null
|
||||
private var context : Context? = null
|
||||
private var option : AMapLocationClientOption? = null
|
||||
|
||||
private const val NORMA_INTERVAL_TIME = 20000L // 普通的GPS上传时间间隔
|
||||
private const val LOCATION_CACHE_DURATION = 60 * 1000L // 1分钟的缓存时间
|
||||
private const val LOCATION_SEARCH_RADIUS = 200f
|
||||
private const val DEFAULT_TIMEOUT = 30000L
|
||||
|
||||
private var globalLocation : AMapLocation? = null
|
||||
private var lastSingleLocationTime = 0L
|
||||
|
||||
fun init(context : Context) {
|
||||
this.context = context.applicationContext
|
||||
initAMapLocationClient()
|
||||
}
|
||||
|
||||
private fun initAMapLocationClient() {
|
||||
context?.let { ctx ->
|
||||
try {
|
||||
AMapLocationClient.updatePrivacyShow(ctx, true, true)
|
||||
AMapLocationClient.updatePrivacyAgree(ctx, true)
|
||||
option = createLocationOption()
|
||||
aMapLocationClient = AMapLocationClient(ctx).apply {
|
||||
setLocationOption(option)
|
||||
setLocationListener(this@ZdLocationManager)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "初始化定位失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLocationOption(isOnceLocation : Boolean = false,
|
||||
isGpsFirst : Boolean = false,
|
||||
isNeedAddress : Boolean = false) : AMapLocationClientOption =
|
||||
AMapLocationClientOption().apply {
|
||||
locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy
|
||||
interval = NORMA_INTERVAL_TIME
|
||||
isWifiScan = true
|
||||
isLocationCacheEnable = false
|
||||
httpTimeOut = DEFAULT_TIMEOUT
|
||||
isMockEnable = false
|
||||
this.isNeedAddress = isNeedAddress
|
||||
this.isOnceLocation = isOnceLocation
|
||||
this.isGpsFirst = isGpsFirst
|
||||
}
|
||||
|
||||
fun startContinuousLocation(context : Context) {
|
||||
stopContinuousLocation()
|
||||
if (aMapLocationClient == null) {
|
||||
init(context)
|
||||
}
|
||||
try {
|
||||
aMapLocationClient?.startLocation()
|
||||
LogUtil.print(TAG, "持续定位开启成功")
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "持续定位开启失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
fun stopContinuousLocation() {
|
||||
try {
|
||||
aMapLocationClient?.stopLocation()
|
||||
LogUtil.print(TAG, "关闭持续定位成功")
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "关闭持续定位失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location : AMapLocation?) {
|
||||
if (location == null) {
|
||||
LogUtil.print(TAG, "定位获取失败: location is null")
|
||||
return
|
||||
}
|
||||
|
||||
if (location.errorCode != 0) {
|
||||
LogUtil.print(TAG, "定位获取失败: ${location.errorInfo}")
|
||||
return
|
||||
}
|
||||
|
||||
GlobalData.currentLocation = location
|
||||
uploadGpsRequest(location)
|
||||
}
|
||||
|
||||
private fun uploadGpsRequest(location : AMapLocation) {
|
||||
val request = UploadGpsRequest().apply {
|
||||
lat = location.latitude
|
||||
lng = location.longitude
|
||||
userId = GlobalData.driverInfo?.userId
|
||||
vehicleId = GlobalData.vehicleInfo?.vehicleId
|
||||
working = GlobalData.currentOrder != null
|
||||
direction = location.bearing.toDouble()
|
||||
speed = location.speed.toDouble()
|
||||
}
|
||||
|
||||
uploadGps(request)
|
||||
}
|
||||
|
||||
private fun uploadGps(uploadGpsRequest : UploadGpsRequest) {
|
||||
CommonMethod.uploadGps(uploadGpsRequest, success = {
|
||||
LogUtil.print(TAG, "定位上传成功: ${uploadGpsRequest.toJson()}")
|
||||
MyMqttClient.publishMessage() // if (ActivityUtils.getTopActivity()==null) {
|
||||
// AppUtils.launchApp(AppUtils.getAppPackageName())
|
||||
// }
|
||||
}, failed = { error ->
|
||||
LogUtil.print(TAG, "定位上传失败: $error $uploadGpsRequest")
|
||||
MyMqttClient.publishMessage()
|
||||
})
|
||||
}
|
||||
|
||||
fun getSingleLocation(success : (AMapLocation) -> Unit,
|
||||
failed : (String) -> Unit,
|
||||
isNeedAddress : Boolean = false) {
|
||||
globalLocation?.let { location ->
|
||||
if (System.currentTimeMillis() - lastSingleLocationTime < LOCATION_CACHE_DURATION && (! isNeedAddress || ! location.address.isNullOrBlank())) {
|
||||
LogUtil.print(TAG, "返回缓存位置")
|
||||
success(location)
|
||||
GlobalData.currentLocation = location
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
NetworkUtils.isAvailableAsync { isNetworkAvailable ->
|
||||
LogUtil.print(TAG, "网络状态: $isNetworkAvailable")
|
||||
if (isNetworkAvailable) {
|
||||
doGetSingleLocationOnline(isNeedAddress = isNeedAddress,
|
||||
success = success,
|
||||
failed = failed)
|
||||
} else {
|
||||
doGetSingleLocationGpsFromOriginal(success,
|
||||
failed) // doGetSingleLocationGps(success, failed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSingleLocationClient(isNeedAddress : Boolean,
|
||||
isGpsFirst : Boolean,
|
||||
locationCallback : (AMapLocation?) -> Unit) : AMapLocationClient? {
|
||||
return context?.let { ctx ->
|
||||
try {
|
||||
AMapLocationClient(ctx).apply {
|
||||
setLocationOption(createLocationOption(isOnceLocation = true,
|
||||
isGpsFirst = isGpsFirst,
|
||||
isNeedAddress = isNeedAddress))
|
||||
setLocationListener { location ->
|
||||
locationCallback(location)
|
||||
stopLocation()
|
||||
onDestroy()
|
||||
}
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "创建单次定位客户端失败: ${e.message}")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLocationResult(location : AMapLocation?,
|
||||
isNeedAddress : Boolean,
|
||||
success : (AMapLocation) -> Unit,
|
||||
failed : (String) -> Unit) {
|
||||
if (location == null) {
|
||||
failed("Location is null")
|
||||
LogUtil.print(TAG, "单次定位失败: location is null")
|
||||
return
|
||||
}
|
||||
|
||||
if (location.errorCode != 0) {
|
||||
failed("Error code: ${location.errorCode}, ${location.errorInfo}")
|
||||
LogUtil.print(TAG,
|
||||
"单次定位失败: errorCode=${location.errorCode}, errorInfo=${location.locationDetail}")
|
||||
return
|
||||
}
|
||||
|
||||
if (isNeedAddress && location.address.isNullOrBlank()) {
|
||||
nativeGeoCoder(location = location, success = { success(it) }, failed = { error ->
|
||||
LogUtil.print(TAG, "逆地理编码失败: $error")
|
||||
failed("位置获取失败")
|
||||
})
|
||||
} else {
|
||||
updateGlobalLocation(location)
|
||||
success(location)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateGlobalLocation(location : AMapLocation) {
|
||||
globalLocation = location
|
||||
lastSingleLocationTime = System.currentTimeMillis()
|
||||
GlobalData.currentLocation = location
|
||||
LogUtil.print(TAG, "单次定位结果: $location")
|
||||
}
|
||||
|
||||
private fun doGetSingleLocationOnline(isNeedAddress : Boolean,
|
||||
success : (AMapLocation) -> Unit,
|
||||
failed : (String) -> Unit) {
|
||||
val locationClient = createSingleLocationClient(isNeedAddress = isNeedAddress,
|
||||
isGpsFirst = false) { location ->
|
||||
handleLocationResult(location, isNeedAddress, success, failed)
|
||||
} ?: run {
|
||||
failed("Failed to create location client")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
locationClient.startLocation()
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "启动单次定位失败: ${e.message}")
|
||||
failed("Failed to start location: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun doGetSingleLocationGps(success : (AMapLocation) -> Unit,
|
||||
failed : (String) -> Unit) {
|
||||
val locationClient =
|
||||
createSingleLocationClient(isNeedAddress = false, isGpsFirst = true) { location ->
|
||||
handleLocationResult(location, false, success, failed)
|
||||
} ?: run {
|
||||
failed("Failed to create location client")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
locationClient.startLocation()
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "启动GPS定位失败: ${e.message}")
|
||||
failed("Failed to start GPS location: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun doGetSingleLocationGpsFromOriginal(success : (AMapLocation) -> Unit,
|
||||
failed : (String) -> Unit) {
|
||||
context?.let {
|
||||
try {
|
||||
val locationManager =
|
||||
ContextCompat.getSystemService(it, LocationManager::class.java)
|
||||
if (locationManager == null || ! LocationManagerCompat.isLocationEnabled(
|
||||
locationManager)
|
||||
) {
|
||||
failed("Location service is disabled")
|
||||
return@let
|
||||
}
|
||||
val lastKnownLocation =
|
||||
locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
|
||||
if (lastKnownLocation != null) {
|
||||
val gcj02Location =
|
||||
wgs84ToGcj02(lastKnownLocation.latitude, lastKnownLocation.longitude)
|
||||
val aMapLocation = AMapLocation("").apply {
|
||||
latitude = gcj02Location.latitude
|
||||
longitude = gcj02Location.longitude
|
||||
accuracy = lastKnownLocation.accuracy
|
||||
bearing = lastKnownLocation.bearing
|
||||
speed = lastKnownLocation.speed
|
||||
time = lastKnownLocation.time
|
||||
errorCode = 0
|
||||
}
|
||||
updateGlobalLocation(aMapLocation)
|
||||
success(aMapLocation)
|
||||
} else { // 如果没有最后位置,尝试请求单次更新
|
||||
requestSingleGpsUpdate(locationManager, success, failed)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
failed("Native GPS location failed: ${e.message}")
|
||||
}
|
||||
|
||||
} ?: failed("获取失败")
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun requestSingleGpsUpdate(locationManager : LocationManager,
|
||||
success : (AMapLocation) -> Unit,
|
||||
failed : (String) -> Unit) {
|
||||
val locationListener = object : android.location.LocationListener {
|
||||
override fun onLocationChanged(location : Location) {
|
||||
locationManager.removeUpdates(this)
|
||||
val gcj02 = wgs84ToGcj02(location.latitude, location.longitude)
|
||||
val aMapLocation = AMapLocation("").apply {
|
||||
latitude = gcj02.latitude
|
||||
longitude = gcj02.longitude
|
||||
accuracy = location.accuracy
|
||||
bearing = location.bearing
|
||||
speed = location.speed
|
||||
time = location.time
|
||||
errorCode = 0
|
||||
}
|
||||
updateGlobalLocation(aMapLocation)
|
||||
success(aMapLocation)
|
||||
}
|
||||
|
||||
override fun onProviderDisabled(provider : String) {
|
||||
locationManager.removeUpdates(this)
|
||||
failed("GPS provider disabled")
|
||||
}
|
||||
|
||||
override fun onProviderEnabled(provider : String) {}
|
||||
}
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
|
||||
0L,
|
||||
0f,
|
||||
ContextCompat.getMainExecutor(context !!),
|
||||
locationListener)
|
||||
} else {
|
||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
|
||||
0L,
|
||||
0f,
|
||||
locationListener)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
failed("Failed to request GPS update: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun geoCoder(aMapLocation : AMapLocation,
|
||||
success : (AMapLocation) -> Unit,
|
||||
failed : (String) -> Unit = {}) {
|
||||
try {
|
||||
val geocoderSearch = GeocodeSearch(context)
|
||||
geocoderSearch.setOnGeocodeSearchListener(object : OnGeocodeSearchListener {
|
||||
override fun onRegeocodeSearched(result : RegeocodeResult?, rCode : Int) {
|
||||
if (result == null || rCode != 1000) {
|
||||
failed("Geocode search failed: $rCode")
|
||||
return
|
||||
}
|
||||
|
||||
val address = result.regeocodeAddress?.formatAddress
|
||||
if (! address.isNullOrBlank()) {
|
||||
aMapLocation.address = address
|
||||
success(aMapLocation)
|
||||
} else {
|
||||
failed("Empty address returned")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGeocodeSearched(result : GeocodeResult?,
|
||||
rCode : Int) { // Not used in reverse geocoding
|
||||
}
|
||||
})
|
||||
|
||||
val point = LatLonPoint(aMapLocation.latitude, aMapLocation.longitude)
|
||||
val query = RegeocodeQuery(point, LOCATION_SEARCH_RADIUS, GeocodeSearch.AMAP)
|
||||
geocoderSearch.getFromLocationAsyn(query)
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "地理编码异常: ${e.message}")
|
||||
failed("Geocoding failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun nativeGeoCoder(location : AMapLocation,
|
||||
success : (AMapLocation) -> Unit,
|
||||
failed : (String) -> Unit) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context?.let { ctx ->
|
||||
try {
|
||||
val geocoder = Geocoder(ctx, Locale.getDefault())
|
||||
geocoder.getFromLocation(location.latitude,
|
||||
location.longitude,
|
||||
1) { addresses ->
|
||||
if (addresses.isNotEmpty()) {
|
||||
updateLocationWithNativeAddress(location, addresses[0])
|
||||
success(location)
|
||||
} else {
|
||||
failed("No address found")
|
||||
}
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "原生地理编码异常: ${e.message}")
|
||||
failed("Native geocoding failed: ${e.message}")
|
||||
}
|
||||
} ?: failed("Context is null")
|
||||
} else {
|
||||
context?.let { ctx ->
|
||||
try {
|
||||
val geocoder = Geocoder(ctx, Locale.getDefault())
|
||||
val addresses =
|
||||
geocoder.getFromLocation(location.latitude, location.longitude, 1)
|
||||
if (! addresses.isNullOrEmpty()) {
|
||||
updateLocationWithNativeAddress(location, addresses[0])
|
||||
success(location)
|
||||
} else {
|
||||
failed("No address found")
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "原生地理编码异常: ${e.message}")
|
||||
failed("Native geocoding failed: ${e.message}")
|
||||
}
|
||||
} ?: failed("Context is null")
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLocationWithNativeAddress(location : AMapLocation,
|
||||
address : android.location.Address) {
|
||||
location.address = buildAddressString(address)
|
||||
location.country = address.countryName
|
||||
location.province = address.adminArea
|
||||
location.city = address.locality
|
||||
location.district = address.subLocality
|
||||
location.street = address.thoroughfare
|
||||
}
|
||||
|
||||
private fun buildAddressString(address : android.location.Address) : String {
|
||||
return buildString {
|
||||
address.countryName?.let { append(it) }
|
||||
address.adminArea?.let { append(it) }
|
||||
address.locality?.let { append(it) }
|
||||
address.subLocality?.let { append(it) }
|
||||
address.thoroughfare?.let { append(it) }
|
||||
address.subThoroughfare?.let { append(it) }
|
||||
}
|
||||
}
|
||||
|
||||
// WGS84转GCJ02(火星坐标)
|
||||
private fun wgs84ToGcj02(lat : Double, lon : Double) : WGS84Coordinate {
|
||||
if (isOutOfChina(lat, lon)) {
|
||||
return WGS84Coordinate(lat, lon)
|
||||
}
|
||||
|
||||
var dLat = transformLat(lon - 105.0, lat - 35.0)
|
||||
var dLon = transformLon(lon - 105.0, lat - 35.0)
|
||||
|
||||
val radLat = lat / 180.0 * Math.PI
|
||||
var magic = sin(radLat)
|
||||
magic = 1 - 0.006693421622965943 * magic * magic
|
||||
|
||||
val sqrtMagic = sqrt(magic)
|
||||
dLat =
|
||||
(dLat * 180.0) / ((6378245.0 * (1 - 0.006693421622965943)) / (magic * sqrtMagic) * Math.PI)
|
||||
dLon = (dLon * 180.0) / (6378245.0 / sqrtMagic * cos(radLat) * Math.PI)
|
||||
|
||||
return WGS84Coordinate(latitude = lat + dLat, longitude = lon + dLon)
|
||||
}
|
||||
|
||||
data class WGS84Coordinate(val latitude : Double, val longitude : Double)
|
||||
|
||||
private fun transformLat(x : Double, y : Double) : Double {
|
||||
var ret = - 100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(abs(x))
|
||||
ret += (20.0 * sin(6.0 * x * Math.PI) + 20.0 * sin(2.0 * x * Math.PI)) * 2.0 / 3.0
|
||||
ret += (20.0 * sin(y * Math.PI) + 40.0 * sin(y / 3.0 * Math.PI)) * 2.0 / 3.0
|
||||
ret += (160.0 * sin(y / 12.0 * Math.PI) + 320 * sin(y * Math.PI / 30.0)) * 2.0 / 3.0
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun transformLon(x : Double, y : Double) : Double {
|
||||
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(abs(x))
|
||||
ret += (20.0 * sin(6.0 * x * Math.PI) + 20.0 * sin(2.0 * x * Math.PI)) * 2.0 / 3.0
|
||||
ret += (20.0 * sin(x * Math.PI) + 40.0 * sin(x / 3.0 * Math.PI)) * 2.0 / 3.0
|
||||
ret += (150.0 * sin(x / 12.0 * Math.PI) + 300.0 * sin(x / 30.0 * Math.PI)) * 2.0 / 3.0
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun isOutOfChina(lat : Double, lon : Double) : Boolean {
|
||||
return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.za.service.mqtt
|
||||
|
||||
import com.za.common.util.Tools.macSignature
|
||||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions
|
||||
|
||||
/**
|
||||
* A utility class that encapsulates the initialization parameters for the MQ4IOT client.
|
||||
*/
|
||||
class ConnectionOptionWrapper(
|
||||
instanceId: String,
|
||||
accessKey: String,
|
||||
clientId: String,
|
||||
secretKey: String? = null,
|
||||
tokenData: Map<String, String>? = null
|
||||
) {
|
||||
/**
|
||||
* Internal connection parameters.
|
||||
*/
|
||||
@JvmField
|
||||
val mqttConnectOptions: MqttConnectOptions = MqttConnectOptions().apply {
|
||||
isCleanSession = false
|
||||
keepAliveInterval = 90 // Keep alive interval in seconds
|
||||
isAutomaticReconnect = true
|
||||
mqttVersion = MqttConnectOptions.MQTT_VERSION_3_1_1
|
||||
connectionTimeout = 30 // Connection timeout in seconds
|
||||
}
|
||||
|
||||
init {
|
||||
if (tokenData != null) {
|
||||
mqttConnectOptions.userName = "Token|$accessKey|$instanceId"
|
||||
mqttConnectOptions.password = buildTokenPassword(tokenData)
|
||||
} else if (secretKey != null) {
|
||||
mqttConnectOptions.userName = "Signature|$accessKey|$instanceId"
|
||||
mqttConnectOptions.password = macSignature(clientId, secretKey).toCharArray()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the password for token authentication.
|
||||
*/
|
||||
private fun buildTokenPassword(tokenData: Map<String, String>): CharArray {
|
||||
val builder = StringBuilder()
|
||||
for ((key, value) in tokenData) {
|
||||
builder.append(key).append("|").append(value).append("|")
|
||||
}
|
||||
if (builder.isNotEmpty()) {
|
||||
builder.setLength(builder.length - 1) // Remove the last '|'
|
||||
}
|
||||
return builder.toString().toCharArray()
|
||||
}
|
||||
}
|
11
servicing/src/main/java/com/za/service/mqtt/MqttConfig.kt
Normal file
11
servicing/src/main/java/com/za/service/mqtt/MqttConfig.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package com.za.service.mqtt
|
||||
|
||||
object MqttConfig {
|
||||
const val INSTANCE_ID = "mqtt-cn-oew23jbkb1f"
|
||||
const val END_POINT = "mqtt-cn-oew23jbkb1f.mqtt.aliyuncs.com"
|
||||
const val ACCESS_KEY = "LTAI5tKgZ9ACKorXzzWLxgg7"
|
||||
const val SECRET_KEY = "D04F0UH2GzrDsYaJ9GTfULGPjcsvvz"
|
||||
const val GROUP_ID = "GID_ZDJY"
|
||||
const val TOPIC_PREFIX = "pushBaseTopic/p2p"
|
||||
const val QOS_LEVEL = 0
|
||||
}
|
127
servicing/src/main/java/com/za/service/mqtt/MyMqttClient.kt
Normal file
127
servicing/src/main/java/com/za/service/mqtt/MyMqttClient.kt
Normal file
@ -0,0 +1,127 @@
|
||||
package com.za.service.mqtt
|
||||
|
||||
import android.util.Log
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.service.ServiceManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken
|
||||
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient
|
||||
import org.eclipse.paho.client.mqttv3.MqttException
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage
|
||||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence
|
||||
|
||||
object MyMqttClient {
|
||||
private lateinit var clientId : String
|
||||
private lateinit var topic : String
|
||||
|
||||
private val mqttClient : MqttClient by lazy {
|
||||
MqttClient("tcp://${MqttConfig.END_POINT}:1883", clientId, MemoryPersistence())
|
||||
}
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private val _connectionState = MutableStateFlow(false)
|
||||
private val connectionState : StateFlow<Boolean> = _connectionState
|
||||
|
||||
fun initialize(deviceId : String?) {
|
||||
clientId = "${MqttConfig.GROUP_ID}@@@$deviceId"
|
||||
topic = "${MqttConfig.TOPIC_PREFIX}/$clientId"
|
||||
setupMqttCallbacks()
|
||||
connect()
|
||||
LogUtil.print("MyMqttClient ", "initialize success")
|
||||
Log.e("MyMqttClient ", "initialize success")
|
||||
}
|
||||
|
||||
private fun setupMqttCallbacks() {
|
||||
mqttClient.setCallback(object : MqttCallbackExtended {
|
||||
override fun connectComplete(reconnect : Boolean, serverURI : String) {
|
||||
val status = if (reconnect) "Reconnected" else "Connected"
|
||||
LogUtil.print("MyMqttClient ", "$status to: $serverURI")
|
||||
_connectionState.value = true
|
||||
subscribeTopic()
|
||||
}
|
||||
|
||||
override fun connectionLost(throwable : Throwable) {
|
||||
LogUtil.print("MyMqttClient ", "Connection lost: ${throwable.message}")
|
||||
_connectionState.value = false
|
||||
}
|
||||
|
||||
override fun messageArrived(topic : String, mqttMessage : MqttMessage) {
|
||||
val message = String(mqttMessage.payload)
|
||||
LogUtil.print("MyMqttClient ", "Message arrived: $message")
|
||||
ServiceManager.handlerPushMsg(message) // Pass the message to ServiceManager for processing
|
||||
}
|
||||
|
||||
override fun deliveryComplete(token : IMqttDeliveryToken) {
|
||||
LogUtil.print("MyMqttClient ", "Message delivery complete")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun connect() {
|
||||
if (connectionState.value && mqttClient.isConnected) {
|
||||
LogUtil.print("MyMqttClient ", "Already connected")
|
||||
return
|
||||
}
|
||||
|
||||
coroutineScope.launch {
|
||||
try {
|
||||
val options = ConnectionOptionWrapper(MqttConfig.INSTANCE_ID,
|
||||
MqttConfig.ACCESS_KEY,
|
||||
clientId,
|
||||
MqttConfig.SECRET_KEY).mqttConnectOptions
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
mqttClient.connect(options)
|
||||
}
|
||||
_connectionState.value = true // Update connection state after successful connection
|
||||
} catch (e : MqttException) {
|
||||
LogUtil.print("MyMqttClient ", "Connection failed: ${e.message}")
|
||||
_connectionState.value = false // Update connection state on failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//检测mqtt连接状态
|
||||
fun publishMessage() {
|
||||
if (mqttClient.isConnected) {
|
||||
LogUtil.print("MyMqttClient ", "publishMessage success")
|
||||
return
|
||||
}
|
||||
connect()
|
||||
}
|
||||
|
||||
private fun subscribeTopic() {
|
||||
coroutineScope.launch {
|
||||
try {
|
||||
mqttClient.subscribe(topic, MqttConfig.QOS_LEVEL)
|
||||
LogUtil.print("MyMqttClient ", "Subscribed to topic: $topic")
|
||||
} catch (e : MqttException) {
|
||||
LogUtil.print("MyMqttClient ", "Subscribe failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
coroutineScope.launch {
|
||||
try {
|
||||
if (connectionState.value) {
|
||||
mqttClient.disconnect()
|
||||
_connectionState.value = false
|
||||
LogUtil.print("MyMqttClient ", "Disconnected successfully")
|
||||
}
|
||||
} catch (e : MqttException) {
|
||||
LogUtil.print("MyMqttClient ", "Disconnect failed: ${e.message}")
|
||||
} finally {
|
||||
coroutineScope.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
76
servicing/src/main/java/com/za/signature/BaseActivity.java
Normal file
76
servicing/src/main/java/com/za/signature/BaseActivity.java
Normal file
@ -0,0 +1,76 @@
|
||||
package com.za.signature;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.util.StatusBarCompat;
|
||||
|
||||
|
||||
/***
|
||||
* Activity基类
|
||||
*
|
||||
* @since 2018-06-25
|
||||
* @author king
|
||||
*/
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
|
||||
protected View actionbar;
|
||||
protected TextView tvCancel;
|
||||
protected TextView tvSave;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getLayout());
|
||||
initTitleBar();
|
||||
initView();
|
||||
initData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化标题栏
|
||||
*/
|
||||
protected void initTitleBar() {
|
||||
actionbar = findViewById(R.id.actionbar);
|
||||
tvCancel = findViewById(R.id.tv_cancel);
|
||||
tvSave = findViewById(R.id.tv_ok);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取布局
|
||||
*/
|
||||
protected abstract int getLayout();
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
*/
|
||||
protected abstract void initView();
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
*/
|
||||
protected abstract void initData();
|
||||
|
||||
/**
|
||||
* 设置主题颜色
|
||||
*
|
||||
* @param color 主题颜色
|
||||
*/
|
||||
protected void setThemeColor(int color) {
|
||||
try {
|
||||
if (actionbar != null) {
|
||||
actionbar.setBackgroundColor(color);
|
||||
}
|
||||
StatusBarCompat.compat(this, color);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
441
servicing/src/main/java/com/za/signature/GridPaintActivity.java
Normal file
441
servicing/src/main/java/com/za/signature/GridPaintActivity.java
Normal file
@ -0,0 +1,441 @@
|
||||
package com.za.signature;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.text.Editable;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.config.PenConfig;
|
||||
import com.za.signature.util.BitmapUtil;
|
||||
import com.za.signature.util.DisplayUtil;
|
||||
import com.za.signature.util.SystemUtil;
|
||||
import com.za.signature.view.CircleImageView;
|
||||
import com.za.signature.view.CircleView;
|
||||
import com.za.signature.view.GridDrawable;
|
||||
import com.za.signature.view.GridPaintView;
|
||||
import com.za.signature.view.HVScrollView;
|
||||
import com.za.signature.view.HandWriteEditView;
|
||||
import com.za.signature.view.PaintSettingWindow;
|
||||
|
||||
|
||||
/***
|
||||
* 名称:GridWriteActivity<br>
|
||||
* 描述:米格输入界面
|
||||
* 最近修改时间:
|
||||
* @since 2017/11/14
|
||||
* @author king
|
||||
*/
|
||||
public class GridPaintActivity extends BaseActivity implements View.OnClickListener, Handler.Callback {
|
||||
|
||||
private View mCircleContainer;
|
||||
private HVScrollView mTextContainer;
|
||||
private HandWriteEditView mEditView;
|
||||
private CircleImageView mDeleteView;
|
||||
private CircleImageView mSpaceView;
|
||||
private CircleImageView mClearView;
|
||||
private CircleImageView mEnterView;
|
||||
private CircleView mPenCircleView;
|
||||
private GridPaintView mPaintView;
|
||||
private ProgressDialog mSaveProgressDlg;
|
||||
private static final int MSG_SAVE_SUCCESS = 1;
|
||||
private static final int MSG_SAVE_FAILED = 2;
|
||||
private static final int MSG_WRITE_OK = 100;
|
||||
private Handler mHandler;
|
||||
|
||||
private String mSavePath;
|
||||
private Editable cacheEditable;
|
||||
|
||||
private int bgColor;
|
||||
private boolean isCrop;
|
||||
private String format;
|
||||
private int lineSize;
|
||||
private int fontSize;
|
||||
|
||||
|
||||
private PaintSettingWindow settingWindow;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
bgColor = getIntent().getIntExtra("background", Color.TRANSPARENT);
|
||||
lineSize = getIntent().getIntExtra("lineLength", 15);
|
||||
fontSize = getIntent().getIntExtra("fontSize", 50);
|
||||
isCrop = getIntent().getBooleanExtra("crop", false);
|
||||
format = getIntent().getStringExtra("format");
|
||||
|
||||
PenConfig.PAINT_COLOR = PenConfig.getPaintColor(this);
|
||||
PenConfig.PAINT_SIZE_LEVEL = PenConfig.getPaintTextLevel(this);
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
super.onCreate(savedInstanceState);
|
||||
SystemUtil.disableShowInput(getApplicationContext(), mEditView);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 横竖屏切换
|
||||
*/
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
setContentView(R.layout.sign_activity_grid_paint);
|
||||
initTitleBar();
|
||||
initView();
|
||||
initData();
|
||||
SystemUtil.disableShowInput(getApplicationContext(), mEditView);
|
||||
|
||||
if (mEditView != null && cacheEditable != null) {
|
||||
mEditView.setText(cacheEditable);
|
||||
mEditView.setSelection(cacheEditable.length());
|
||||
mEditView.requestFocus();
|
||||
}
|
||||
mHandler = new Handler(this);
|
||||
if (settingWindow != null) {
|
||||
settingWindow.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayout() {
|
||||
return R.layout.sign_activity_grid_paint;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
setThemeColor(PenConfig.THEME_COLOR);
|
||||
mPenCircleView.setOutBorderColor(PenConfig.THEME_COLOR);
|
||||
|
||||
mClearView.setEnabled(false);
|
||||
mClearView.setImage(R.mipmap.sign_ic_clear, Color.LTGRAY);
|
||||
mEnterView.setImage(R.mipmap.sign_ic_enter, PenConfig.THEME_COLOR);
|
||||
mSpaceView.setImage(R.mipmap.sign_ic_space, PenConfig.THEME_COLOR);
|
||||
mDeleteView.setImage(R.mipmap.sign_ic_delete, PenConfig.THEME_COLOR);
|
||||
|
||||
mHandler = new Handler(this);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
*/
|
||||
@Override
|
||||
protected void initView() {
|
||||
mPaintView = findViewById(R.id.paint_view);
|
||||
mDeleteView = findViewById(R.id.delete);
|
||||
mSpaceView = findViewById(R.id.space);
|
||||
mPenCircleView = findViewById(R.id.pen_color);
|
||||
mClearView = findViewById(R.id.clear);
|
||||
mEnterView = findViewById(R.id.enter);
|
||||
mEditView = findViewById(R.id.et_view);
|
||||
mTextContainer = findViewById(R.id.sv_container);
|
||||
mCircleContainer = findViewById(R.id.circle_container);
|
||||
mEnterView.setOnClickListener(this);
|
||||
mDeleteView.setOnClickListener(this);
|
||||
mSpaceView.setOnClickListener(this);
|
||||
mPenCircleView.setOnClickListener(this);
|
||||
tvCancel.setOnClickListener(this);
|
||||
mClearView.setOnClickListener(this);
|
||||
tvSave.setOnClickListener(this);
|
||||
mPenCircleView.setPaintColor(PenConfig.PAINT_COLOR);
|
||||
mPenCircleView.setRadiusLevel(PenConfig.PAINT_SIZE_LEVEL);
|
||||
|
||||
int size = getResources().getDimensionPixelSize(R.dimen.sign_grid_size);
|
||||
GridDrawable gridDrawable = new GridDrawable(size, size, Color.WHITE);
|
||||
mPaintView.setBackground(gridDrawable);
|
||||
|
||||
mPaintView.setGetTimeListener(new GridPaintView.WriteListener() {
|
||||
@Override
|
||||
public void onWriteStart() {
|
||||
mHandler.removeMessages(MSG_WRITE_OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteCompleted(long time) {
|
||||
mHandler.sendEmptyMessageDelayed(MSG_WRITE_OK, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
int maxWidth = lineSize * DisplayUtil.dip2px(this, fontSize);
|
||||
if (!isCrop) {
|
||||
mEditView.setWidth(maxWidth + 2);
|
||||
mEditView.setMaxWidth(maxWidth);
|
||||
} else {
|
||||
mEditView.setWidth(maxWidth * 2 / 3);
|
||||
mEditView.setMaxWidth(maxWidth * 2 / 3);
|
||||
}
|
||||
mEditView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
|
||||
mEditView.setLineHeight(DisplayUtil.dip2px(this, fontSize));
|
||||
mEditView.setHorizontallyScrolling(false);
|
||||
mEditView.requestFocus();
|
||||
if (bgColor != Color.TRANSPARENT) {
|
||||
mTextContainer.setBackgroundColor(bgColor);
|
||||
}
|
||||
mEditView.addTextWatcher(s -> {
|
||||
if (s != null && s.length() > 0) {
|
||||
mClearView.setEnabled(true);
|
||||
mClearView.setImage(R.mipmap.sign_ic_clear, PenConfig.THEME_COLOR);
|
||||
} else {
|
||||
mClearView.setEnabled(false);
|
||||
mClearView.setImage(R.mipmap.sign_ic_clear, Color.LTGRAY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mHandler.removeMessages(MSG_WRITE_OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
mPaintView.release();
|
||||
super.onDestroy();
|
||||
mHandler.removeMessages(MSG_SAVE_FAILED);
|
||||
mHandler.removeMessages(MSG_SAVE_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
private void initSaveProgressDlg() {
|
||||
mSaveProgressDlg = new ProgressDialog(this);
|
||||
mSaveProgressDlg.setMessage("正在保存,请稍候...");
|
||||
mSaveProgressDlg.setCancelable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出清空提示
|
||||
*/
|
||||
private void showClearTips() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("提示")
|
||||
.setMessage("清空文本框内手写内容?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("确定", (dialog, which) -> {
|
||||
mEditView.setText("");
|
||||
mEditView.setSelection(0);
|
||||
cacheEditable = null;
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(@NonNull Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SAVE_FAILED:
|
||||
if (mSaveProgressDlg != null) {
|
||||
mSaveProgressDlg.dismiss();
|
||||
}
|
||||
Toast.makeText(getApplicationContext(), "保存失败", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case MSG_SAVE_SUCCESS:
|
||||
if (mSaveProgressDlg != null) {
|
||||
mSaveProgressDlg.dismiss();
|
||||
}
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(PenConfig.SAVE_PATH, mSavePath);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
break;
|
||||
case MSG_WRITE_OK:
|
||||
if (!mPaintView.isEmpty()) {
|
||||
Bitmap bitmap = mPaintView.buildBitmap(isCrop, DisplayUtil.dip2px(GridPaintActivity.this, fontSize));
|
||||
this.cacheEditable = mEditView.addBitmapToText(bitmap);
|
||||
mPaintView.reset();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
private void save() {
|
||||
if (mEditView.getText() == null || mEditView.getText().length() == 0) {
|
||||
Toast.makeText(getApplicationContext(), "没有写入任何文字", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
//先检查是否有存储权限
|
||||
// if (ContextCompat.checkSelfPermission(this,
|
||||
// Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Toast.makeText(getApplicationContext(), "没有读写存储的权限", Toast.LENGTH_SHORT).show();
|
||||
// return;
|
||||
// }
|
||||
if (mSaveProgressDlg == null) {
|
||||
initSaveProgressDlg();
|
||||
}
|
||||
mEditView.clearFocus();
|
||||
mEditView.setCursorVisible(false);
|
||||
|
||||
mSaveProgressDlg.show();
|
||||
new Thread(() -> {
|
||||
if (PenConfig.FORMAT_JPG.equals(format) && bgColor == Color.TRANSPARENT) {
|
||||
bgColor = Color.WHITE;
|
||||
}
|
||||
Bitmap bm = getWriteBitmap(bgColor);
|
||||
bm = BitmapUtil.clearBlank(bm, 20, bgColor);
|
||||
|
||||
if (bm == null) {
|
||||
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
|
||||
return;
|
||||
}
|
||||
mSavePath = BitmapUtil.saveImage(GridPaintActivity.this, bm, 100, format);
|
||||
if (mSavePath != null) {
|
||||
mHandler.obtainMessage(MSG_SAVE_SUCCESS).sendToTarget();
|
||||
} else {
|
||||
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
|
||||
}
|
||||
bm.recycle();
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取EdiText截图
|
||||
*
|
||||
* @param bgColor 背景颜色
|
||||
* @return EdiText截图
|
||||
*/
|
||||
private Bitmap getWriteBitmap(int bgColor) {
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
for (int i = 0; i < mTextContainer.getChildCount(); i++) {
|
||||
h += mTextContainer.getChildAt(i).getHeight();
|
||||
w += mTextContainer.getChildAt(i).getWidth();
|
||||
}
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
bitmap = Bitmap.createBitmap(w, h,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
} catch (OutOfMemoryError e) {
|
||||
bitmap = Bitmap.createBitmap(w, h,
|
||||
Bitmap.Config.ARGB_4444);
|
||||
}
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawColor(bgColor);
|
||||
mTextContainer.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int i = v.getId();
|
||||
if (i == R.id.delete) {
|
||||
this.cacheEditable = mEditView.deleteBitmapFromText();
|
||||
} else if (i == R.id.tv_cancel) {
|
||||
if (mEditView.getText() != null && mEditView.getText().length() > 0) {
|
||||
showQuitTip();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
} else if (i == R.id.tv_ok) {
|
||||
save();
|
||||
} else if (i == R.id.enter) {
|
||||
Editable editable = mEditView.getText();
|
||||
editable.insert(mEditView.getSelectionStart(), "\n");
|
||||
} else if (i == R.id.space) {
|
||||
mEditView.addSpace(fontSize);
|
||||
} else if (i == R.id.clear) {
|
||||
showClearTips();
|
||||
} else if (i == R.id.pen_color) {
|
||||
showSettingWindow();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 弹出画笔设置
|
||||
*/
|
||||
private void showSettingWindow() {
|
||||
settingWindow = new PaintSettingWindow(this);
|
||||
|
||||
settingWindow.setSettingListener(new PaintSettingWindow.OnSettingListener() {
|
||||
@Override
|
||||
public void onColorSetting(int color) {
|
||||
mPaintView.setPaintColor(color);
|
||||
mPenCircleView.setPaintColor(PenConfig.PAINT_COLOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeSetting(int level) {
|
||||
mPaintView.setPaintWidth(PaintSettingWindow.PEN_SIZES[level]);
|
||||
mPenCircleView.setRadiusLevel(level);
|
||||
}
|
||||
});
|
||||
|
||||
int[] location = new int[2];
|
||||
mCircleContainer.getLocationOnScreen(location);
|
||||
View contentView = settingWindow.getContentView();
|
||||
//需要先测量,PopupWindow还未弹出时,宽高为0
|
||||
contentView.measure(SystemUtil.makeDropDownMeasureSpec(settingWindow.getWidth()),
|
||||
SystemUtil.makeDropDownMeasureSpec(settingWindow.getHeight()));
|
||||
|
||||
int padding = DisplayUtil.dip2px(this, 10);
|
||||
int offsetX, offsetY;
|
||||
|
||||
Configuration config = getResources().getConfiguration();
|
||||
int smallestScreenWidth = config.smallestScreenWidthDp;
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && smallestScreenWidth >= 720) {
|
||||
//平板上横屏显示
|
||||
settingWindow.popAtBottomRight();
|
||||
settingWindow.showAsDropDown(mCircleContainer, mCircleContainer.getWidth() - settingWindow.getContentView().getMeasuredWidth() - padding, 10);
|
||||
} else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
//横屏显示
|
||||
offsetX = -settingWindow.getContentView().getMeasuredWidth() - padding;
|
||||
offsetY = -settingWindow.getContentView().getMeasuredHeight() - mCircleContainer.getHeight() / 2 + padding;
|
||||
settingWindow.popAtLeft();
|
||||
settingWindow.showAsDropDown(mCircleContainer, offsetX, offsetY);
|
||||
} else {
|
||||
//竖屏显示
|
||||
offsetX = 0;
|
||||
offsetY = -(settingWindow.getContentView().getMeasuredHeight() + mPenCircleView.getHeight() + 4 * padding);
|
||||
settingWindow.popAtTopLeft();
|
||||
settingWindow.showAsDropDown(mPenCircleView, offsetX, offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mEditView.getText() != null && mEditView.getText().length() > 0) {
|
||||
showQuitTip();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出退出提示
|
||||
*/
|
||||
private void showQuitTip() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("提示")
|
||||
.setMessage("当前文字未保存,是否退出?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("确定", (dialog, which) -> {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
|
||||
}
|
430
servicing/src/main/java/com/za/signature/PaintActivity.java
Normal file
430
servicing/src/main/java/com/za/signature/PaintActivity.java
Normal file
@ -0,0 +1,430 @@
|
||||
package com.za.signature;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.config.PenConfig;
|
||||
import com.za.signature.util.BitmapUtil;
|
||||
import com.za.signature.util.DisplayUtil;
|
||||
import com.za.signature.util.StatusBarCompat;
|
||||
import com.za.signature.util.SystemUtil;
|
||||
import com.za.signature.view.CircleView;
|
||||
import com.za.signature.view.PaintSettingWindow;
|
||||
import com.za.signature.view.PaintView;
|
||||
|
||||
|
||||
/**
|
||||
* 空白手写画板
|
||||
*
|
||||
* @author king
|
||||
* @since 2018/07/10 14:20
|
||||
*/
|
||||
public class PaintActivity extends BaseActivity implements View.OnClickListener, PaintView.StepCallback {
|
||||
|
||||
/**
|
||||
* 画布最大宽度
|
||||
*/
|
||||
public static final int CANVAS_MAX_WIDTH = 3000;
|
||||
/**
|
||||
* 画布最大高度
|
||||
*/
|
||||
public static final int CANVAS_MAX_HEIGHT = 3000;
|
||||
|
||||
private ImageView mHandView; //切换 滚动/手写
|
||||
private ImageView mUndoView;
|
||||
private ImageView mRedoView;
|
||||
private ImageView mPenView;
|
||||
private ImageView mClearView;
|
||||
private CircleView mSettingView;
|
||||
|
||||
private PaintView mPaintView;
|
||||
|
||||
private ProgressDialog mSaveProgressDlg;
|
||||
private static final int MSG_SAVE_SUCCESS = 1;
|
||||
private static final int MSG_SAVE_FAILED = 2;
|
||||
|
||||
private String mSavePath;
|
||||
private boolean hasSize = false;
|
||||
|
||||
private float mWidth;
|
||||
private float mHeight;
|
||||
private float widthRate = 1.0f;
|
||||
private float heightRate = 1.0f;
|
||||
private int bgColor;
|
||||
private boolean isCrop;
|
||||
private String format;
|
||||
|
||||
|
||||
private PaintSettingWindow settingWindow;
|
||||
|
||||
@Override
|
||||
protected int getLayout() {
|
||||
return R.layout.sign_activity_paint;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
View mCancelView = findViewById(R.id.tv_cancel);
|
||||
View mOkView = findViewById(R.id.tv_ok);
|
||||
|
||||
mPaintView = findViewById(R.id.paint_view);
|
||||
mHandView = findViewById(R.id.btn_hand);
|
||||
mUndoView = findViewById(R.id.btn_undo);
|
||||
mRedoView = findViewById(R.id.btn_redo);
|
||||
mPenView = findViewById(R.id.btn_pen);
|
||||
mClearView = findViewById(R.id.btn_clear);
|
||||
mSettingView = findViewById(R.id.btn_setting);
|
||||
mUndoView.setOnClickListener(this);
|
||||
mRedoView.setOnClickListener(this);
|
||||
mPenView.setOnClickListener(this);
|
||||
mClearView.setOnClickListener(this);
|
||||
mSettingView.setOnClickListener(this);
|
||||
mHandView.setOnClickListener(this);
|
||||
mCancelView.setOnClickListener(this);
|
||||
mOkView.setOnClickListener(this);
|
||||
|
||||
mPenView.setSelected(true);
|
||||
mUndoView.setEnabled(false);
|
||||
mRedoView.setEnabled(false);
|
||||
mClearView.setEnabled(!mPaintView.isEmpty());
|
||||
|
||||
mPaintView.setBackgroundColor(Color.WHITE);
|
||||
mPaintView.setStepCallback(this);
|
||||
|
||||
PenConfig.PAINT_SIZE_LEVEL = PenConfig.getPaintTextLevel(this);
|
||||
PenConfig.PAINT_COLOR = PenConfig.getPaintColor(this);
|
||||
|
||||
mSettingView.setPaintColor(PenConfig.PAINT_COLOR);
|
||||
mSettingView.setRadiusLevel(PenConfig.PAINT_SIZE_LEVEL);
|
||||
|
||||
setThemeColor(PenConfig.THEME_COLOR);
|
||||
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, PenConfig.THEME_COLOR);
|
||||
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_pen, PenConfig.THEME_COLOR);
|
||||
BitmapUtil.setImage(mRedoView, R.mipmap.sign_ic_redo, mPaintView.canRedo() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
BitmapUtil.setImage(mUndoView, R.mipmap.sign_ic_undo, mPaintView.canUndo() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, !mPaintView.isEmpty() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
// mSettingView.setOutBorderColor(PenConfig.THEME_COLOR);
|
||||
BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_hand, PenConfig.THEME_COLOR);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取画布默认宽度
|
||||
*
|
||||
*/
|
||||
private int getResizeWidth() {
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && dm.widthPixels < dm.heightPixels) {
|
||||
return (int) (dm.heightPixels * widthRate);
|
||||
}
|
||||
return (int) (dm.widthPixels * widthRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取画布默认高度
|
||||
*
|
||||
*/
|
||||
private int getResizeHeight() {
|
||||
int toolBarHeight = getResources().getDimensionPixelSize(R.dimen.sign_grid_toolbar_height);
|
||||
int actionbarHeight = getResources().getDimensionPixelSize(R.dimen.sign_actionbar_height);
|
||||
int statusBarHeight = StatusBarCompat.getStatusBarHeight(this);
|
||||
int otherHeight = toolBarHeight + actionbarHeight + statusBarHeight;
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && dm.widthPixels < dm.heightPixels) {
|
||||
return (int) ((dm.widthPixels - otherHeight) * heightRate);
|
||||
}
|
||||
return (int) ((dm.heightPixels - otherHeight) * heightRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
isCrop = getIntent().getBooleanExtra("crop", false);
|
||||
format = getIntent().getStringExtra("format");
|
||||
bgColor = getIntent().getIntExtra("background", Color.TRANSPARENT);
|
||||
String mInitPath = getIntent().getStringExtra("image");
|
||||
float bitmapWidth = getIntent().getFloatExtra("width", 1.0f);
|
||||
float bitmapHeight = getIntent().getFloatExtra("height", 1.0f);
|
||||
|
||||
if (bitmapWidth > 0 && bitmapWidth <= 1.0f) {
|
||||
widthRate = bitmapWidth;
|
||||
mWidth = getResizeWidth();
|
||||
} else {
|
||||
hasSize = true;
|
||||
mWidth = bitmapWidth;
|
||||
}
|
||||
if (bitmapHeight > 0 && bitmapHeight <= 1.0f) {
|
||||
heightRate = bitmapHeight;
|
||||
mHeight = getResizeHeight();
|
||||
} else {
|
||||
hasSize = true;
|
||||
mHeight = bitmapHeight;
|
||||
}
|
||||
if (mWidth > CANVAS_MAX_WIDTH) {
|
||||
Toast.makeText(getApplicationContext(), "画板宽度已超过" + CANVAS_MAX_WIDTH, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (mHeight > CANVAS_MAX_WIDTH) {
|
||||
Toast.makeText(getApplicationContext(), "画板高度已超过" + CANVAS_MAX_WIDTH, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
//初始画板设置
|
||||
if (!hasSize && !TextUtils.isEmpty(mInitPath)) {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(mInitPath);
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
hasSize = true;
|
||||
if (mWidth > CANVAS_MAX_WIDTH || mHeight > CANVAS_MAX_HEIGHT) {
|
||||
bitmap = BitmapUtil.zoomImg(bitmap, CANVAS_MAX_WIDTH, CANVAS_MAX_WIDTH);
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
}
|
||||
}
|
||||
mPaintView.init((int) mWidth, (int) mHeight, mInitPath);
|
||||
if (bgColor != Color.TRANSPARENT) {
|
||||
mPaintView.setBackgroundColor(bgColor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 横竖屏切换
|
||||
*/
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (settingWindow != null) {
|
||||
settingWindow.dismiss();
|
||||
}
|
||||
|
||||
int resizeWidth = getResizeWidth();
|
||||
int resizeHeight = getResizeHeight();
|
||||
if (mPaintView != null && !hasSize) {
|
||||
mPaintView.resize(mPaintView.getLastBitmap(), resizeWidth, resizeHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int i = v.getId();
|
||||
if (i == R.id.btn_setting) {
|
||||
showPaintSettingWindow();
|
||||
|
||||
} else if (i == R.id.btn_hand) {
|
||||
//切换是否允许写字
|
||||
mPaintView.setFingerEnable(!mPaintView.isFingerEnable());
|
||||
if (mPaintView.isFingerEnable()) {
|
||||
BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_hand, PenConfig.THEME_COLOR);
|
||||
} else {
|
||||
BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_drag, PenConfig.THEME_COLOR);
|
||||
}
|
||||
|
||||
} else if (i == R.id.btn_clear) {
|
||||
mPaintView.reset();
|
||||
|
||||
} else if (i == R.id.btn_undo) {
|
||||
mPaintView.undo();
|
||||
|
||||
} else if (i == R.id.btn_redo) {
|
||||
mPaintView.redo();
|
||||
|
||||
} else if (i == R.id.btn_pen) {
|
||||
if (!mPaintView.isEraser()) {
|
||||
mPaintView.setPenType(PaintView.TYPE_ERASER);
|
||||
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_eraser, PenConfig.THEME_COLOR);
|
||||
} else {
|
||||
mPaintView.setPenType(PaintView.TYPE_PEN);
|
||||
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_pen, PenConfig.THEME_COLOR);
|
||||
}
|
||||
} else if (i == R.id.tv_ok) {
|
||||
save();
|
||||
|
||||
} else if (i == R.id.tv_cancel) {
|
||||
if (!mPaintView.isEmpty()) {
|
||||
showQuitTip();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (mPaintView != null) {
|
||||
mPaintView.release();
|
||||
}
|
||||
if (mHandler != null) {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 弹出画笔设置
|
||||
*/
|
||||
private void showPaintSettingWindow() {
|
||||
settingWindow = new PaintSettingWindow(this);
|
||||
settingWindow.setSettingListener(new PaintSettingWindow.OnSettingListener() {
|
||||
@Override
|
||||
public void onColorSetting(int color) {
|
||||
mPaintView.setPaintColor(color);
|
||||
mSettingView.setPaintColor(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeSetting(int index) {
|
||||
mSettingView.setRadiusLevel(index);
|
||||
mPaintView.setPaintWidth(PaintSettingWindow.PEN_SIZES[index]);
|
||||
}
|
||||
});
|
||||
|
||||
View contentView = settingWindow.getContentView();
|
||||
//需要先测量,PopupWindow还未弹出时,宽高为0
|
||||
contentView.measure(SystemUtil.makeDropDownMeasureSpec(settingWindow.getWidth()),
|
||||
SystemUtil.makeDropDownMeasureSpec(settingWindow.getHeight()));
|
||||
|
||||
int padding = DisplayUtil.dip2px(this, 45);
|
||||
settingWindow.popAtTopRight();
|
||||
settingWindow.showAsDropDown(mSettingView, -250, -2 * padding - settingWindow.getContentView().getMeasuredHeight());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void initSaveProgressDlg() {
|
||||
mSaveProgressDlg = new ProgressDialog(this);
|
||||
mSaveProgressDlg.setMessage("正在保存,请稍候...");
|
||||
mSaveProgressDlg.setCancelable(false);
|
||||
}
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
private Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SAVE_FAILED:
|
||||
mSaveProgressDlg.dismiss();
|
||||
Toast.makeText(getApplicationContext(), "保存失败", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case MSG_SAVE_SUCCESS:
|
||||
mSaveProgressDlg.dismiss();
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(PenConfig.SAVE_PATH, mSavePath);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
private void save() {
|
||||
if (mPaintView.isEmpty()) {
|
||||
Toast.makeText(getApplicationContext(), "没有写入任何文字", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
//先检查是否有存储权限
|
||||
// if (ContextCompat.checkSelfPermission(this,
|
||||
// Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Toast.makeText(getApplicationContext(), "没有读写存储的权限", Toast.LENGTH_SHORT).show();
|
||||
// return;
|
||||
// }
|
||||
if (mSaveProgressDlg == null) {
|
||||
initSaveProgressDlg();
|
||||
}
|
||||
mSaveProgressDlg.show();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Bitmap result = mPaintView.buildAreaBitmap(isCrop);
|
||||
if (PenConfig.FORMAT_JPG.equals(format) && bgColor == Color.TRANSPARENT) {
|
||||
bgColor = Color.WHITE;
|
||||
}
|
||||
if (bgColor != Color.TRANSPARENT) {
|
||||
result = BitmapUtil.drawBgToBitmap(result, bgColor);
|
||||
}
|
||||
if (result == null) {
|
||||
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
|
||||
return;
|
||||
}
|
||||
mSavePath = BitmapUtil.saveImage(PaintActivity.this, result, 100, format);
|
||||
if (mSavePath != null) {
|
||||
mHandler.obtainMessage(MSG_SAVE_SUCCESS).sendToTarget();
|
||||
} else {
|
||||
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 画布有操作
|
||||
*/
|
||||
@Override
|
||||
public void onOperateStatusChanged() {
|
||||
mUndoView.setEnabled(mPaintView.canUndo());
|
||||
mRedoView.setEnabled(mPaintView.canRedo());
|
||||
mClearView.setEnabled(!mPaintView.isEmpty());
|
||||
|
||||
BitmapUtil.setImage(mRedoView, R.mipmap.sign_ic_redo, mPaintView.canRedo() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
BitmapUtil.setImage(mUndoView, R.mipmap.sign_ic_undo, mPaintView.canUndo() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, !mPaintView.isEmpty() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!mPaintView.isEmpty()) {
|
||||
showQuitTip();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出退出提示
|
||||
*/
|
||||
private void showQuitTip() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("提示")
|
||||
.setMessage("当前文字未保存,是否退出?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("确定", (dialog, which) -> {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user