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