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()
|
.fillMaxSize()
|
||||||
.clickable {
|
.clickable {
|
||||||
val uri =
|
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)
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ publishing {
|
|||||||
release(MavenPublication) {
|
release(MavenPublication) {
|
||||||
groupId = 'io.github.szl9'
|
groupId = 'io.github.szl9'
|
||||||
artifactId = 'zd_servicing'
|
artifactId = 'zd_servicing'
|
||||||
version = "1.0.1.9.9.4"
|
version = "1.0.1.9.9.12"
|
||||||
|
|
||||||
pom {
|
pom {
|
||||||
packaging = "aar"
|
packaging = "aar"
|
||||||
|
@ -80,6 +80,11 @@
|
|||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:targetApi="24">
|
tools:targetApi="24">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="com.za.ui.servicing.inservice_people_confirm.ServicePeopleConfirmActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/Theme.Dealer" />
|
||||||
<activity
|
<activity
|
||||||
android:name="com.za.ui.main.ServiceLauncherActivity"
|
android:name="com.za.ui.main.ServiceLauncherActivity"
|
||||||
android:exported="true"
|
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 android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import com.tencent.smtt.sdk.QbSdk
|
import com.tencent.smtt.sdk.QbSdk
|
||||||
import com.za.base.BaseVm.Companion.showTipDialog
|
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.base.view.LoadingManager
|
||||||
import com.za.common.log.LogUtil
|
import com.za.common.log.LogUtil
|
||||||
|
|
||||||
abstract class BaseActivity : AppCompatActivity() {
|
abstract class BaseActivity : PushMessageActivity() {
|
||||||
protected val TAG by lazy { javaClass.simpleName }
|
protected val TAG by lazy { javaClass.simpleName }
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
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
|
currentLocation = null
|
||||||
driverInfoBean = null
|
driverInfoBean = null
|
||||||
loginTime = null
|
loginTime = null
|
||||||
|
isLoginRecognition = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearAllOrderCache() {
|
fun clearAllOrderCache() {
|
||||||
|
@ -5,6 +5,7 @@ import com.tencent.bugly.Bugly
|
|||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
import com.za.base.AppConfig
|
import com.za.base.AppConfig
|
||||||
import com.za.common.log.LogUtil
|
import com.za.common.log.LogUtil
|
||||||
|
import com.za.common.speech.SpeechManager
|
||||||
import com.za.room.RoomHelper
|
import com.za.room.RoomHelper
|
||||||
import com.za.service.location.ZdLocationManager
|
import com.za.service.location.ZdLocationManager
|
||||||
|
|
||||||
@ -28,5 +29,6 @@ object ZDManager {
|
|||||||
LogUtil.init(application)
|
LogUtil.init(application)
|
||||||
RoomHelper.init(application)
|
RoomHelper.init(application)
|
||||||
ZdLocationManager.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 {
|
ThreadUtils.runOnUiThread {
|
||||||
try {
|
try {
|
||||||
ToastUtils.showShort("登陆信息已过期,请重新登录")
|
ToastUtils.showShort("登陆信息已过期,请重新登录")
|
||||||
GlobalData.clearUserCache()
|
|
||||||
ZdLocationManager.stopContinuousLocation()
|
ZdLocationManager.stopContinuousLocation()
|
||||||
|
GlobalData.clearUserCache()
|
||||||
ActivityUtils.startLauncherActivity()
|
ActivityUtils.startLauncherActivity()
|
||||||
} catch (e : Exception) {
|
} catch (e : Exception) {
|
||||||
LogUtil.print("handlerTokenExpired", e)
|
LogUtil.print("handlerTokenExpired", e)
|
||||||
|
@ -138,7 +138,7 @@ object CommonMethod {
|
|||||||
success(it)
|
success(it)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
GlobalData.driverInfoBean=it
|
GlobalData.driverInfoBean = it
|
||||||
lastFetchGenerateInfoTime = System.currentTimeMillis()
|
lastFetchGenerateInfoTime = System.currentTimeMillis()
|
||||||
LogUtil.print("GlobalData.driverInfoBean",
|
LogUtil.print("GlobalData.driverInfoBean",
|
||||||
"${GlobalData.driverInfoBean?.toJson()}}")
|
"${GlobalData.driverInfoBean?.toJson()}}")
|
||||||
@ -160,31 +160,34 @@ object CommonMethod {
|
|||||||
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(object : BaseObserver<JpushBean>() {
|
.subscribe(object : BaseObserver<JpushBean>() {
|
||||||
override fun doSuccess(order : JpushBean?) {
|
override fun doSuccess(order : JpushBean?) {
|
||||||
|
if (order == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
NewOrderActivity.goNewOrderActivity(context,
|
NewOrderActivity.goNewOrderActivity(context,
|
||||||
jpushBean = JpushBean(taskId = order?.taskId,
|
jpushBean = JpushBean(taskId = order.taskId,
|
||||||
taskCode = order?.taskCode,
|
taskCode = order.taskCode,
|
||||||
customerName = order?.customerName,
|
customerName = order.customerName,
|
||||||
customerPhone = order?.customerPhone,
|
customerPhone = order.customerPhone,
|
||||||
carBrand = order?.carBrand,
|
carBrand = order.carBrand,
|
||||||
carModel = order?.carModel,
|
carModel = order.carModel,
|
||||||
carNo = order?.carNo,
|
carNo = order.carNo,
|
||||||
taskState = order?.taskState,
|
taskState = order.taskState,
|
||||||
address = order?.address,
|
address = order.address,
|
||||||
addressProperty = order?.addressProperty,
|
addressProperty = order.addressProperty,
|
||||||
hotline = order?.hotline,
|
hotline = order.hotline,
|
||||||
expectArriveTime = order?.expectArriveTime,
|
expectArriveTime = order.expectArriveTime,
|
||||||
serviceTypeName = order?.serviceTypeName,
|
serviceTypeName = order.serviceTypeName,
|
||||||
dispatchTime = order?.dispatchTime,
|
dispatchTime = order.dispatchTime,
|
||||||
lat = order?.lat,
|
lat = order.lat,
|
||||||
lng = order?.lng,
|
lng = order.lng,
|
||||||
distLat = order?.distLat,
|
distLat = order.distLat,
|
||||||
distLng = order?.distLng,
|
distLng = order.distLng,
|
||||||
importantTip = order?.importantTip,
|
importantTip = order.importantTip,
|
||||||
hasReplaceBatteryCapable = order?.hasReplaceBatteryCapable,
|
hasReplaceBatteryCapable = order.hasReplaceBatteryCapable,
|
||||||
distAddress = order?.distAddress,
|
distAddress = order.distAddress,
|
||||||
distAddressRemark = order?.distAddressRemark,
|
distAddressRemark = order.distAddressRemark,
|
||||||
addressRemark = order?.addressRemark,
|
addressRemark = order.addressRemark,
|
||||||
voiceType = order?.voiceType))
|
voiceType = order.voiceType))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun doFailure(code : Int, msg : String?) {
|
override fun doFailure(code : Int, msg : String?) {
|
||||||
@ -211,7 +214,8 @@ object CommonMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val inServicingOrder = it.find { it.isCurrent == true }
|
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) {
|
if (inServicingOrder == null) {
|
||||||
success(null, waitServiceOrders)
|
success(null, waitServiceOrders)
|
||||||
|
@ -10,6 +10,7 @@ import androidx.core.app.NotificationCompat
|
|||||||
import cn.jiguang.api.utils.JCollectionAuth
|
import cn.jiguang.api.utils.JCollectionAuth
|
||||||
import cn.jpush.android.api.JPushInterface
|
import cn.jpush.android.api.JPushInterface
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import com.za.base.PushMessageLiveData
|
||||||
import com.za.bean.JpushBean
|
import com.za.bean.JpushBean
|
||||||
import com.za.common.GlobalData
|
import com.za.common.GlobalData
|
||||||
import com.za.common.log.LogUtil
|
import com.za.common.log.LogUtil
|
||||||
@ -17,19 +18,9 @@ import com.za.common.util.DeviceUtil
|
|||||||
import com.za.service.mqtt.MyMqttClient
|
import com.za.service.mqtt.MyMqttClient
|
||||||
import com.za.servicing.R
|
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())
|
data class LastJPushBean(val msg : Int, val time : Long = System.nanoTime())
|
||||||
|
|
||||||
object ServiceManager {
|
object ServiceManager {
|
||||||
private var pushListener : PushListener? = null
|
|
||||||
private var lastJPushBean : LastJPushBean? = null
|
private var lastJPushBean : LastJPushBean? = null
|
||||||
private const val DUPLICATE_MSG_THRESHOLD = 3000L // 3秒
|
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
|
// Handle incoming push messages
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun handlerPushMsg(msg : String) {
|
fun handlerPushMsg(msg : String) {
|
||||||
@ -75,93 +59,22 @@ object ServiceManager {
|
|||||||
|
|
||||||
if (msg.startsWith("broadcast:")) {
|
if (msg.startsWith("broadcast:")) {
|
||||||
lastJPushBean = LastJPushBean(msg = msg.hashCode())
|
lastJPushBean = LastJPushBean(msg = msg.hashCode())
|
||||||
handleBroadcast(msg)
|
PushMessageLiveData.postPushMessage(msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
lastJPushBean = LastJPushBean(msg = msg.hashCode())
|
lastJPushBean = LastJPushBean(msg = msg.hashCode())
|
||||||
val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java)
|
val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java)
|
||||||
sendSystemNotificationFromMessage(jpushOrderInfoBean)
|
sendSystemNotificationFromMessage(jpushOrderInfoBean)
|
||||||
when (jpushOrderInfoBean.pushType) {
|
PushMessageLiveData.postPushMessage(msg)
|
||||||
0 -> newOrderMsg(jpushOrderInfoBean)
|
|
||||||
1 -> handleTypeOneMessage(jpushOrderInfoBean)
|
|
||||||
3 -> importantTip(jpushOrderInfoBean)
|
|
||||||
else -> LogUtil.print("JpushMessage",
|
|
||||||
"Unknown push type: ${jpushOrderInfoBean.pushType}")
|
|
||||||
}
|
|
||||||
} catch (e : Exception) {
|
} catch (e : Exception) {
|
||||||
if (msg.startsWith("broadcast:")) {
|
if (msg.startsWith("broadcast:")) {
|
||||||
handleBroadcast(msg)
|
PushMessageLiveData.postPushMessage(msg)
|
||||||
}
|
}
|
||||||
LogUtil.print("JpushMessage", "Error handling message: ${e.message}")
|
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 CHANNEL_ID = "ImportantMessagesChannel"
|
||||||
private const val NOTIFICATION_ID = 1003
|
private const val NOTIFICATION_ID = 1003
|
||||||
|
|
||||||
@ -224,5 +137,15 @@ object ServiceManager {
|
|||||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
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() {
|
fun stopContinuousLocation() {
|
||||||
try {
|
try {
|
||||||
aMapLocationClient?.stopLocation()
|
aMapLocationClient?.stopLocation()
|
||||||
|
aMapLocationClient?.onDestroy()
|
||||||
|
aMapLocationClient?.unRegisterLocationListener(this)
|
||||||
|
aMapLocationClient = null
|
||||||
LogUtil.print(TAG, "关闭持续定位成功")
|
LogUtil.print(TAG, "关闭持续定位成功")
|
||||||
} catch (e : Exception) {
|
} catch (e : Exception) {
|
||||||
LogUtil.print(TAG, "关闭持续定位失败: ${e.message}")
|
LogUtil.print(TAG, "关闭持续定位失败: ${e.message}")
|
||||||
@ -134,6 +137,10 @@ object ZdLocationManager : AMapLocationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun uploadGps(uploadGpsRequest : UploadGpsRequest) {
|
private fun uploadGps(uploadGpsRequest : UploadGpsRequest) {
|
||||||
|
if (GlobalData.token.isNullOrBlank()) {
|
||||||
|
LogUtil.print(TAG, "定位上传失败: token is null,request=$uploadGpsRequest")
|
||||||
|
return
|
||||||
|
}
|
||||||
CommonMethod.uploadGps(uploadGpsRequest, success = {
|
CommonMethod.uploadGps(uploadGpsRequest, success = {
|
||||||
LogUtil.print(TAG, "定位上传成功: ${uploadGpsRequest.toJson()}")
|
LogUtil.print(TAG, "定位上传成功: ${uploadGpsRequest.toJson()}")
|
||||||
MyMqttClient.publishMessage() // if (ActivityUtils.getTopActivity()==null) {
|
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
|
package com.za.ui.servicing.wait_to_start
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.theme.headBgColor
|
||||||
import com.za.base.view.CommonDialog
|
import com.za.base.view.CommonDialog
|
||||||
import com.za.common.GlobalData
|
import com.za.common.GlobalData
|
||||||
import com.za.common.log.LogUtil
|
|
||||||
import com.za.common.util.ImageUtil
|
import com.za.common.util.ImageUtil
|
||||||
import com.za.common.util.ServicingSpeechManager
|
import com.za.common.util.ServicingSpeechManager
|
||||||
import com.za.ext.copy
|
import com.za.ext.copy
|
||||||
import com.za.ext.finish
|
import com.za.ext.finish
|
||||||
import com.za.ext.goNextPage
|
import com.za.ext.goNextPage
|
||||||
|
import com.za.ext.navigationActivity
|
||||||
import com.za.servicing.R
|
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
|
import com.za.ui.servicing.view.InServicingHeadView
|
||||||
|
|
||||||
|
|
||||||
@ -89,19 +85,6 @@ fun WaitToStartScreen(vm : WaitToStartVm = viewModel()) {
|
|||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
val mapView = remember { MapView(context) }
|
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 状态
|
// 添加 BottomSheet 状态
|
||||||
val bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.Expanded)
|
val bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.Expanded)
|
||||||
val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
|
val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
|
||||||
@ -166,27 +149,7 @@ fun WaitToStartScreen(vm : WaitToStartVm = viewModel()) {
|
|||||||
confirm = {
|
confirm = {
|
||||||
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
|
vm.dispatch(WaitToStartVm.Action.UpdateState(uiState.value.copy(
|
||||||
showServicePeopleConfirmDialog = false)))
|
showServicePeopleConfirmDialog = false)))
|
||||||
val intent = Intent(context, ZdCameraXActivity::class.java)
|
context.navigationActivity(ServicePeopleConfirmActivity::class.java, true)
|
||||||
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)))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.za.ui.servicing.wait_to_start
|
package com.za.ui.servicing.wait_to_start
|
||||||
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.amap.api.maps.AMapUtils
|
import com.amap.api.maps.AMapUtils
|
||||||
import com.amap.api.maps.model.BitmapDescriptorFactory
|
import com.amap.api.maps.model.BitmapDescriptorFactory
|
||||||
import com.amap.api.maps.model.LatLng
|
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.IServicingVm
|
||||||
import com.za.base.view.LoadingManager
|
import com.za.base.view.LoadingManager
|
||||||
import com.za.bean.db.order.OrderInfo
|
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.UpdateTaskBean
|
||||||
import com.za.bean.request.UpdateTaskRequest
|
import com.za.bean.request.UpdateTaskRequest
|
||||||
import com.za.common.GlobalData
|
import com.za.common.GlobalData
|
||||||
import com.za.common.log.LogUtil
|
import com.za.common.log.LogUtil
|
||||||
import com.za.ext.toJson
|
import com.za.ext.toJson
|
||||||
import com.za.net.BaseObserver
|
|
||||||
import com.za.net.CommonMethod
|
import com.za.net.CommonMethod
|
||||||
import com.za.net.RetrofitHelper
|
|
||||||
import com.za.service.location.ZdLocationManager
|
import com.za.service.location.ZdLocationManager
|
||||||
import com.za.servicing.R
|
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.Job
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
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.text.SimpleDateFormat
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
@ -55,7 +43,6 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
|
|||||||
is Action.UpdateState -> updateState(action.uiState)
|
is Action.UpdateState -> updateState(action.uiState)
|
||||||
is Action.StartTimer -> startTimer()
|
is Action.StartTimer -> startTimer()
|
||||||
is Action.UpdateTimer -> updateTimer()
|
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 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() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
timerJob?.cancel()
|
timerJob?.cancel()
|
||||||
@ -288,7 +229,6 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
|
|||||||
data class UpdateState(val uiState : UiState) : Action()
|
data class UpdateState(val uiState : UiState) : Action()
|
||||||
data object StartTimer : Action()
|
data object StartTimer : Action()
|
||||||
data object UpdateTimer : Action()
|
data object UpdateTimer : Action()
|
||||||
data class CompareServicePeople(val localPath : String) : Action()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class UiState(val orderInfo : OrderInfo? = null,
|
data class UiState(val orderInfo : OrderInfo? = null,
|
||||||
@ -299,6 +239,5 @@ class WaitToStartVm : IServicingVm<WaitToStartVm.Action, WaitToStartVm.UiState>(
|
|||||||
val routePoints : List<LatLng>? = null,
|
val routePoints : List<LatLng>? = null,
|
||||||
val remainingDistance : Float = 0f,
|
val remainingDistance : Float = 0f,
|
||||||
val showServicePeopleConfirmDialog : Boolean? = false,
|
val showServicePeopleConfirmDialog : Boolean? = false,
|
||||||
val comparableFailedStr : String? = null,
|
|
||||||
val estimatedArrivalTime : String = "")
|
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