From 22b0f14236fab0702233e8258de070fe402a6b61 Mon Sep 17 00:00:00 2001 From: songzhiling <17630035658@163.com> Date: Tue, 29 Apr 2025 17:37:46 +0800 Subject: [PATCH] =?UTF-8?q?refactor(servicing):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E6=B6=88=E6=81=AF=E5=A4=84=E7=90=86=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 ZdPushServiceReceive 类用于集中处理推送消息 - 优化了消息处理逻辑,提高了代码的可维护性和可扩展性 -改进了消息在主进程中的广播发送方式 - 优化了语音播放逻辑,提高了播放的稳定性和流畅性 - 调整了通知渠道的创建和通知的发送方式 --- .idea/deploymentTargetSelector.xml | 8 + .../main/java/com/za/sdk/demo/MainActivity.kt | 2 +- servicing/build.gradle | 2 +- servicing/src/main/AndroidManifest.xml | 8 + .../java/com/za/base/PushMessageActivity.kt | 260 +++--------------- .../src/main/java/com/za/common/GlobalData.kt | 7 +- .../com/za/common/speech/SpeechManager.kt | 145 +++++++--- .../java/com/za/service/ServiceManager.kt | 14 +- .../com/za/service/ZdPushServiceReceive.kt | 237 ++++++++++++++++ .../service/mqtt/ConnectionOptionWrapper.kt | 2 +- .../com/za/ui/main/ServicingMainActivity.kt | 47 ++++ .../modify_money/ModifyMoneyViewModel.kt | 2 +- 12 files changed, 453 insertions(+), 281 deletions(-) create mode 100644 servicing/src/main/java/com/za/service/ZdPushServiceReceive.kt diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..f8dae00 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,6 +4,14 @@ 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 0c417dc..9389713 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=ZD250427100009&driverName=宋志领&driverPhone=17630035658&rescueVehicle=沪88888".toUri() + "zd.assist://app?taskCode=ZD250429100095&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 99fe723..3e3a581 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.25" + version = "1.0.1.9.9.31" pom { packaging = "aar" diff --git a/servicing/src/main/AndroidManifest.xml b/servicing/src/main/AndroidManifest.xml index bdcd282..a0f1d51 100644 --- a/servicing/src/main/AndroidManifest.xml +++ b/servicing/src/main/AndroidManifest.xml @@ -256,6 +256,14 @@ + + + + + + handleBroadcast("broadcast:$message") - "giveUp" -> { - try { - val jpushBean = Gson().fromJson(message, JpushBean::class.java) - val activity = - getCurrentActivity() ?: ActivityUtils.getTopActivity() - if (activity is AppCompatActivity) { - handleGiveUpOrder(activity, jpushBean) - } - } catch (e : Exception) { - LogUtil.print("PushActivityLifecycleCallbacks", - "处理订单放弃消息失败: ${e.message}") - } - } - - "importantTip" -> { - try { - val jpushBean = Gson().fromJson(message, JpushBean::class.java) - val activity = - getCurrentActivity() ?: ActivityUtils.getTopActivity() - if (activity is AppCompatActivity) { - handleImportantTip(activity, jpushBean) - } - } catch (e : Exception) { - LogUtil.print("PushActivityLifecycleCallbacks", - "处理重要提示消息失败: ${e.message}") - } - } - - "reDispatch" -> { - try { - val jpushBean = Gson().fromJson(message, JpushBean::class.java) - val activity = - getCurrentActivity() ?: ActivityUtils.getTopActivity() - if (activity is AppCompatActivity) { - handleReDispatchOrder(activity, jpushBean) - } - } catch (e : Exception) { - LogUtil.print("PushActivityLifecycleCallbacks", - "处理订单重新派发消息失败: ${e.message}") - } - } - - "revoke" -> { - try { - handleRevokeOrder() - } catch (e : Exception) { - LogUtil.print("PushActivityLifecycleCallbacks", - "处理订单撤回消息失败: ${e.message}") - } - } - } - } + private fun setupPushMessageReceiver() { // 注册推送消息接收器 + ServiceManager.registerPushListener(object : PushListener { + override fun broadcast(msg : String) { + sendMessageToMainProcess("broadcast", msg) } - } - ContextCompat.registerReceiver(context, - pushMessageReceiver, - IntentFilter("com.za.rescue.dealer.PUSH_MESSAGE"), - ContextCompat.RECEIVER_NOT_EXPORTED) - LogUtil.print("PushActivityLifecycleCallbacks", "注册推送消息接收器") - } - - override fun onDestroy() { - super.onDestroy() - if (isLastActivity()) { - try { - this.unregisterReceiver(pushMessageReceiver) - pushMessageReceiver = null - LogUtil.print("PushActivityLifecycleCallbacks", "注销推送消息接收器") - } catch (e : Exception) { - LogUtil.print("PushActivityLifecycleCallbacks", - "注销推送消息接收器失败: ${e.message}") + override fun giveUpOrder(jpushBean : JpushBean) { + sendMessageToMainProcess("giveUp", Gson().toJson(jpushBean)) } - } + + override fun importantTip(jpushBean : JpushBean) { + sendMessageToMainProcess("importantTip", Gson().toJson(jpushBean)) + } + + override fun newOrderMsg(jpushBean : JpushBean) { + sendMessageToMainProcess("newOrder", Gson().toJson(jpushBean)) + } + + override fun reDispatchOrder(jpushBean : JpushBean) { + sendMessageToMainProcess("reDispatch", Gson().toJson(jpushBean)) + } + + override fun revokeOrder(jpushBean : JpushBean) { + sendMessageToMainProcess("revoke", Gson().toJson(jpushBean)) + } + }) } - private fun isLastActivity() : Boolean { // 检查是否是最后一个活动的Activity - return ActivityUtils.getActivityList().size <= 1 + private fun sendMessageToMainProcess(type : String, message : String) { // 使用广播将消息发送到主进程 + val intent = Intent(ZdPushServiceReceive.RECEIVE_ACTION).setPackage(packageName) + intent.putExtra("type", type) + intent.putExtra("message", message) + sendBroadcast(intent) + LogUtil.print("KeepAliveService", "发送消息到主进程: $type") } override fun onPause() { @@ -132,18 +60,6 @@ open class PushMessageActivity : AppCompatActivity() { dismissCurrentDialog() } - // 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() @@ -153,116 +69,6 @@ open class PushMessageActivity : AppCompatActivity() { } } - private fun handleGiveUpOrder(activity : AppCompatActivity, - 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(activity : AppCompatActivity, jpushBean : JpushBean) { - playNotificationSound(this) - currentDialog = AlertDialog.Builder(this).setTitle("订单重新派发") - .setMessage(buildReDispatchMessage(jpushBean)).setCancelable(false) - .setPositiveButton("确定") { dialog, _ -> - dialog.dismiss() - }.show() - } - - private fun handleImportantTip(activity : AppCompatActivity, 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" diff --git a/servicing/src/main/java/com/za/common/GlobalData.kt b/servicing/src/main/java/com/za/common/GlobalData.kt index b9e7d76..fe5ed19 100644 --- a/servicing/src/main/java/com/za/common/GlobalData.kt +++ b/servicing/src/main/java/com/za/common/GlobalData.kt @@ -95,10 +95,11 @@ object GlobalData : GlobalLocalData() { var currentLocation : AMapLocation? = null get() { - return field + return mmkv.decodeParcelable("currentLocation", AMapLocation::class.java) } set(value) { - value?.time = System.currentTimeMillis() + value?.time= System.currentTimeMillis() + mmkv.encode("currentLocation", value) field = value } @@ -117,7 +118,7 @@ object GlobalData : GlobalLocalData() { currentLocation = null driverInfoBean = null loginTime = null -// isLoginRecognition = null + isLoginRecognition = null } fun clearAllOrderCache() { diff --git a/servicing/src/main/java/com/za/common/speech/SpeechManager.kt b/servicing/src/main/java/com/za/common/speech/SpeechManager.kt index 09631c8..ef8d239 100644 --- a/servicing/src/main/java/com/za/common/speech/SpeechManager.kt +++ b/servicing/src/main/java/com/za/common/speech/SpeechManager.kt @@ -1,10 +1,10 @@ package com.za.common.speech import android.app.Application +import android.media.AudioAttributes 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 @@ -16,7 +16,11 @@ 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 kotlin.concurrent.thread +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext object SpeechManager { private var mContext : Application? = null @@ -65,6 +69,7 @@ object SpeechManager { } val audioManager = ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java) + originVolume = audioManager?.getStreamVolume(AudioManager.STREAM_MUSIC) ?: 0 val maxVolume = audioManager?.getStreamMaxVolume(AudioManager.STREAM_MUSIC) audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume ?: 1, @@ -127,60 +132,89 @@ object SpeechManager { } private fun playNewOrder() { - stopPlayMedia() - setMaxAudioVolume() - mediaPlayer = MediaPlayer.create(mContext, R.raw.neworder) - mediaPlayer?.start() - mediaPlayer?.setOnCompletionListener { - resetAudioVolume() + try { // 先释放之前的MediaPlayer实例,避免资源泄漏 + releaseMediaPlayer() + + // 创建新的MediaPlayer实例 + mediaPlayer = MediaPlayer.create(GlobalData.application, R.raw.neworder) + mediaPlayer?.setOnCompletionListener { // 播放完成后释放资源 + releaseMediaPlayer() + } + mediaPlayer?.setOnErrorListener { mp, what, extra -> + LogUtil.print("playNewOrder", "MediaPlayer错误: what=$what, extra=$extra") + releaseMediaPlayer() + true + } + + // 设置最大音量 + setMaxAudioVolume() + + // 开始播放 + mediaPlayer?.start() + } catch (e : Exception) { + LogUtil.print("playNewOrder", "播放出错: ${e.message}") + releaseMediaPlayer() } } private fun playNewOrderFromNet(url : String) { - try { - stopPlayMedia() - setMaxAudioVolume() + try { // 先释放之前的MediaPlayer实例 + LogUtil.print("playNewOrderFromNet", "播放新订单语音: $url") + releaseMediaPlayer() // 创建新的MediaPlayer实例 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?.setAudioAttributes(AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .setUsage(AudioAttributes.USAGE_MEDIA).build()) + + mediaPlayer?.setDataSource(url) + mediaPlayer?.setOnPreparedListener { // 设置最大音量 + setMaxAudioVolume() // 准备完成后开始播放 + it.start() } - mediaPlayer?.setOnCompletionListener { - resetAudioVolume() + mediaPlayer?.setOnCompletionListener { // 播放完成后释放资源 + releaseMediaPlayer() } + mediaPlayer?.setOnErrorListener { mp, what, extra -> + LogUtil.print("playNewOrderFromNet", "MediaPlayer错误: what=$what, extra=$extra") + releaseMediaPlayer() + true + } + + // 异步准备,避免阻塞UI线程 + mediaPlayer?.prepareAsync() } catch (e : Exception) { - playNewOrder() - LogUtil.print("播放新订单失败", e) + LogUtil.print("playNewOrderFromNet", "播放出错: ${e.message}") + releaseMediaPlayer() } } - fun stopPlayMedia() { - ThreadUtils.runOnUiThread { - if (null != mediaPlayer) { - mediaPlayer?.stop() - mediaPlayer?.release() - mediaPlayer = null + // 安全释放MediaPlayer资源 + fun releaseMediaPlayer() { + try { + mediaPlayer?.let { + if (it.isPlaying) { + it.stop() + } + it.reset() + it.release() } + mediaPlayer = null // 恢复原始音量 + resetAudioVolume() + } catch (e : Exception) { + LogUtil.print("releaseMediaPlayer", "释放MediaPlayer出错: ${e.message}") } } fun speechNewOrderSound(content : String?) { if (content.isNullOrBlank()) { - speechNewOrderLooper("您有新的中道救援订单,请尽快接单!") { playNewOrder() } + speechNewOrderLocalSoundLooper() return } val localResourceDao = RoomHelper.db?.localResourceDao() val localUrlResource = localResourceDao?.getLocalResourceByName(content) if (localUrlResource != null && ! localUrlResource.resourceUrl.isNullOrBlank()) { - speechNewOrderLooper(content) { - playNewOrderFromNet(localUrlResource.resourceUrl) - } + speechNewOrderNetLooper(content, localUrlResource.resourceUrl ?: "") LogUtil.print("handlerNewOrderVoice", "播放本地语音"); return } @@ -191,28 +225,59 @@ object SpeechManager { .subscribe(object : BaseObserver() { override fun doSuccess(it : String?) { if (it == null) { - speechNewOrderLooper(content) { playNewOrder() } + speechNewOrderLocalSoundLooper() return } localResourceDao?.insert(LocalResourceBean(resourceName = content, resourceType = 1, resourceUrl = it)) - speechNewOrderLooper(content) { playNewOrderFromNet(it) } + speechNewOrderNetLooper(content, it) } override fun doFailure(code : Int, msg : String?) { - speechNewOrderLooper("您有新的中道救援订单,请尽快接单!") { playNewOrder() } + speechNewOrderLocalSoundLooper() } }) } - private fun speechNewOrderLooper(content : String, play : () -> Unit) { - thread { + + private fun speechNewOrderLocalSoundLooper() { + CoroutineScope(Dispatchers.IO).launch { val startTime = System.currentTimeMillis() while (System.currentTimeMillis() - startTime < 1000 * 60 * 3 && GlobalData.isHandlerNewOrder == false) { - play() - Thread.sleep(250L * content.length) + try { + delay(250L * "您有新的中道救援订单,请尽快接单!".length) + withContext(Dispatchers.Main) { + playNewOrder() + } + } catch (e : Exception) { + LogUtil.print("speechNewOrderLooper", + "播放循环出错: ${e.message}") // 出错时也要等待一下,避免无限循环 + delay(1000) + } } + GlobalData.isHandlerNewOrder = false + } + } + + private fun speechNewOrderNetLooper(content : String, url : String) { + val url = url.replace("http://", "https://") + GlobalData.isHandlerNewOrder = false + CoroutineScope(Dispatchers.IO).launch { + val startTime = System.currentTimeMillis() + while (System.currentTimeMillis() - startTime < 1000 * 60 * 3 && GlobalData.isHandlerNewOrder == false) { + try { + withContext(Dispatchers.Main) { + playNewOrderFromNet(url) + } + delay(250L * content.length) + } catch (e : Exception) { + LogUtil.print("speechNewOrderLooper", + "播放循环出错: ${e.message}") // 出错时也要等待一下,避免无限循环 + delay(1000) + } + } + GlobalData.isHandlerNewOrder = false } } } \ No newline at end of file diff --git a/servicing/src/main/java/com/za/service/ServiceManager.kt b/servicing/src/main/java/com/za/service/ServiceManager.kt index decfa9a..3418f30 100644 --- a/servicing/src/main/java/com/za/service/ServiceManager.kt +++ b/servicing/src/main/java/com/za/service/ServiceManager.kt @@ -30,7 +30,7 @@ interface PushListener { fun importantTip(jpushBean : JpushBean) } -data class LastJPushBean(val msg : Int, val time : Long = System.nanoTime()) +data class LastJPushBean(val msg : String, val time : Long = System.currentTimeMillis()) object ServiceManager { @Volatile @@ -115,21 +115,21 @@ object ServiceManager { // 优化后的重复消息判断 lastJPushBean?.let { - if (System.nanoTime() - it.time < DUPLICATE_MSG_THRESHOLD && it.msg == msg.hashCode()) { + if (System.currentTimeMillis() - it.time < DUPLICATE_MSG_THRESHOLD && it.msg == msg) { LogUtil.print("MessageHandler", "Duplicate message detected (hash: ${msg})") return } } if (msg.startsWith("broadcast:")) { - lastJPushBean = LastJPushBean(msg = msg.hashCode()) - handleBroadcast(msg) // PushMessageLiveData.postPushMessage(msg) + lastJPushBean = LastJPushBean(msg = msg) + handleBroadcast(msg) return } try { - lastJPushBean = LastJPushBean(msg = msg.hashCode()) + lastJPushBean = LastJPushBean(msg = msg) val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java) - sendSystemNotificationFromMessage(jpushOrderInfoBean) // PushMessageLiveData.postPushMessage(msg) + sendSystemNotificationFromMessage(jpushOrderInfoBean) when (jpushOrderInfoBean.pushType) { 0 -> newOrderMsg(jpushOrderInfoBean) 1 -> handleTypeOneMessage(jpushOrderInfoBean) @@ -138,7 +138,7 @@ object ServiceManager { "Unknown push type: ${jpushOrderInfoBean.pushType}") } } catch (e : Exception) { - if (msg.startsWith("broadcast:")) { // PushMessageLiveData.postPushMessage(msg) + if (msg.startsWith("broadcast:")) { handleBroadcast(msg) } LogUtil.print("JpushMessage", "Error handling message: ${e.message}") diff --git a/servicing/src/main/java/com/za/service/ZdPushServiceReceive.kt b/servicing/src/main/java/com/za/service/ZdPushServiceReceive.kt new file mode 100644 index 0000000..fa80b55 --- /dev/null +++ b/servicing/src/main/java/com/za/service/ZdPushServiceReceive.kt @@ -0,0 +1,237 @@ +package com.za.service + +import android.app.Activity +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.media.RingtoneManager +import android.os.Build +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.NotificationCompat +import com.blankj.utilcode.util.ActivityUtils +import com.blankj.utilcode.util.AppUtils +import com.google.gson.Gson +import com.za.base.BaseActivityLifecycleCallbacks +import com.za.bean.JpushBean +import com.za.common.GlobalData +import com.za.common.log.LogUtil +import com.za.common.speech.SpeechManager +import com.za.servicing.R +import com.za.ui.servicing.order_give_up.OrderGiveUpActivity +import com.za.ui.view.CommonDialogFragment + +class ZdPushServiceReceive : BroadcastReceiver() { + companion object { + const val RECEIVE_ACTION = "com.zd.servicing.PUSH_MESSAGE" + private const val GIVE_UP_TYPE_NORMAL = 1 + private const val DIALOG_TAG_GIVE_UP = "giveUp" + } + + private fun handlerMessage(context : Context?, intent : Intent?) { + val activity = context ?: ActivityUtils.getTopActivity() + activity as? AppCompatActivity + if (intent?.action == RECEIVE_ACTION) { + val type = intent.getStringExtra("type") ?: return + val message = intent.getStringExtra("message") ?: return + if (ActivityUtils.getTopActivity() == null) { + AppUtils.launchApp(GlobalData.application.packageName) + } + LogUtil.print("PushActivityLifecycleCallbacks", "收到来自远程进程的消息: $type") + when (type) { + "broadcast" -> handleBroadcast("broadcast:$message") + + "giveUp" -> { + try { + val jpushBean = Gson().fromJson(message, JpushBean::class.java) + val activity = BaseActivityLifecycleCallbacks.Companion.getCurrentActivity() + ?: ActivityUtils.getTopActivity() + if (activity is AppCompatActivity) { + handleGiveUpOrder(activity, jpushBean) + } + } catch (e : Exception) { + LogUtil.print("PushActivityLifecycleCallbacks", + "处理订单放弃消息失败: ${e.message}") + } + } // 处理其他类型的消息... + "importantTip" -> { + try { + val jpushBean = Gson().fromJson(message, JpushBean::class.java) + val activity = BaseActivityLifecycleCallbacks.Companion.getCurrentActivity() + ?: ActivityUtils.getTopActivity() + if (activity is AppCompatActivity) { + handleImportantTip(activity, jpushBean) + } + } catch (e : Exception) { + LogUtil.print("PushActivityLifecycleCallbacks", + "处理重要提示消息失败: ${e.message}") + } + } + + "reDispatch" -> { + try { + val jpushBean = Gson().fromJson(message, JpushBean::class.java) + val activity = BaseActivityLifecycleCallbacks.Companion.getCurrentActivity() + ?: ActivityUtils.getTopActivity() + if (activity is AppCompatActivity) { + handleReDispatchOrder(activity, jpushBean) + } + } catch (e : Exception) { + LogUtil.print("PushActivityLifecycleCallbacks", + "处理订单重新派发消息失败: ${e.message}") + } + } + + "revoke" -> { + try { + val activity = BaseActivityLifecycleCallbacks.Companion.getCurrentActivity() + ?: ActivityUtils.getTopActivity() + if (activity is AppCompatActivity) { + handleRevokeOrder(activity) + } + } catch (e : Exception) { + LogUtil.print("PushActivityLifecycleCallbacks", + "处理订单撤回消息失败: ${e.message}") + } + } + + } + } + } + + private fun handleGiveUpOrder(activity : Activity, jpushBean : JpushBean) { + if (activity !is AppCompatActivity) return + + // 播放提示音 + playNotificationSound(activity) + + if (GlobalData.currentOrder != null && GlobalData.currentOrder?.taskId == jpushBean.taskId) { + SpeechManager.playCurrentOrderCanceled() + CommonDialogFragment(title = "订单放弃", + message = buildGiveUpMessage(jpushBean), + confirmText = "去拍照", + cancelText = "我已了解", + confirm = { + OrderGiveUpActivity.Companion.goOrderGiveUpActivity(activity, + giveUpType = GIVE_UP_TYPE_NORMAL, + taskId = jpushBean.taskId) + }).show(activity.supportFragmentManager, DIALOG_TAG_GIVE_UP) + } else { + SpeechManager.playOrderCanceled() + } + } + + private fun handleRevokeOrder(activity : Activity) { + playNotificationSound(activity) + SpeechManager.speech("订单被撤回") + ActivityUtils.getTopActivity().finish() + } + + private fun handleReDispatchOrder(activity : Activity, jpushBean : JpushBean) { + playNotificationSound(activity) + BaseActivityLifecycleCallbacks.Companion.getCurrentActivity()?.let { currentActivity -> + if (currentActivity is AppCompatActivity) { + AlertDialog.Builder(currentActivity).setTitle("订单重新派发") + .setMessage(buildReDispatchMessage(jpushBean)).setCancelable(false) + .setPositiveButton("确定") { dialog, _ -> + dialog.dismiss() + currentActivity.finish() + }.show() + } + } + } + + private fun handleImportantTip(activity : Activity, jpushBean : JpushBean) { + playNotificationSound(activity) + SpeechManager.speech("重要提醒:${jpushBean.tipContent ?: ""}") + AlertDialog.Builder(activity).setTitle("重要提醒").setMessage(jpushBean.tipContent) + .setNegativeButton("我已了解") { dialog : DialogInterface, _ : Int -> dialog.dismiss() } + .show() + } + + // 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 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 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 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(Context.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(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.notify(NOTIFICATION_ID, notification) + } + + override fun onReceive(context : Context?, intent : Intent?) { + handlerMessage(context, intent) + } +} \ No newline at end of file diff --git a/servicing/src/main/java/com/za/service/mqtt/ConnectionOptionWrapper.kt b/servicing/src/main/java/com/za/service/mqtt/ConnectionOptionWrapper.kt index 572cd53..2f82ea9 100644 --- a/servicing/src/main/java/com/za/service/mqtt/ConnectionOptionWrapper.kt +++ b/servicing/src/main/java/com/za/service/mqtt/ConnectionOptionWrapper.kt @@ -18,7 +18,7 @@ class ConnectionOptionWrapper( */ @JvmField val mqttConnectOptions: MqttConnectOptions = MqttConnectOptions().apply { - isCleanSession = false + isCleanSession = true keepAliveInterval = 90 // Keep alive interval in seconds isAutomaticReconnect = true mqttVersion = MqttConnectOptions.MQTT_VERSION_3_1_1 diff --git a/servicing/src/main/java/com/za/ui/main/ServicingMainActivity.kt b/servicing/src/main/java/com/za/ui/main/ServicingMainActivity.kt index d114845..9c5cb13 100644 --- a/servicing/src/main/java/com/za/ui/main/ServicingMainActivity.kt +++ b/servicing/src/main/java/com/za/ui/main/ServicingMainActivity.kt @@ -46,20 +46,25 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.blankj.utilcode.util.ActivityUtils +import com.google.gson.Gson import com.za.base.BaseActivity import com.za.base.theme.bgColor import com.za.base.view.CommonButton import com.za.base.view.EmptyView import com.za.base.view.HeadView import com.za.base.view.LoadError +import com.za.bean.JpushBean import com.za.bean.db.order.OrderInfo import com.za.common.GlobalData +import com.za.common.log.LogUtil import com.za.common.util.DeviceUtil import com.za.ext.convertToFlowName import com.za.ext.copy import com.za.ext.finish import com.za.ext.goStatusPage +import com.za.service.PushListener import com.za.service.ServiceManager +import com.za.service.ZdPushServiceReceive import com.za.service.location.ZdLocationManager import com.za.servicing.R import kotlinx.coroutines.launch @@ -77,6 +82,47 @@ class ServicingMainActivity : BaseActivity() { ) } + override fun onResume() { + super.onResume() + setupPushMessageReceiver() + } + + private fun setupPushMessageReceiver() { // 注册推送消息接收器 + ServiceManager.registerPushListener(object : PushListener { + override fun broadcast(msg : String) { + sendMessageToMainProcess("broadcast", msg) + } + + override fun giveUpOrder(jpushBean : JpushBean) { + sendMessageToMainProcess("giveUp", Gson().toJson(jpushBean)) + } + + override fun importantTip(jpushBean : JpushBean) { + sendMessageToMainProcess("importantTip", Gson().toJson(jpushBean)) + } + + override fun newOrderMsg(jpushBean : JpushBean) { + sendMessageToMainProcess("newOrder", Gson().toJson(jpushBean)) + } + + override fun reDispatchOrder(jpushBean : JpushBean) { + sendMessageToMainProcess("reDispatch", Gson().toJson(jpushBean)) + } + + override fun revokeOrder(jpushBean : JpushBean) { + sendMessageToMainProcess("revoke", Gson().toJson(jpushBean)) + } + }) + } + + private fun sendMessageToMainProcess(type : String, message : String) { // 使用广播将消息发送到主进程 + val intent = Intent(ZdPushServiceReceive.RECEIVE_ACTION).setPackage(packageName) + intent.putExtra("type", type) + intent.putExtra("message", message) + sendBroadcast(intent) + LogUtil.print(TAG, "发送消息到主进程: $type") + } + companion object { fun goToMain(context : Context, driverName : String? = null, @@ -116,6 +162,7 @@ private fun ServicingMainScreen(jobCode : String? = null, scope.launch { ServiceManager.initialize(GlobalData.application) ZdLocationManager.startContinuousLocation(GlobalData.application) + } } diff --git a/servicing/src/main/java/com/za/ui/servicing/order_confirm/modify_money/ModifyMoneyViewModel.kt b/servicing/src/main/java/com/za/ui/servicing/order_confirm/modify_money/ModifyMoneyViewModel.kt index 4834706..7d0a35a 100644 --- a/servicing/src/main/java/com/za/ui/servicing/order_confirm/modify_money/ModifyMoneyViewModel.kt +++ b/servicing/src/main/java/com/za/ui/servicing/order_confirm/modify_money/ModifyMoneyViewModel.kt @@ -47,7 +47,7 @@ class ModifyMoneyViewModel : BaseVm() { userOrderId = userOrderId, taskId = taskId, unitPrice = it?.unitPrice, - mileage = it?.amount, + mileage = it?.mileage, mileageText = "${it?.mileage ?: ""}", calculateAmount = it?.calculateAmount, adjustAmount = it?.adjustAmount?.toFloat(),