diff --git a/app/src/main/java/com/za/sdk/demo/MainActivity.kt b/app/src/main/java/com/za/sdk/demo/MainActivity.kt
index 1bfa70d..0c417dc 100644
--- a/app/src/main/java/com/za/sdk/demo/MainActivity.kt
+++ b/app/src/main/java/com/za/sdk/demo/MainActivity.kt
@@ -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)
}
diff --git a/servicing/build.gradle b/servicing/build.gradle
index 5f2558b..61bf4fd 100644
--- a/servicing/build.gradle
+++ b/servicing/build.gradle
@@ -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"
diff --git a/servicing/src/main/AndroidManifest.xml b/servicing/src/main/AndroidManifest.xml
index ccc5ed1..bdcd282 100644
--- a/servicing/src/main/AndroidManifest.xml
+++ b/servicing/src/main/AndroidManifest.xml
@@ -80,6 +80,11 @@
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true"
tools:targetApi="24">
+
+
// 处理推送消息
+ 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"
+ }
+}
\ No newline at end of file
diff --git a/servicing/src/main/java/com/za/base/PushMessageLiveData.kt b/servicing/src/main/java/com/za/base/PushMessageLiveData.kt
new file mode 100644
index 0000000..849c8ef
--- /dev/null
+++ b/servicing/src/main/java/com/za/base/PushMessageLiveData.kt
@@ -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()
+
+ // 对外暴露不可变的 LiveData
+ val pushMessage : LiveData = _pushMessage
+
+ // 发送推送消息
+ fun postPushMessage(message : String) {
+ _pushMessage.postValue(message)
+ }
+}
+
+class SingleLiveEvent : MutableLiveData() {
+ private val pending = AtomicBoolean(false)
+
+ @MainThread
+ override fun observe(owner : LifecycleOwner, observer : Observer) {
+ 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"
+ }
+}
\ No newline at end of file
diff --git a/servicing/src/main/java/com/za/common/GlobalData.kt b/servicing/src/main/java/com/za/common/GlobalData.kt
index bac37af..aeedcc1 100644
--- a/servicing/src/main/java/com/za/common/GlobalData.kt
+++ b/servicing/src/main/java/com/za/common/GlobalData.kt
@@ -117,6 +117,7 @@ object GlobalData : GlobalLocalData() {
currentLocation = null
driverInfoBean = null
loginTime = null
+ isLoginRecognition = null
}
fun clearAllOrderCache() {
diff --git a/servicing/src/main/java/com/za/common/ZDManager.kt b/servicing/src/main/java/com/za/common/ZDManager.kt
index 6efd24e..5301d89 100644
--- a/servicing/src/main/java/com/za/common/ZDManager.kt
+++ b/servicing/src/main/java/com/za/common/ZDManager.kt
@@ -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)
}
}
diff --git a/servicing/src/main/java/com/za/common/speech/CustomerSpeechManager.kt b/servicing/src/main/java/com/za/common/speech/CustomerSpeechManager.kt
new file mode 100644
index 0000000..1f7c715
--- /dev/null
+++ b/servicing/src/main/java/com/za/common/speech/CustomerSpeechManager.kt
@@ -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
+}
+
+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
+ }
+ }
+}
+
+
diff --git a/servicing/src/main/java/com/za/common/speech/SpeechManager.kt b/servicing/src/main/java/com/za/common/speech/SpeechManager.kt
new file mode 100644
index 0000000..3af5863
--- /dev/null
+++ b/servicing/src/main/java/com/za/common/speech/SpeechManager.kt
@@ -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() {
+ 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)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/servicing/src/main/java/com/za/common/speech/TTSManager.kt b/servicing/src/main/java/com/za/common/speech/TTSManager.kt
new file mode 100644
index 0000000..c0a27f5
--- /dev/null
+++ b/servicing/src/main/java/com/za/common/speech/TTSManager.kt
@@ -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?)
+}
\ No newline at end of file
diff --git a/servicing/src/main/java/com/za/net/BaseObserver.kt b/servicing/src/main/java/com/za/net/BaseObserver.kt
index f216c7e..bf1ac48 100644
--- a/servicing/src/main/java/com/za/net/BaseObserver.kt
+++ b/servicing/src/main/java/com/za/net/BaseObserver.kt
@@ -106,8 +106,8 @@ abstract class BaseObserver : Observer> {
ThreadUtils.runOnUiThread {
try {
ToastUtils.showShort("登陆信息已过期,请重新登录")
- GlobalData.clearUserCache()
ZdLocationManager.stopContinuousLocation()
+ GlobalData.clearUserCache()
ActivityUtils.startLauncherActivity()
} catch (e : Exception) {
LogUtil.print("handlerTokenExpired", e)
diff --git a/servicing/src/main/java/com/za/net/CommonMethod.kt b/servicing/src/main/java/com/za/net/CommonMethod.kt
index 9c89c69..9b32a98 100644
--- a/servicing/src/main/java/com/za/net/CommonMethod.kt
+++ b/servicing/src/main/java/com/za/net/CommonMethod.kt
@@ -138,7 +138,7 @@ object CommonMethod {
success(it)
return
}
- GlobalData.driverInfoBean=it
+ GlobalData.driverInfoBean = it
lastFetchGenerateInfoTime = System.currentTimeMillis()
LogUtil.print("GlobalData.driverInfoBean",
"${GlobalData.driverInfoBean?.toJson()}}")
@@ -160,31 +160,34 @@ object CommonMethod {
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BaseObserver() {
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)
diff --git a/servicing/src/main/java/com/za/service/ServiceManager.kt b/servicing/src/main/java/com/za/service/ServiceManager.kt
index 4d347e4..13c1532 100644
--- a/servicing/src/main/java/com/za/service/ServiceManager.kt
+++ b/servicing/src/main/java/com/za/service/ServiceManager.kt
@@ -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}")
+ }
+ }
}
\ No newline at end of file
diff --git a/servicing/src/main/java/com/za/service/location/ZdLocationManager.kt b/servicing/src/main/java/com/za/service/location/ZdLocationManager.kt
index adc7136..9d63370 100644
--- a/servicing/src/main/java/com/za/service/location/ZdLocationManager.kt
+++ b/servicing/src/main/java/com/za/service/location/ZdLocationManager.kt
@@ -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) {
diff --git a/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/InServicePeopleConfirmActivity.kt b/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/InServicePeopleConfirmActivity.kt
new file mode 100644
index 0000000..6ba3ee4
--- /dev/null
+++ b/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/InServicePeopleConfirmActivity.kt
@@ -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()
+ }
+}
diff --git a/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/InServicePeopleConfirmVm.kt b/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/InServicePeopleConfirmVm.kt
new file mode 100644
index 0000000..9b94bc8
--- /dev/null
+++ b/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/InServicePeopleConfirmVm.kt
@@ -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() {
+ private val _uiState = MutableStateFlow(UiState())
+ val uiState get() = _uiState as StateFlow
+ 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() {
+ 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,
+ )
+}
\ No newline at end of file
diff --git a/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/ServicePeopleConfirmScreen.kt b/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/ServicePeopleConfirmScreen.kt
new file mode 100644
index 0000000..552ef24
--- /dev/null
+++ b/servicing/src/main/java/com/za/ui/servicing/inservice_people_confirm/ServicePeopleConfirmScreen.kt
@@ -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)
+ })
+ }
+ }
+}
\ No newline at end of file
diff --git a/servicing/src/main/java/com/za/ui/servicing/wait_to_start/WaitToStartActivity.kt b/servicing/src/main/java/com/za/ui/servicing/wait_to_start/WaitToStartActivity.kt
index 109bf42..203205a 100644
--- a/servicing/src/main/java/com/za/ui/servicing/wait_to_start/WaitToStartActivity.kt
+++ b/servicing/src/main/java/com/za/ui/servicing/wait_to_start/WaitToStartActivity.kt
@@ -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)
})
}
diff --git a/servicing/src/main/java/com/za/ui/servicing/wait_to_start/WaitToStartVm.kt b/servicing/src/main/java/com/za/ui/servicing/wait_to_start/WaitToStartVm.kt
index 9049d2c..bc046a5 100644
--- a/servicing/src/main/java/com/za/ui/servicing/wait_to_start/WaitToStartVm.kt
+++ b/servicing/src/main/java/com/za/ui/servicing/wait_to_start/WaitToStartVm.kt
@@ -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(
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(
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() {
- 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(
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(
val routePoints : List? = null,
val remainingDistance : Float = 0f,
val showServicePeopleConfirmDialog : Boolean? = false,
- val comparableFailedStr : String? = null,
val estimatedArrivalTime : String = "")
}
\ No newline at end of file
diff --git a/servicing/src/main/res/drawable/sv_face_face.xml b/servicing/src/main/res/drawable/sv_face_face.xml
new file mode 100644
index 0000000..b36639e
--- /dev/null
+++ b/servicing/src/main/res/drawable/sv_face_face.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/servicing/src/main/res/drawable/sv_face_light.xml b/servicing/src/main/res/drawable/sv_face_light.xml
new file mode 100644
index 0000000..326c052
--- /dev/null
+++ b/servicing/src/main/res/drawable/sv_face_light.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
diff --git a/servicing/src/main/res/drawable/sv_face_phone.xml b/servicing/src/main/res/drawable/sv_face_phone.xml
new file mode 100644
index 0000000..13c3b0d
--- /dev/null
+++ b/servicing/src/main/res/drawable/sv_face_phone.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/servicing/src/main/res/drawable/sv_facea_verify.xml b/servicing/src/main/res/drawable/sv_facea_verify.xml
new file mode 100644
index 0000000..b0d5729
--- /dev/null
+++ b/servicing/src/main/res/drawable/sv_facea_verify.xml
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/servicing/src/main/res/raw/cancel_order.mp3 b/servicing/src/main/res/raw/cancel_order.mp3
new file mode 100644
index 0000000..0d5b623
Binary files /dev/null and b/servicing/src/main/res/raw/cancel_order.mp3 differ
diff --git a/servicing/src/main/res/raw/current_order_cancel.mp3 b/servicing/src/main/res/raw/current_order_cancel.mp3
new file mode 100644
index 0000000..1f1d398
Binary files /dev/null and b/servicing/src/main/res/raw/current_order_cancel.mp3 differ
diff --git a/servicing/src/main/res/raw/face_center.mp3 b/servicing/src/main/res/raw/face_center.mp3
new file mode 100644
index 0000000..f5a1570
Binary files /dev/null and b/servicing/src/main/res/raw/face_center.mp3 differ
diff --git a/servicing/src/main/res/raw/face_left.mp3 b/servicing/src/main/res/raw/face_left.mp3
new file mode 100644
index 0000000..3ce663a
Binary files /dev/null and b/servicing/src/main/res/raw/face_left.mp3 differ
diff --git a/servicing/src/main/res/raw/face_right.mp3 b/servicing/src/main/res/raw/face_right.mp3
new file mode 100644
index 0000000..caa1f40
Binary files /dev/null and b/servicing/src/main/res/raw/face_right.mp3 differ
diff --git a/servicing/src/main/res/raw/neworder.mp3 b/servicing/src/main/res/raw/neworder.mp3
new file mode 100644
index 0000000..789c23b
Binary files /dev/null and b/servicing/src/main/res/raw/neworder.mp3 differ