feat(servicing): 添加客户语音通知功能
- 新增 CustomerSpeechManager 对象,用于处理文本转语音功能 - 添加 AppForegroundListener 接口和 BaseActivityLifecycleCallbacks 类,用于监听应用前后台切换- 更新 BaseActivity,使其支持推送消息 - 新增 ServicePeopleConfirmActivity 活动 - 优化订单处理逻辑,过滤掉已接受的订单 - 更新版本号至 1.0.1.9.9.12
This commit is contained in:
@ -27,7 +27,7 @@ class MainActivity : ComponentActivity() {
|
||||
.fillMaxSize()
|
||||
.clickable {
|
||||
val uri =
|
||||
"zd.assist://app?taskCode=ZD250425100361&driverName=宋志领&driverPhone=17630035658&rescueVehicle=沪88888".toUri()
|
||||
"zd.assist://app?taskCode=ZD250427100009&driverName=宋志领&driverPhone=17630035658&rescueVehicle=沪88888".toUri()
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ publishing {
|
||||
release(MavenPublication) {
|
||||
groupId = 'io.github.szl9'
|
||||
artifactId = 'zd_servicing'
|
||||
version = "1.0.1.9.9.4"
|
||||
version = "1.0.1.9.9.12"
|
||||
|
||||
pom {
|
||||
packaging = "aar"
|
||||
|
@ -80,6 +80,11 @@
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="24">
|
||||
|
||||
<activity
|
||||
android:name="com.za.ui.servicing.inservice_people_confirm.ServicePeopleConfirmActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Dealer" />
|
||||
<activity
|
||||
android:name="com.za.ui.main.ServiceLauncherActivity"
|
||||
android:exported="true"
|
||||
|
14
servicing/src/main/java/com/za/base/AppForegroundListener.kt
Normal file
14
servicing/src/main/java/com/za/base/AppForegroundListener.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package com.za.base
|
||||
|
||||
interface AppForegroundListener {
|
||||
|
||||
/**
|
||||
* APP处于 前台
|
||||
*/
|
||||
fun onAppForeground()
|
||||
|
||||
/**
|
||||
* APP处于 后台
|
||||
*/
|
||||
fun onAppBackground()
|
||||
}
|
@ -3,7 +3,6 @@ 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
|
||||
@ -12,7 +11,7 @@ import com.za.base.view.CommonDialog
|
||||
import com.za.base.view.LoadingManager
|
||||
import com.za.common.log.LogUtil
|
||||
|
||||
abstract class BaseActivity : AppCompatActivity() {
|
||||
abstract class BaseActivity : PushMessageActivity() {
|
||||
protected val TAG by lazy { javaClass.simpleName }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -0,0 +1,95 @@
|
||||
package com.za.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import com.za.call.CallLogManager
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
open class BaseActivityLifecycleCallbacks : ActivityLifecycleCallbacks {
|
||||
|
||||
override fun onActivityCreated(activity : Activity, savedInstanceState : Bundle?) {
|
||||
LogUtil.print("onActivityCreated", activity.javaClass.simpleName)
|
||||
keepScreenOn(activity)
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity : Activity) {
|
||||
LogUtil.print("onActivityStarted", activity.javaClass.simpleName)
|
||||
synchronized(this) {
|
||||
GlobalData.activityCount ++
|
||||
if (GlobalData.activityCount == 1) {
|
||||
isBackground.set(false)
|
||||
appForegroundListener?.onAppForeground()
|
||||
LogUtil.print("AppState", "切换到前台")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity : Activity) {
|
||||
LogUtil.print("onActivityResumed", activity.javaClass.simpleName)
|
||||
currentActivity = activity
|
||||
CallLogManager.uploadCallLog(activity.applicationContext)
|
||||
}
|
||||
|
||||
override fun onActivityPaused(activity : Activity) {
|
||||
LogUtil.print("onActivityPaused", activity.javaClass.simpleName)
|
||||
if (currentActivity == activity) {
|
||||
currentActivity = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity : Activity) {
|
||||
LogUtil.print("onActivityStopped", activity.javaClass.simpleName)
|
||||
synchronized(this) {
|
||||
GlobalData.activityCount --
|
||||
if (GlobalData.activityCount == 0) {
|
||||
isBackground.set(true)
|
||||
appForegroundListener?.onAppBackground()
|
||||
LogUtil.print("AppState", "切换到后台")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity : Activity, outState : Bundle) {
|
||||
LogUtil.print("onActivitySaveInstanceState", activity.javaClass.simpleName)
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(activity : Activity) {
|
||||
LogUtil.print("onActivityDestroyed", activity.javaClass.simpleName)
|
||||
}
|
||||
|
||||
private fun keepScreenOn(activity : Activity) { //保持屏幕不息屏
|
||||
activity.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var appForegroundListener : AppForegroundListener? = null
|
||||
|
||||
// 使用 AtomicBoolean 保证线程安全
|
||||
private val isBackground = AtomicBoolean(true)
|
||||
|
||||
// 当前显示的Activity
|
||||
private var currentActivity : Activity? = null
|
||||
|
||||
// 判断应用是否在后台
|
||||
fun isBackground() : Boolean = isBackground.get()
|
||||
|
||||
// 判断应用是否在前台
|
||||
fun isForeground() : Boolean = ! isBackground.get()
|
||||
|
||||
// 获取当前Activity
|
||||
fun getCurrentActivity() : Activity? = currentActivity
|
||||
|
||||
// 重置状态
|
||||
fun reset() {
|
||||
synchronized(BaseActivityLifecycleCallbacks::class.java) {
|
||||
isBackground.set(true)
|
||||
currentActivity = null
|
||||
appForegroundListener = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
207
servicing/src/main/java/com/za/base/PushMessageActivity.kt
Normal file
207
servicing/src/main/java/com/za/base/PushMessageActivity.kt
Normal file
@ -0,0 +1,207 @@
|
||||
package com.za.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.media.RingtoneManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.NotificationCompat
|
||||
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.speech.SpeechManager
|
||||
import com.za.servicing.R
|
||||
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity
|
||||
import com.za.ui.view.CommonDialogFragment
|
||||
|
||||
open class PushMessageActivity : AppCompatActivity() {
|
||||
private var currentDialog : AlertDialog? = null
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (! GlobalData.isMaster) {
|
||||
PushMessageLiveData.pushMessage.observe(this) { message -> // 处理推送消息
|
||||
LogUtil.print("PushMessageActivity", "Received push message: $message")
|
||||
handlePushMessage(msg = message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
dismissCurrentDialog()
|
||||
}
|
||||
|
||||
private fun handlePushMessage(msg : String) {
|
||||
if (msg.startsWith("broadcast:")) {
|
||||
handleBroadcast(msg)
|
||||
return
|
||||
}
|
||||
try {
|
||||
val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java)
|
||||
when (jpushOrderInfoBean.pushType) {
|
||||
1 -> handleTypeOneMessage(jpushOrderInfoBean)
|
||||
3 -> handleImportantTip(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}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleTypeOneMessage(jpushOrderBean : JpushBean) {
|
||||
when (jpushOrderBean.typeDesc) {
|
||||
"giveUp" -> handleGiveUpOrder(jpushOrderBean)
|
||||
"revoke" -> handleRevokeOrder()
|
||||
"reDispatch" -> handleReDispatchOrder(jpushOrderBean)
|
||||
else -> LogUtil.print("JpushMessage", "Unknown typeDesc: ${jpushOrderBean.typeDesc}")
|
||||
}
|
||||
}
|
||||
|
||||
// Handle broadcast messages
|
||||
private fun handleBroadcast(msg : String) {
|
||||
try {
|
||||
val content = msg.substring(10)
|
||||
sendNotification(GlobalData.application, content)
|
||||
LogUtil.print("JpushMessage", "Broadcast content: $content")
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("JpushMessage", "Broadcast failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun dismissCurrentDialog() {
|
||||
try {
|
||||
currentDialog?.dismiss()
|
||||
currentDialog = null
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("PushActivityLifecycleCallbacks", "关闭对话框失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleGiveUpOrder(jpushBean : JpushBean) { // 播放提示音
|
||||
playNotificationSound(this)
|
||||
|
||||
if (GlobalData.currentOrder != null && GlobalData.currentOrder?.taskId == jpushBean.taskId) {
|
||||
SpeechManager.playCurrentOrderCanceled()
|
||||
CommonDialogFragment(title = "订单放弃",
|
||||
message = buildGiveUpMessage(jpushBean),
|
||||
confirmText = "去拍照",
|
||||
cancelText = "我已了解",
|
||||
confirm = {
|
||||
OrderGiveUpActivity.goOrderGiveUpActivity(this,
|
||||
giveUpType = GIVE_UP_TYPE_NORMAL,
|
||||
taskId = jpushBean.taskId)
|
||||
}).show(this.supportFragmentManager, DIALOG_TAG_GIVE_UP)
|
||||
} else {
|
||||
SpeechManager.playOrderCanceled()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRevokeOrder() {
|
||||
playNotificationSound(this)
|
||||
SpeechManager.speech("订单被撤回") // 获取当前Activity进行处理
|
||||
this.finish()
|
||||
}
|
||||
|
||||
private fun handleReDispatchOrder(jpushBean : JpushBean) {
|
||||
playNotificationSound(this)
|
||||
currentDialog = AlertDialog.Builder(this).setTitle("订单重新派发")
|
||||
.setMessage(buildReDispatchMessage(jpushBean)).setCancelable(false)
|
||||
.setPositiveButton("确定") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}.show()
|
||||
|
||||
}
|
||||
|
||||
private fun handleImportantTip(jpushBean : JpushBean) {
|
||||
playNotificationSound(this)
|
||||
SpeechManager.speech("重要提醒:${jpushBean.tipContent ?: ""}")
|
||||
currentDialog =
|
||||
AlertDialog.Builder(this).setTitle("重要提醒").setMessage(jpushBean.tipContent)
|
||||
.setNegativeButton("我已了解") { dialog : DialogInterface, _ : Int -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun buildGiveUpMessage(jpushBean : JpushBean) : String {
|
||||
return buildString {
|
||||
append("该工单已放弃")
|
||||
jpushBean.taskCode?.let { append("\n\n订单号:$it") }
|
||||
jpushBean.address?.let { append("\n地址:$it") }
|
||||
append("\n\n是否需要拍放空照片?")
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildReDispatchMessage(jpushBean : JpushBean) : String {
|
||||
return buildString {
|
||||
append("该订单已重新派发")
|
||||
jpushBean.taskCode?.let { append("\n\n订单号:$it") }
|
||||
jpushBean.address?.let { append("\n地址:$it") }
|
||||
jpushBean.addressRemark?.let {
|
||||
if (it.isNotBlank()) {
|
||||
append("\n\n备注:$it")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun playNotificationSound(activity : Activity) {
|
||||
try {
|
||||
val notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
||||
RingtoneManager.getRingtone(activity, notification)?.play()
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("PushActivityLifecycleCallbacks", "播放提示音失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private val CHANNEL_ID = "ImportantMessagesChannel"
|
||||
private 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(NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
|
||||
// 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(NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val GIVE_UP_TYPE_NORMAL = 1
|
||||
internal const val DIALOG_TAG_GIVE_UP = "giveUp"
|
||||
}
|
||||
}
|
59
servicing/src/main/java/com/za/base/PushMessageLiveData.kt
Normal file
59
servicing/src/main/java/com/za/base/PushMessageLiveData.kt
Normal file
@ -0,0 +1,59 @@
|
||||
package com.za.base
|
||||
|
||||
import android.util.Log
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import cn.jpush.android.api.JPushMessage
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
object PushMessageLiveData {
|
||||
// 使用 SingleLiveEvent 或类似机制避免粘性事件问题
|
||||
private val _pushMessage = SingleLiveEvent<String>()
|
||||
|
||||
// 对外暴露不可变的 LiveData
|
||||
val pushMessage : LiveData<String> = _pushMessage
|
||||
|
||||
// 发送推送消息
|
||||
fun postPushMessage(message : String) {
|
||||
_pushMessage.postValue(message)
|
||||
}
|
||||
}
|
||||
|
||||
class SingleLiveEvent<T> : MutableLiveData<T>() {
|
||||
private val pending = AtomicBoolean(false)
|
||||
|
||||
@MainThread
|
||||
override fun observe(owner : LifecycleOwner, observer : Observer<in T>) {
|
||||
if (hasActiveObservers()) {
|
||||
Log.w(TAG, "多个观察者注册了 SingleLiveEvent,但只有一个会收到更新通知")
|
||||
}
|
||||
|
||||
// 观察 LiveData 内部值
|
||||
super.observe(owner) { t ->
|
||||
if (pending.compareAndSet(true, false)) {
|
||||
observer.onChanged(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
override fun setValue(t : T?) {
|
||||
pending.set(true)
|
||||
super.setValue(t)
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于主线程外调用
|
||||
*/
|
||||
override fun postValue(value : T) {
|
||||
pending.set(true)
|
||||
super.postValue(value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SingleLiveEvent"
|
||||
}
|
||||
}
|
@ -117,6 +117,7 @@ object GlobalData : GlobalLocalData() {
|
||||
currentLocation = null
|
||||
driverInfoBean = null
|
||||
loginTime = null
|
||||
isLoginRecognition = null
|
||||
}
|
||||
|
||||
fun clearAllOrderCache() {
|
||||
|
@ -5,6 +5,7 @@ import com.tencent.bugly.Bugly
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.za.base.AppConfig
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.common.speech.SpeechManager
|
||||
import com.za.room.RoomHelper
|
||||
import com.za.service.location.ZdLocationManager
|
||||
|
||||
@ -28,5 +29,6 @@ object ZDManager {
|
||||
LogUtil.init(application)
|
||||
RoomHelper.init(application)
|
||||
ZdLocationManager.init(application)
|
||||
SpeechManager.init(application)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,122 @@
|
||||
package com.za.common.speech
|
||||
|
||||
import android.media.MediaPlayer
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import com.za.common.log.LogUtil
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.ResponseBody
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Call
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.POST
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
interface SpeechApiService {
|
||||
@POST("v1/audio/speech")
|
||||
fun textToSpeech(@Body request: JsonObject): Call<ResponseBody>
|
||||
}
|
||||
|
||||
object CustomerSpeechManager {
|
||||
|
||||
private const val BASE_URL = "http://192.168.3.129:8880/"
|
||||
|
||||
private val gson: Gson by lazy {
|
||||
Gson().newBuilder()
|
||||
.setLenient()
|
||||
.create()
|
||||
}
|
||||
|
||||
private val retrofit: Retrofit by lazy {
|
||||
val loggingInterceptor = HttpLoggingInterceptor().apply {
|
||||
level = HttpLoggingInterceptor.Level.BODY
|
||||
}
|
||||
|
||||
val client = OkHttpClient.Builder()
|
||||
.addInterceptor(loggingInterceptor)
|
||||
.build()
|
||||
|
||||
Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.client(client)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
|
||||
.build()
|
||||
}
|
||||
|
||||
private val speechApiService: SpeechApiService by lazy {
|
||||
retrofit.create(SpeechApiService::class.java)
|
||||
}
|
||||
|
||||
fun textToSpeech(input: String, destinationFile: File): Boolean {
|
||||
val requestBody = JsonObject().apply {
|
||||
addProperty("input", input)
|
||||
addProperty("voice", "zf_xiaoxiao")
|
||||
addProperty("response_format", "mp3")
|
||||
addProperty("stream", true)
|
||||
addProperty("speed", 1)
|
||||
addProperty("return_download_link", false) // Set to false to get the stream directly
|
||||
addProperty("lang_code", "z")
|
||||
}
|
||||
|
||||
val call = speechApiService.textToSpeech(requestBody)
|
||||
try {
|
||||
val response = call.execute()
|
||||
LogUtil.print("CustomerSpeechManager", "response: $response")
|
||||
if (response.isSuccessful) {
|
||||
val responseBody = response.body()
|
||||
if (responseBody != null) {
|
||||
saveToFile(responseBody, destinationFile)
|
||||
playAudio(destinationFile)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
LogUtil.print("CustomerSpeechManager", "Request failed: ${response.code()}")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun saveToFile(body: ResponseBody, destinationFile: File) {
|
||||
body.byteStream().use {
|
||||
FileOutputStream(destinationFile).buffered().use { outputStream ->
|
||||
val buffer = ByteArray(4096)
|
||||
var bytesRead: Int
|
||||
while (it.read(buffer).also { bytesRead = it } != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun playAudio(file: File) {
|
||||
val mediaPlayer = MediaPlayer().apply {
|
||||
try {
|
||||
setDataSource(file.absolutePath)
|
||||
prepare()
|
||||
start()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
release()
|
||||
}
|
||||
}
|
||||
|
||||
mediaPlayer.setOnCompletionListener {
|
||||
it.release()
|
||||
}
|
||||
|
||||
mediaPlayer.setOnErrorListener { mp, what, extra ->
|
||||
mp.release()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
221
servicing/src/main/java/com/za/common/speech/SpeechManager.kt
Normal file
221
servicing/src/main/java/com/za/common/speech/SpeechManager.kt
Normal file
@ -0,0 +1,221 @@
|
||||
package com.za.common.speech
|
||||
|
||||
import android.app.Application
|
||||
import android.media.AudioManager
|
||||
import android.media.MediaPlayer
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.blankj.utilcode.util.ThreadUtils
|
||||
import com.za.base.AppConfig
|
||||
import com.za.bean.request.AppNewOrderVoiceRequest
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.net.BaseObserver
|
||||
import com.za.net.RetrofitHelper
|
||||
import com.za.room.RoomHelper
|
||||
import com.za.room.db.user.LocalResourceBean
|
||||
import com.za.servicing.R
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object SpeechManager {
|
||||
private var mContext : Application? = null
|
||||
|
||||
|
||||
fun init(context : Application?) {
|
||||
mContext = context //语音合成初始化|
|
||||
|
||||
TTSManager.init(context, object : OnTTSListener {
|
||||
override fun onTTSInitialized() { // TTS初始化成功,可以开始播放语音
|
||||
LogUtil.print("TTS", "TTS initialized successfully")
|
||||
}
|
||||
|
||||
override fun onTTSSuccess() {
|
||||
LogUtil.print("TTS", "TTS speech completed successfully")
|
||||
resetAudioVolume()
|
||||
}
|
||||
|
||||
override fun onTTSFailed(errorMessage : String?) {
|
||||
resetAudioVolume() // TTS初始化失败或语音播放失败,使用科大讯飞进行播放
|
||||
LogUtil.print("TTS", "TTS failed: $errorMessage")
|
||||
if (lastFailedText != null && ! lastFailedText.isNullOrBlank()) {
|
||||
speechKDXF(lastFailedText)
|
||||
lastFailedText = null
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private var lastFailedText : String? = null
|
||||
fun speech(msg : String?) {
|
||||
setMaxAudioVolume()
|
||||
lastFailedText = msg
|
||||
TTSManager.speak(msg)
|
||||
}
|
||||
|
||||
private fun speechKDXF(msg : String?) {
|
||||
|
||||
}
|
||||
|
||||
private var originVolume = 0
|
||||
private fun setMaxAudioVolume() {
|
||||
try {
|
||||
if (! AppConfig.isRelease) {
|
||||
return
|
||||
}
|
||||
val audioManager =
|
||||
ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java)
|
||||
val maxVolume = audioManager?.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC,
|
||||
maxVolume ?: 1,
|
||||
AudioManager.FLAG_PLAY_SOUND)
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("setMaxAudioVolume", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetAudioVolume() {
|
||||
try {
|
||||
if (! AppConfig.isRelease) {
|
||||
return
|
||||
}
|
||||
val audioManager =
|
||||
ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java)
|
||||
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC,
|
||||
originVolume,
|
||||
AudioManager.FLAG_PLAY_SOUND)
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("resetAudioVolume", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun stopSpeech() {
|
||||
TTSManager.stop()
|
||||
lastFailedText = null
|
||||
}
|
||||
|
||||
private var mediaPlayer : MediaPlayer? = null
|
||||
|
||||
// 当前订单被取消
|
||||
fun playCurrentOrderCanceled() {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.current_order_cancel)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
// 订单被取消
|
||||
fun playOrderCanceled() {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.cancel_order)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
// 面部居中
|
||||
fun playFaceCenter() {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.face_center)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
// 面部居中
|
||||
fun playFaceLeft() {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.face_left)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
// 面部居中
|
||||
fun playFaceRight() {
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.face_right)
|
||||
mediaPlayer?.start()
|
||||
}
|
||||
|
||||
private fun playNewOrder() {
|
||||
stopPlayMedia()
|
||||
setMaxAudioVolume()
|
||||
mediaPlayer = MediaPlayer.create(mContext, R.raw.neworder)
|
||||
mediaPlayer?.start()
|
||||
mediaPlayer?.setOnCompletionListener {
|
||||
resetAudioVolume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun playNewOrderFromNet(url : String) {
|
||||
try {
|
||||
stopPlayMedia()
|
||||
setMaxAudioVolume()
|
||||
mediaPlayer = MediaPlayer()
|
||||
mediaPlayer?.setAudioStreamType(AudioManager.STREAM_MUSIC) // 设置音频流类型
|
||||
mediaPlayer?.setDataSource(url) // 设置音频文件的 URL
|
||||
mediaPlayer?.prepareAsync() // 异步准备音频
|
||||
// 准备完成后开始播放
|
||||
mediaPlayer?.setOnPreparedListener { obj : MediaPlayer -> obj.start() }
|
||||
mediaPlayer?.setOnErrorListener { mp : MediaPlayer?, what : Int, extra : Int ->
|
||||
playNewOrder()
|
||||
false
|
||||
}
|
||||
mediaPlayer?.setOnCompletionListener {
|
||||
resetAudioVolume()
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
playNewOrder()
|
||||
LogUtil.print("播放新订单失败", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun stopPlayMedia() {
|
||||
ThreadUtils.runOnUiThread {
|
||||
if (null != mediaPlayer) {
|
||||
mediaPlayer?.stop()
|
||||
mediaPlayer?.release()
|
||||
mediaPlayer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun speechNewOrderSound(content : String?) {
|
||||
if (content.isNullOrBlank()) {
|
||||
speechNewOrderLooper("您有新的中道救援订单,请尽快接单!") { playNewOrder() }
|
||||
return
|
||||
}
|
||||
|
||||
val localResourceDao = RoomHelper.db?.localResourceDao()
|
||||
val localUrlResource = localResourceDao?.getLocalResourceByName(content)
|
||||
if (localUrlResource != null && ! localUrlResource.resourceUrl.isNullOrBlank()) {
|
||||
speechNewOrderLooper(content) {
|
||||
playNewOrderFromNet(localUrlResource.resourceUrl ?: "")
|
||||
}
|
||||
LogUtil.print("handlerNewOrderVoice", "播放本地语音");
|
||||
return
|
||||
}
|
||||
|
||||
val appNewOrderVoiceRequest = AppNewOrderVoiceRequest(content)
|
||||
RetrofitHelper.getDefaultService().getVoiceUrl(appNewOrderVoiceRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<String>() {
|
||||
override fun doSuccess(it : String?) {
|
||||
if (it == null) {
|
||||
speechNewOrderLooper(content) { playNewOrder() }
|
||||
return
|
||||
}
|
||||
localResourceDao?.insert(LocalResourceBean(resourceName = content,
|
||||
resourceType = 1,
|
||||
resourceUrl = it))
|
||||
speechNewOrderLooper(content) { playNewOrderFromNet(it) }
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
speechNewOrderLooper("您有新的中道救援订单,请尽快接单!") { playNewOrder() }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun speechNewOrderLooper(content : String, play : () -> Unit) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val startTime = System.currentTimeMillis()
|
||||
while (System.currentTimeMillis() - startTime < 1000 * 60 * 3 && GlobalData.isHandlerNewOrder == false) {
|
||||
play()
|
||||
delay(250L * content.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
servicing/src/main/java/com/za/common/speech/TTSManager.kt
Normal file
81
servicing/src/main/java/com/za/common/speech/TTSManager.kt
Normal file
@ -0,0 +1,81 @@
|
||||
package com.za.common.speech
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.speech.tts.TextToSpeech
|
||||
import android.speech.tts.UtteranceProgressListener
|
||||
import com.za.common.log.LogUtil
|
||||
import java.util.Locale
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object TTSManager {
|
||||
private var tts: TextToSpeech? = null
|
||||
private var context: Context? = null
|
||||
private var listener: OnTTSListener? = null
|
||||
|
||||
|
||||
fun init(context: Context?, onTTSListener: OnTTSListener) {
|
||||
this.context = context
|
||||
this.listener = onTTSListener
|
||||
initTTS()
|
||||
}
|
||||
|
||||
private fun initTTS() {
|
||||
tts = TextToSpeech(context) { status ->
|
||||
if (status == TextToSpeech.SUCCESS) {
|
||||
val result = tts?.setLanguage(Locale.getDefault())
|
||||
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
|
||||
LogUtil.print("TTS", "Language not supported")
|
||||
listener?.onTTSFailed("Language not supported")
|
||||
} else {
|
||||
LogUtil.print("TTS", "TTS initialized successfully")
|
||||
listener?.onTTSInitialized()
|
||||
}
|
||||
} else {
|
||||
LogUtil.print("TTS", "Initialization failed")
|
||||
listener?.onTTSFailed("Initialization failed")
|
||||
}
|
||||
}
|
||||
|
||||
tts?.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
|
||||
override fun onStart(utteranceId: String) {
|
||||
LogUtil.print("TTS", "Speech started")
|
||||
}
|
||||
|
||||
override fun onDone(utteranceId: String) {
|
||||
LogUtil.print("TTS", "Speech completed")
|
||||
listener?.onTTSSuccess()
|
||||
}
|
||||
|
||||
override fun onError(utteranceId: String) {
|
||||
LogUtil.print("TTS", "Speech error")
|
||||
listener?.onTTSFailed("Speech error")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun speak(text: String?) {
|
||||
if (tts != null && tts?.isSpeaking == false) {
|
||||
tts?.speak(text, TextToSpeech.QUEUE_FLUSH, null, "uniqueId")
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
tts?.takeIf { it.isSpeaking }?.stop()
|
||||
}
|
||||
|
||||
fun shutdown() {
|
||||
tts?.apply {
|
||||
stop()
|
||||
shutdown()
|
||||
}
|
||||
tts = null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface OnTTSListener {
|
||||
fun onTTSInitialized()
|
||||
fun onTTSSuccess()
|
||||
fun onTTSFailed(errorMessage: String?)
|
||||
}
|
@ -106,8 +106,8 @@ abstract class BaseObserver<T> : Observer<BaseResponse<T>> {
|
||||
ThreadUtils.runOnUiThread {
|
||||
try {
|
||||
ToastUtils.showShort("登陆信息已过期,请重新登录")
|
||||
GlobalData.clearUserCache()
|
||||
ZdLocationManager.stopContinuousLocation()
|
||||
GlobalData.clearUserCache()
|
||||
ActivityUtils.startLauncherActivity()
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print("handlerTokenExpired", e)
|
||||
|
@ -160,31 +160,34 @@ object CommonMethod {
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<JpushBean>() {
|
||||
override fun doSuccess(order : JpushBean?) {
|
||||
if (order == null) {
|
||||
return
|
||||
}
|
||||
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))
|
||||
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?) {
|
||||
@ -211,7 +214,8 @@ object CommonMethod {
|
||||
}
|
||||
|
||||
val inServicingOrder = it.find { it.isCurrent == true }
|
||||
val waitServiceOrders = it.filter { it.isCurrent == false }
|
||||
val waitServiceOrders =
|
||||
it.filter { it.isCurrent == false && it.taskState != "ACCEPT" }
|
||||
|
||||
if (inServicingOrder == null) {
|
||||
success(null, waitServiceOrders)
|
||||
|
@ -10,6 +10,7 @@ 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.base.PushMessageLiveData
|
||||
import com.za.bean.JpushBean
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
@ -17,19 +18,9 @@ import com.za.common.util.DeviceUtil
|
||||
import com.za.service.mqtt.MyMqttClient
|
||||
import com.za.servicing.R
|
||||
|
||||
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 : Int, val time : Long = System.nanoTime())
|
||||
|
||||
object ServiceManager {
|
||||
private var pushListener : PushListener? = null
|
||||
private var lastJPushBean : LastJPushBean? = null
|
||||
private const val DUPLICATE_MSG_THRESHOLD = 3000L // 3秒
|
||||
|
||||
@ -53,13 +44,6 @@ object ServiceManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Register push listener
|
||||
fun registerPushListener(listener : PushListener?) {
|
||||
this.pushListener = listener
|
||||
LogUtil.print("ServiceManager",
|
||||
"Registered push listener: ${pushListener?.javaClass?.simpleName}")
|
||||
}
|
||||
|
||||
// Handle incoming push messages
|
||||
@Synchronized
|
||||
fun handlerPushMsg(msg : String) {
|
||||
@ -75,93 +59,22 @@ object ServiceManager {
|
||||
|
||||
if (msg.startsWith("broadcast:")) {
|
||||
lastJPushBean = LastJPushBean(msg = msg.hashCode())
|
||||
handleBroadcast(msg)
|
||||
PushMessageLiveData.postPushMessage(msg)
|
||||
return
|
||||
}
|
||||
try {
|
||||
lastJPushBean = LastJPushBean(msg = msg.hashCode())
|
||||
val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java)
|
||||
sendSystemNotificationFromMessage(jpushOrderInfoBean)
|
||||
when (jpushOrderInfoBean.pushType) {
|
||||
0 -> newOrderMsg(jpushOrderInfoBean)
|
||||
1 -> handleTypeOneMessage(jpushOrderInfoBean)
|
||||
3 -> importantTip(jpushOrderInfoBean)
|
||||
else -> LogUtil.print("JpushMessage",
|
||||
"Unknown push type: ${jpushOrderInfoBean.pushType}")
|
||||
}
|
||||
PushMessageLiveData.postPushMessage(msg)
|
||||
} catch (e : Exception) {
|
||||
if (msg.startsWith("broadcast:")) {
|
||||
handleBroadcast(msg)
|
||||
PushMessageLiveData.postPushMessage(msg)
|
||||
}
|
||||
LogUtil.print("JpushMessage", "Error handling message: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Handle broadcast messages
|
||||
private fun handleBroadcast(msg : String) {
|
||||
try {
|
||||
val content = msg.substring(10)
|
||||
pushListener?.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 {
|
||||
LogUtil.print("JpushMessage",
|
||||
"Handling new order message: $pushListener ${pushListener?.javaClass?.simpleName}")
|
||||
pushListener?.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?.giveUpOrder(jpushOrderBean)
|
||||
}
|
||||
|
||||
// Handle revoke order messages
|
||||
private fun revokeOrder(jpushOrderBean : JpushBean) {
|
||||
pushListener?.revokeOrder(jpushOrderBean)
|
||||
}
|
||||
|
||||
// Handle re-dispatch order messages
|
||||
private fun reDispatchOrder(jpushOrderBean : JpushBean) {
|
||||
pushListener?.reDispatchOrder(jpushOrderBean)
|
||||
}
|
||||
|
||||
// Handle important tip messages
|
||||
private fun importantTip(jpushOrderBean : JpushBean) {
|
||||
pushListener?.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
|
||||
|
||||
@ -224,5 +137,15 @@ object ServiceManager {
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
// 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}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -98,6 +98,9 @@ object ZdLocationManager : AMapLocationListener {
|
||||
fun stopContinuousLocation() {
|
||||
try {
|
||||
aMapLocationClient?.stopLocation()
|
||||
aMapLocationClient?.onDestroy()
|
||||
aMapLocationClient?.unRegisterLocationListener(this)
|
||||
aMapLocationClient = null
|
||||
LogUtil.print(TAG, "关闭持续定位成功")
|
||||
} catch (e : Exception) {
|
||||
LogUtil.print(TAG, "关闭持续定位失败: ${e.message}")
|
||||
@ -134,6 +137,10 @@ object ZdLocationManager : AMapLocationListener {
|
||||
}
|
||||
|
||||
private fun uploadGps(uploadGpsRequest : UploadGpsRequest) {
|
||||
if (GlobalData.token.isNullOrBlank()) {
|
||||
LogUtil.print(TAG, "定位上传失败: token is null,request=$uploadGpsRequest")
|
||||
return
|
||||
}
|
||||
CommonMethod.uploadGps(uploadGpsRequest, success = {
|
||||
LogUtil.print(TAG, "定位上传成功: ${uploadGpsRequest.toJson()}")
|
||||
MyMqttClient.publishMessage() // if (ActivityUtils.getTopActivity()==null) {
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.za.ui.servicing.inservice_people_confirm
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.za.base.BaseActivity
|
||||
|
||||
|
||||
class ServicePeopleConfirmActivity : BaseActivity() {
|
||||
|
||||
@Composable
|
||||
override fun ContentView() {
|
||||
ServicePeopleConfirmScreen()
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
package com.za.ui.servicing.inservice_people_confirm
|
||||
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import com.za.base.IServicingVm
|
||||
import com.za.base.view.LoadingManager
|
||||
import com.za.bean.db.order.OrderInfo
|
||||
import com.za.bean.request.DriverFaceCompareRequest
|
||||
import com.za.bean.request.IaiCompareFaceBean
|
||||
import com.za.bean.request.UpdateTaskBean
|
||||
import com.za.bean.request.UpdateTaskRequest
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.ext.toJson
|
||||
import com.za.net.BaseObserver
|
||||
import com.za.net.CommonMethod
|
||||
import com.za.net.RetrofitHelper
|
||||
import com.za.service.location.ZdLocationManager
|
||||
import com.za.ui.servicing.inservice_people_confirm.InServicePeopleConfirmVm.Action
|
||||
import com.za.ui.servicing.inservice_people_confirm.InServicePeopleConfirmVm.Action.CompilePeople
|
||||
import com.za.ui.servicing.inservice_people_confirm.InServicePeopleConfirmVm.UiState
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.File
|
||||
|
||||
class InServicePeopleConfirmVm : IServicingVm<Action, UiState>() {
|
||||
private val _uiState = MutableStateFlow(UiState())
|
||||
val uiState get() = _uiState as StateFlow<UiState>
|
||||
override fun updateState(uiState : UiState) {
|
||||
_uiState.value = uiState
|
||||
}
|
||||
|
||||
override fun dispatch(action : Action) {
|
||||
when (action) {
|
||||
is Action.Init -> init()
|
||||
is Action.UpdateTask -> updateTask()
|
||||
is CompilePeople -> comparePeople(action.url)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun updateTask() {
|
||||
LoadingManager.showLoading()
|
||||
ZdLocationManager.getSingleLocation(success = {
|
||||
LoadingManager.hideLoading()
|
||||
val taskRequest = UpdateTaskRequest(type = "START",
|
||||
taskId = getCurrentOrder()?.taskId,
|
||||
userId = GlobalData.driverInfoBean?.userId,
|
||||
vehicleId = GlobalData.driverInfoBean?.vehicleId,
|
||||
currentState = getCurrentOrder()?.taskState,
|
||||
offlineMode = 0,
|
||||
operateTime = System.currentTimeMillis().toString(),
|
||||
lat = it.latitude,
|
||||
lng = it.longitude)
|
||||
doUploadTask(request = taskRequest)
|
||||
}, failed = {
|
||||
LoadingManager.hideLoading()
|
||||
ToastUtils.showShort(it)
|
||||
})
|
||||
}
|
||||
|
||||
private fun doUploadTask(request : UpdateTaskRequest) {
|
||||
LoadingManager.showLoading()
|
||||
CommonMethod.updateTask(request, success = { data ->
|
||||
LoadingManager.hideLoading()
|
||||
updateOrder(getCurrentOrder()?.copy(taskState = data?.nextState))
|
||||
updateState(uiState.value.copy(goNextPage = data, orderInfo = getCurrentOrder()))
|
||||
}, failed = { msg, _ ->
|
||||
LoadingManager.hideLoading()
|
||||
ToastUtils.showShort(msg)
|
||||
LogUtil.print("$tag doUploadTask", "状态更新失败==${request.toJson()} msg==$msg")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
private fun comparePeople(url : String?) {
|
||||
if (url.isNullOrBlank()) {
|
||||
ToastUtils.showLong("图片路径为空!")
|
||||
return
|
||||
}
|
||||
LoadingManager.showLoading()
|
||||
CommonMethod.uploadImage(file = File(url), success = { it ->
|
||||
LoadingManager.hideLoading()
|
||||
doComparePeople(it)
|
||||
}, failed = {
|
||||
LoadingManager.hideLoading()
|
||||
updateState(uiState.value.copy(showCompareFailedDialog = "$it", compareResult = false))
|
||||
})
|
||||
}
|
||||
|
||||
private fun doComparePeople(url : String?) {
|
||||
val driverFaceCompareRequest =
|
||||
DriverFaceCompareRequest(vehicleId = GlobalData.driverInfoBean?.vehicleId,
|
||||
driverId = GlobalData.driverInfoBean?.userId,
|
||||
photoUrl = url)
|
||||
LoadingManager.showLoading()
|
||||
RetrofitHelper.getDefaultService().iaiCompareFace(driverFaceCompareRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<IaiCompareFaceBean>() {
|
||||
override fun doSuccess(it : IaiCompareFaceBean?) {
|
||||
LoadingManager.hideLoading()
|
||||
if (it?.compareResult == true) {
|
||||
updateState(uiState.value.copy(compareResult = true))
|
||||
LogUtil.print("face", "人脸对比成功:$it")
|
||||
return
|
||||
}
|
||||
updateState(uiState.value.copy(showCompareFailedDialog = "人脸对比失败",
|
||||
compareResult = false))
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
LoadingManager.hideLoading()
|
||||
updateState(uiState.value.copy(showCompareFailedDialog = "$msg",
|
||||
compareResult = false))
|
||||
LogUtil.print("face", "人脸对比失败:$msg")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
|
||||
}
|
||||
|
||||
sealed class Action {
|
||||
object Init : Action()
|
||||
data class CompilePeople(val url : String? = null) : Action()
|
||||
object UpdateTask : Action()
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
val orderInfo : OrderInfo? = null,
|
||||
val goNextPage : UpdateTaskBean? = null,
|
||||
val compareResult : Boolean? = null,
|
||||
val showCompareFailedDialog : String? = null,
|
||||
)
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
package com.za.ui.servicing.inservice_people_confirm
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
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.material3.Scaffold
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import com.za.base.view.CommonButton
|
||||
import com.za.base.view.CommonDialog
|
||||
import com.za.base.view.HeadView
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.ext.goNextPage
|
||||
import com.za.servicing.R
|
||||
import com.za.ui.camera.ZdCameraXActivity
|
||||
|
||||
@Composable
|
||||
fun ServicePeopleConfirmScreen(vm : InServicePeopleConfirmVm = viewModel(),
|
||||
success : () -> Unit = {}) {
|
||||
val context = LocalContext.current
|
||||
val uiState = vm.uiState.collectAsStateWithLifecycle()
|
||||
val getResult =
|
||||
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { it ->
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
val value = it.data?.getStringExtra("path")
|
||||
LogUtil.print("takePhoto", "path==$value")
|
||||
if (value.isNullOrBlank()) {
|
||||
ToastUtils.showLong("照片路径为空,请重新拍摄!")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
vm.dispatch(InServicePeopleConfirmVm.Action.CompilePeople(value))
|
||||
}
|
||||
}
|
||||
|
||||
if (uiState.value.compareResult == true) {
|
||||
vm.updateState(uiState.value.copy(compareResult = null))
|
||||
if (GlobalData.isMaster) {
|
||||
success()
|
||||
} else {
|
||||
vm.dispatch(InServicePeopleConfirmVm.Action.UpdateTask)
|
||||
}
|
||||
}
|
||||
|
||||
if (uiState.value.goNextPage != null) {
|
||||
goNextPage(uiState.value.goNextPage?.nextState, context)
|
||||
vm.updateState(uiState.value.copy(goNextPage = null, compareResult = null))
|
||||
}
|
||||
|
||||
|
||||
if (! uiState.value.showCompareFailedDialog.isNullOrBlank()) {
|
||||
CommonDialog(title = uiState.value.showCompareFailedDialog,
|
||||
confirmText = "重新认证",
|
||||
confirm = {
|
||||
vm.updateState(uiState.value.copy(showCompareFailedDialog = null))
|
||||
val intent = Intent(context, ZdCameraXActivity::class.java)
|
||||
intent.putExtra("isBack", false)
|
||||
getResult.launch(intent)
|
||||
},
|
||||
cancelText = "关闭",
|
||||
cancel = {
|
||||
vm.updateState(uiState.value.copy(showCompareFailedDialog = null))
|
||||
},
|
||||
dismiss = {
|
||||
vm.updateState(uiState.value.copy(showCompareFailedDialog = null))
|
||||
})
|
||||
}
|
||||
|
||||
Scaffold(topBar = { HeadView(title = "身份验证") }) {
|
||||
Column(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = Color.White)
|
||||
.padding(it),
|
||||
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Spacer(modifier = Modifier.height(60.dp))
|
||||
|
||||
Box {
|
||||
Text(text = "为了确保服务技师信息的准确",
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.DarkGray,
|
||||
fontSize = 18.sp)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Box {
|
||||
Text(text = "需要进行人脸核验",
|
||||
color = Color.Gray,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 14.sp)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
|
||||
|
||||
AsyncImage(model = R.drawable.sv_facea_verify,
|
||||
contentDescription = "",
|
||||
modifier = Modifier
|
||||
.background(color = Color.White)
|
||||
.padding(30.dp))
|
||||
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center) {
|
||||
AsyncImage(model = R.drawable.sv_face_light,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(20.dp))
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Text(text = "保持光线充足", fontSize = 10.sp, color = Color.Gray)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(30.dp))
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center) {
|
||||
AsyncImage(model = R.drawable.sv_face_phone,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(20.dp))
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Text(text = "需正对手机", fontSize = 10.sp, color = Color.Gray)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(30.dp))
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center) {
|
||||
AsyncImage(model = R.drawable.sv_face_face,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(20.dp))
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Text(text = "确保面部无遮挡", fontSize = 10.sp, color = Color.Gray)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(60.dp))
|
||||
CommonButton(text = "开始核验", onClick = {
|
||||
val intent = Intent(context, ZdCameraXActivity::class.java)
|
||||
intent.putExtra("isBack", false)
|
||||
getResult.launch(intent)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,8 @@
|
||||
package com.za.ui.servicing.wait_to_start
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -63,14 +59,14 @@ import com.za.base.BaseActivity
|
||||
import com.za.base.theme.headBgColor
|
||||
import com.za.base.view.CommonDialog
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.common.util.ImageUtil
|
||||
import com.za.common.util.ServicingSpeechManager
|
||||
import com.za.ext.copy
|
||||
import com.za.ext.finish
|
||||
import com.za.ext.goNextPage
|
||||
import com.za.ext.navigationActivity
|
||||
import com.za.servicing.R
|
||||
import com.za.ui.camera.ZdCameraXActivity
|
||||
import com.za.ui.servicing.inservice_people_confirm.ServicePeopleConfirmActivity
|
||||
import com.za.ui.servicing.view.InServicingHeadView
|
||||
|
||||
|
||||
@ -89,19 +85,6 @@ fun WaitToStartScreen(vm : WaitToStartVm = viewModel()) {
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val mapView = remember { MapView(context) }
|
||||
|
||||
val getResult =
|
||||
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { it ->
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
val value = it.data?.getStringExtra("path")
|
||||
LogUtil.print("takePhoto", "path==$value")
|
||||
if (value.isNullOrBlank()) {
|
||||
vm.updateState(uiState.value.copy(comparableFailedStr = "照片路径为空,请重新认证!"))
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
vm.dispatch(WaitToStartVm.Action.CompareServicePeople(value))
|
||||
}
|
||||
}
|
||||
|
||||
// 添加 BottomSheet 状态
|
||||
val bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.Expanded)
|
||||
val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
|
||||
@ -166,27 +149,7 @@ fun WaitToStartScreen(vm : WaitToStartVm = viewModel()) {
|
||||
confirm = {
|
||||
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
|
||||
showServicePeopleConfirmDialog = false)))
|
||||
val intent = Intent(context, ZdCameraXActivity::class.java)
|
||||
intent.putExtra("isBack", false)
|
||||
getResult.launch(intent)
|
||||
})
|
||||
}
|
||||
|
||||
if (! uiState.value.comparableFailedStr.isNullOrBlank()) {
|
||||
CommonDialog(title = uiState.value.comparableFailedStr,
|
||||
confirmText = "重新核验",
|
||||
confirm = {
|
||||
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(comparableFailedStr = null)))
|
||||
val intent = Intent(context, ZdCameraXActivity::class.java)
|
||||
intent.putExtra("isBack", false)
|
||||
getResult.launch(intent)
|
||||
},
|
||||
cancelText = "关闭",
|
||||
cancel = {
|
||||
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(comparableFailedStr = null)))
|
||||
},
|
||||
dismiss = {
|
||||
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(comparableFailedStr = null)))
|
||||
context.navigationActivity(ServicePeopleConfirmActivity::class.java, true)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.za.ui.servicing.wait_to_start
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.amap.api.maps.AMapUtils
|
||||
import com.amap.api.maps.model.BitmapDescriptorFactory
|
||||
import com.amap.api.maps.model.LatLng
|
||||
@ -15,27 +14,16 @@ import com.blankj.utilcode.util.ToastUtils
|
||||
import com.za.base.IServicingVm
|
||||
import com.za.base.view.LoadingManager
|
||||
import com.za.bean.db.order.OrderInfo
|
||||
import com.za.bean.request.DriverFaceCompareRequest
|
||||
import com.za.bean.request.IaiCompareFaceBean
|
||||
import com.za.bean.request.UpdateTaskBean
|
||||
import com.za.bean.request.UpdateTaskRequest
|
||||
import com.za.common.GlobalData
|
||||
import com.za.common.log.LogUtil
|
||||
import com.za.ext.toJson
|
||||
import com.za.net.BaseObserver
|
||||
import com.za.net.CommonMethod
|
||||
import com.za.net.RetrofitHelper
|
||||
import com.za.service.location.ZdLocationManager
|
||||
import com.za.servicing.R
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
@ -55,7 +43,6 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
|
||||
is Action.UpdateState -> updateState(action.uiState)
|
||||
is Action.StartTimer -> startTimer()
|
||||
is Action.UpdateTimer -> updateTimer()
|
||||
is Action.CompareServicePeople -> compareServicePeople(action.localPath)
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,52 +218,6 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
|
||||
private fun updateTimer() { // 在这里处理倒计时更新逻辑
|
||||
}
|
||||
|
||||
private fun compareServicePeople(localPath : String) {
|
||||
LoadingManager.showLoading()
|
||||
CommonMethod.uploadImage(file = File(localPath), success = { it ->
|
||||
compilePeople(it, success = {
|
||||
LoadingManager.hideLoading()
|
||||
updateTask()
|
||||
}, failed = {
|
||||
LoadingManager.hideLoading()
|
||||
updateState(uiState.value.copy(comparableFailedStr = it))
|
||||
})
|
||||
}, failed = {
|
||||
LoadingManager.hideLoading()
|
||||
updateState(uiState.value.copy(comparableFailedStr = it))
|
||||
})
|
||||
}
|
||||
|
||||
private fun compilePeople(url : String?,
|
||||
success : (IaiCompareFaceBean?) -> Unit,
|
||||
failed : (String) -> Unit) {
|
||||
if (url.isNullOrBlank()) {
|
||||
ToastUtils.showLong("图片路径为空!")
|
||||
return
|
||||
}
|
||||
val driverFaceCompareRequest =
|
||||
DriverFaceCompareRequest(vehicleId = GlobalData.driverInfoBean?.vehicleId,
|
||||
driverId = GlobalData.driverInfoBean?.userId,
|
||||
photoUrl = url)
|
||||
RetrofitHelper.getDefaultService().iaiCompareFace(driverFaceCompareRequest)
|
||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BaseObserver<IaiCompareFaceBean>() {
|
||||
override fun doSuccess(it : IaiCompareFaceBean?) {
|
||||
if (it?.compareResult == true) {
|
||||
success(it)
|
||||
return
|
||||
}
|
||||
failed("人脸对比失败")
|
||||
LogUtil.print("face", "人脸对比成功:$it")
|
||||
}
|
||||
|
||||
override fun doFailure(code : Int, msg : String?) {
|
||||
failed(msg ?: "人脸对比失败")
|
||||
LogUtil.print("face", "人脸对比失败:$msg")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
timerJob?.cancel()
|
||||
@ -288,7 +229,6 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
|
||||
data class UpdateState(val uiState : UiState) : Action()
|
||||
data object StartTimer : Action()
|
||||
data object UpdateTimer : Action()
|
||||
data class CompareServicePeople(val localPath : String) : Action()
|
||||
}
|
||||
|
||||
data class UiState(val orderInfo : OrderInfo? = null,
|
||||
@ -299,6 +239,5 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
|
||||
val routePoints : List<LatLng>? = null,
|
||||
val remainingDistance : Float = 0f,
|
||||
val showServicePeopleConfirmDialog : Boolean? = false,
|
||||
val comparableFailedStr : String? = null,
|
||||
val estimatedArrivalTime : String = "")
|
||||
}
|
25
servicing/src/main/res/drawable/sv_face_face.xml
Normal file
25
servicing/src/main/res/drawable/sv_face_face.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19.824,19.321C23.983,15.161 23.982,8.418 19.823,4.26C15.664,0.101 8.921,0.101 4.762,4.26C0.603,8.418 0.603,15.161 4.762,19.321C6.759,21.318 9.468,22.44 12.293,22.44C15.118,22.44 17.827,21.318 19.824,19.321L19.824,19.321Z"
|
||||
android:strokeWidth="1.3"
|
||||
android:fillColor="#00000000"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#2C2C2C"/>
|
||||
<path
|
||||
android:pathData="M7.585,14.291C7.821,14.716 9.15,16.728 12.291,16.728C15.432,16.728 16.704,14.746 17.1,14.291"
|
||||
android:strokeWidth="1.3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF4200"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M9.62,9.759C9.62,10.421 9.085,10.957 8.423,10.957C7.76,10.957 7.224,10.424 7.224,9.759C7.224,9.093 7.76,8.561 8.423,8.561C9.085,8.561 9.62,9.097 9.62,9.759ZM17.573,9.759C17.573,10.421 17.037,10.957 16.375,10.957C15.713,10.957 15.176,10.421 15.176,9.759C15.176,9.096 15.712,8.561 16.375,8.561C17.039,8.561 17.573,9.097 17.573,9.759Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#352B2B"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
54
servicing/src/main/res/drawable/sv_face_light.xml
Normal file
54
servicing/src/main/res/drawable/sv_face_light.xml
Normal file
@ -0,0 +1,54 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M7.329,17.2L16.982,17.2L7.329,17.2Z"
|
||||
android:strokeWidth="1.3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2C2C2C"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M8.359,23.798L15.776,23.798"
|
||||
android:strokeWidth="1.2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#979797"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M12.362,10.031l0.291,6.447l-1.3,0.001l0.202,-6.448z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#FF4200"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M12.003,1.7C16.604,1.7 20.334,5.43 20.334,10.031C20.334,13.079 18.698,15.744 16.256,17.197L16.982,17.196L16.982,19.612C16.982,20.662 16.131,21.512 15.082,21.512L9.229,21.512C8.18,21.512 7.329,20.662 7.329,19.612L7.329,17.196L7.75,17.197C5.308,15.744 3.672,13.079 3.672,10.031C3.672,5.43 7.402,1.7 12.003,1.7Z"
|
||||
android:strokeWidth="1.3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2C2C2C"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M8.359,23.798L15.776,23.798"
|
||||
android:strokeWidth="1.2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2C2C2C"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M8.514,9.366l1.183,2.582l2.304,-3.102"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF4200"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M12,8.846l2.246,3.102l1.267,-2.582"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="1.3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF4200"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
25
servicing/src/main/res/drawable/sv_face_phone.xml
Normal file
25
servicing/src/main/res/drawable/sv_face_phone.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="25dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="25"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M8.604,4.595L16.324,4.595C16.877,4.595 17.324,5.043 17.324,5.595L17.324,14.119C17.324,14.672 16.877,15.119 16.324,15.119L8.604,15.119C8.052,15.119 7.604,14.672 7.604,14.119L7.604,5.595C7.604,5.043 8.052,4.595 8.604,4.595Z"
|
||||
android:strokeWidth="1.2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2C2C2C"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M10.822,19.086L14.252,19.086"
|
||||
android:strokeWidth="1.3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FF4200"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M6.672,1.427L18.369,1.427C19.473,1.427 20.369,2.323 20.369,3.427L20.369,20.715C20.369,21.82 19.473,22.715 18.369,22.715L6.672,22.715C5.567,22.715 4.672,21.82 4.672,20.715L4.672,3.427C4.672,2.323 5.567,1.427 6.672,1.427Z"
|
||||
android:strokeWidth="1.2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2C2C2C"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
220
servicing/src/main/res/drawable/sv_facea_verify.xml
Normal file
220
servicing/src/main/res/drawable/sv_facea_verify.xml
Normal file
@ -0,0 +1,220 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="215dp"
|
||||
android:height="215dp"
|
||||
android:viewportWidth="215"
|
||||
android:viewportHeight="215">
|
||||
<path
|
||||
android:pathData="M215,107.5C215,48.13 166.87,0 107.5,0C48.13,0 0,48.13 0,107.5C0,166.87 48.13,215 107.5,215"
|
||||
android:strokeAlpha="0.4359189"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FE4402"
|
||||
android:fillType="evenOdd"
|
||||
android:fillAlpha="0.4359189"/>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M107.5,107.5m-93,0a93,93 0,1 1,186 0a93,93 0,1 1,-186 0"/>
|
||||
<path
|
||||
android:pathData="M-1,176.15L216,176.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,148.15L216,148.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,190.15L216,190.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,162.15L216,162.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,121.15L216,121.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,135.15L216,135.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,93.15L216,93.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,107.15L216,107.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,66.15L216,66.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,79.15L216,79.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,39.15L216,39.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,25.15L216,25.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M-1,52.15L216,52.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M107.5,107.5m-93,0a93,93 0,1 1,186 0a93,93 0,1 1,-186 0"/>
|
||||
<path
|
||||
android:pathData="M39,-0.85L39,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M67,-0.85L67,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M25,-0.85L25,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M53,-0.85L53,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M94,-0.85L94,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M80,-0.85L80,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M122,-0.85L122,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M108,-0.85L108,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M149,-0.85L149,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M136,-0.85L136,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M176,-0.85L176,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M190,-0.85L190,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M163,-0.85L163,216.15"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#E7E7E7"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
<path
|
||||
android:pathData="M107.5,200.5C158.86,200.5 200.5,158.86 200.5,107.5C200.5,56.14 158.86,14.5 107.5,14.5C56.14,14.5 14.5,56.14 14.5,107.5C14.5,158.86 56.14,200.5 107.5,200.5Z"
|
||||
android:strokeWidth="22"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#F5CBBD"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M107.5,215C166.87,215 215,166.87 215,107.5"
|
||||
android:strokeWidth="3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#F5CBBD"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M144,26.96C132.74,21.85 120.23,19 107.04,19C93.82,19 81.28,21.86 70,27M20.65,126.23C23.34,138.3 28.16,150.16 36.1,160.69C44.06,171.25 53.89,179.55 64.78,185.46M195.13,123.77C192.87,135.93 188.46,147.95 180.89,158.75C173.31,169.58 163.69,177.72 153.02,184.01"
|
||||
android:strokeWidth="8"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#F99571"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M177,107.5A69.5,69.5 0,0 1,107.5 177,69.5 69.5,0 0,1 38,107.5 69.5,69.5 0,0 1,107.5 38,69.5 69.5,0 0,1 177,107.5Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="107.5"
|
||||
android:startY="38"
|
||||
android:endX="107.5"
|
||||
android:endY="177"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFF4CCBE"/>
|
||||
<item android:offset="1" android:color="#FFFE4402"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="m109.33,53.5c18.54,0.43 31.25,11.28 35.71,26.49 0.1,0.33 0.19,0.67 0.28,1 1.61,6.2 1.55,12.18 0.44,19.98l-0.3,2.03 0.23,0.23c8.25,8.33 5.48,22.07 -6.01,26.53l-0.08,0.27c-1.31,4.3 -4.27,9.53 -8.88,15.71 -0.55,0.78 -1.13,1.54 -1.74,2.28 -0.45,0.58 -0.92,1.17 -1.4,1.77l-0.1,-0.08c-4.49,4.74 -10.46,8.36 -17.86,10.86l-1.02,0.33 -0.35,-0.06c-7.51,-1.27 -14.4,-4.92 -20.65,-10.92l-0.13,-0.14C81.02,141.81 77.01,135.25 75.42,130.04l-0.08,-0.27C63.84,125.31 61.07,111.57 69.33,103.24l0.23,-0.23 -0.2,-1.32c-1.21,-8.15 -1.32,-14.31 0.34,-20.7 0.09,-0.34 0.18,-0.67 0.28,-1C74.33,65.09 86.64,54.35 104.61,53.54l0.62,-0.02 3.78,-0.02 0.05,-0z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#f4f4f4"
|
||||
android:fillType="nonZero"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
BIN
servicing/src/main/res/raw/cancel_order.mp3
Normal file
BIN
servicing/src/main/res/raw/cancel_order.mp3
Normal file
Binary file not shown.
BIN
servicing/src/main/res/raw/current_order_cancel.mp3
Normal file
BIN
servicing/src/main/res/raw/current_order_cancel.mp3
Normal file
Binary file not shown.
BIN
servicing/src/main/res/raw/face_center.mp3
Normal file
BIN
servicing/src/main/res/raw/face_center.mp3
Normal file
Binary file not shown.
BIN
servicing/src/main/res/raw/face_left.mp3
Normal file
BIN
servicing/src/main/res/raw/face_left.mp3
Normal file
Binary file not shown.
BIN
servicing/src/main/res/raw/face_right.mp3
Normal file
BIN
servicing/src/main/res/raw/face_right.mp3
Normal file
Binary file not shown.
BIN
servicing/src/main/res/raw/neworder.mp3
Normal file
BIN
servicing/src/main/res/raw/neworder.mp3
Normal file
Binary file not shown.
Reference in New Issue
Block a user