refactor(servicing): 重构推送消息处理机制

- 新增 ZdPushServiceReceive 类用于集中处理推送消息
- 优化了消息处理逻辑,提高了代码的可维护性和可扩展性
-改进了消息在主进程中的广播发送方式
- 优化了语音播放逻辑,提高了播放的稳定性和流畅性
- 调整了通知渠道的创建和通知的发送方式
This commit is contained in:
songzhiling
2025-04-29 17:37:46 +08:00
parent 2f57b3e238
commit 22b0f14236
12 changed files with 453 additions and 281 deletions

View File

@ -4,6 +4,14 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-04-27T03:49:03.966203100Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=4f3d584c" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState> </SelectionState>
</selectionStates> </selectionStates>
</component> </component>

View File

@ -27,7 +27,7 @@ class MainActivity : ComponentActivity() {
.fillMaxSize() .fillMaxSize()
.clickable { .clickable {
val uri = 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) val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent) startActivity(intent)
} }

View File

@ -73,7 +73,7 @@ publishing {
release(MavenPublication) { release(MavenPublication) {
groupId = 'io.github.szl9' groupId = 'io.github.szl9'
artifactId = 'zd_servicing' artifactId = 'zd_servicing'
version = "1.0.1.9.9.25" version = "1.0.1.9.9.31"
pom { pom {
packaging = "aar" packaging = "aar"

View File

@ -256,6 +256,14 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver
android:name="com.za.service.ZdPushServiceReceive"
android:exported="false">
<intent-filter>
<action android:name="com.zd.servicing.PUSH_MESSAGE" />
</intent-filter>
</receiver>
<service <service
android:name="cn.jpush.android.service.DaemonService" android:name="cn.jpush.android.service.DaemonService"
android:enabled="true" android:enabled="true"

View File

@ -1,130 +1,58 @@
package com.za.base package com.za.base
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.content.Intent
import android.content.IntentFilter
import android.media.RingtoneManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.ActivityUtils
import com.google.gson.Gson import com.google.gson.Gson
import com.za.base.BaseActivityLifecycleCallbacks.Companion.getCurrentActivity
import com.za.bean.JpushBean import com.za.bean.JpushBean
import com.za.common.GlobalData
import com.za.common.log.LogUtil import com.za.common.log.LogUtil
import com.za.common.speech.SpeechManager import com.za.service.PushListener
import com.za.servicing.R import com.za.service.ServiceManager
import com.za.ui.servicing.order_give_up.OrderGiveUpActivity import com.za.service.ZdPushServiceReceive
import com.za.ui.view.CommonDialogFragment
open class PushMessageActivity : AppCompatActivity() { open class PushMessageActivity : AppCompatActivity() {
private var pushMessageReceiver : BroadcastReceiver? = null
private var currentDialog : AlertDialog? = null private var currentDialog : AlertDialog? = null
override fun onCreate(savedInstanceState : Bundle?) { override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (! GlobalData.isMaster && pushMessageReceiver == null) { setupPushMessageReceiver()
registerPushMessageReceiver(this)
}
} }
private fun registerPushMessageReceiver(context : Context) { private fun setupPushMessageReceiver() { // 注册推送消息接收器
pushMessageReceiver = object : BroadcastReceiver() { ServiceManager.registerPushListener(object : PushListener {
override fun onReceive(context : Context?, intent : Intent?) { override fun broadcast(msg : String) {
if (intent?.action == "com.za.rescue.dealer.PUSH_MESSAGE") { sendMessageToMainProcess("broadcast", msg)
val type = intent.getStringExtra("type") ?: return
val message = intent.getStringExtra("message") ?: return
LogUtil.print("PushActivityLifecycleCallbacks", "收到来自远程进程的消息: $type")
when (type) {
"broadcast" -> 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}")
}
}
}
}
} }
}
ContextCompat.registerReceiver(context, override fun giveUpOrder(jpushBean : JpushBean) {
pushMessageReceiver, sendMessageToMainProcess("giveUp", Gson().toJson(jpushBean))
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 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 private fun sendMessageToMainProcess(type : String, message : String) { // 使用广播将消息发送到主进程
return ActivityUtils.getActivityList().size <= 1 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() { override fun onPause() {
@ -132,18 +60,6 @@ open class PushMessageActivity : AppCompatActivity() {
dismissCurrentDialog() 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() { private fun dismissCurrentDialog() {
try { try {
currentDialog?.dismiss() 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 { companion object {
internal const val GIVE_UP_TYPE_NORMAL = 1 internal const val GIVE_UP_TYPE_NORMAL = 1
internal const val DIALOG_TAG_GIVE_UP = "giveUp" internal const val DIALOG_TAG_GIVE_UP = "giveUp"

View File

@ -95,10 +95,11 @@ object GlobalData : GlobalLocalData() {
var currentLocation : AMapLocation? = null var currentLocation : AMapLocation? = null
get() { get() {
return field return mmkv.decodeParcelable("currentLocation", AMapLocation::class.java)
} }
set(value) { set(value) {
value?.time = System.currentTimeMillis() value?.time= System.currentTimeMillis()
mmkv.encode("currentLocation", value)
field = value field = value
} }
@ -117,7 +118,7 @@ object GlobalData : GlobalLocalData() {
currentLocation = null currentLocation = null
driverInfoBean = null driverInfoBean = null
loginTime = null loginTime = null
// isLoginRecognition = null isLoginRecognition = null
} }
fun clearAllOrderCache() { fun clearAllOrderCache() {

View File

@ -1,10 +1,10 @@
package com.za.common.speech package com.za.common.speech
import android.app.Application import android.app.Application
import android.media.AudioAttributes
import android.media.AudioManager import android.media.AudioManager
import android.media.MediaPlayer import android.media.MediaPlayer
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.ThreadUtils
import com.za.base.AppConfig import com.za.base.AppConfig
import com.za.bean.request.AppNewOrderVoiceRequest import com.za.bean.request.AppNewOrderVoiceRequest
import com.za.common.GlobalData import com.za.common.GlobalData
@ -16,7 +16,11 @@ import com.za.room.db.user.LocalResourceBean
import com.za.servicing.R import com.za.servicing.R
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers 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 { object SpeechManager {
private var mContext : Application? = null private var mContext : Application? = null
@ -65,6 +69,7 @@ object SpeechManager {
} }
val audioManager = val audioManager =
ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java) ContextCompat.getSystemService(GlobalData.application, AudioManager::class.java)
originVolume = audioManager?.getStreamVolume(AudioManager.STREAM_MUSIC) ?: 0
val maxVolume = audioManager?.getStreamMaxVolume(AudioManager.STREAM_MUSIC) val maxVolume = audioManager?.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager?.setStreamVolume(AudioManager.STREAM_MUSIC,
maxVolume ?: 1, maxVolume ?: 1,
@ -127,60 +132,89 @@ object SpeechManager {
} }
private fun playNewOrder() { private fun playNewOrder() {
stopPlayMedia() try { // 先释放之前的MediaPlayer实例避免资源泄漏
setMaxAudioVolume() releaseMediaPlayer()
mediaPlayer = MediaPlayer.create(mContext, R.raw.neworder)
mediaPlayer?.start() // 创建新的MediaPlayer实例
mediaPlayer?.setOnCompletionListener { mediaPlayer = MediaPlayer.create(GlobalData.application, R.raw.neworder)
resetAudioVolume() 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) { private fun playNewOrderFromNet(url : String) {
try { try { // 先释放之前的MediaPlayer实例
stopPlayMedia() LogUtil.print("playNewOrderFromNet", "播放新订单语音: $url")
setMaxAudioVolume() releaseMediaPlayer() // 创建新的MediaPlayer实例
mediaPlayer = MediaPlayer() mediaPlayer = MediaPlayer()
mediaPlayer?.setAudioStreamType(AudioManager.STREAM_MUSIC) // 设置音频流类型 mediaPlayer?.setAudioAttributes(AudioAttributes.Builder()
mediaPlayer?.setDataSource(url) // 设置音频文件的 URL .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
mediaPlayer?.prepareAsync() // 异步准备音频 .setUsage(AudioAttributes.USAGE_MEDIA).build())
// 准备完成后开始播放
mediaPlayer?.setOnPreparedListener { obj : MediaPlayer -> obj.start() } mediaPlayer?.setDataSource(url)
mediaPlayer?.setOnErrorListener { mp : MediaPlayer?, what : Int, extra : Int -> mediaPlayer?.setOnPreparedListener { // 设置最大音量
playNewOrder() setMaxAudioVolume() // 准备完成后开始播放
false it.start()
} }
mediaPlayer?.setOnCompletionListener { mediaPlayer?.setOnCompletionListener { // 播放完成后释放资源
resetAudioVolume() releaseMediaPlayer()
} }
mediaPlayer?.setOnErrorListener { mp, what, extra ->
LogUtil.print("playNewOrderFromNet", "MediaPlayer错误: what=$what, extra=$extra")
releaseMediaPlayer()
true
}
// 异步准备避免阻塞UI线程
mediaPlayer?.prepareAsync()
} catch (e : Exception) { } catch (e : Exception) {
playNewOrder() LogUtil.print("playNewOrderFromNet", "播放出错: ${e.message}")
LogUtil.print("播放新订单失败", e) releaseMediaPlayer()
} }
} }
fun stopPlayMedia() { // 安全释放MediaPlayer资源
ThreadUtils.runOnUiThread { fun releaseMediaPlayer() {
if (null != mediaPlayer) { try {
mediaPlayer?.stop() mediaPlayer?.let {
mediaPlayer?.release() if (it.isPlaying) {
mediaPlayer = null it.stop()
}
it.reset()
it.release()
} }
mediaPlayer = null // 恢复原始音量
resetAudioVolume()
} catch (e : Exception) {
LogUtil.print("releaseMediaPlayer", "释放MediaPlayer出错: ${e.message}")
} }
} }
fun speechNewOrderSound(content : String?) { fun speechNewOrderSound(content : String?) {
if (content.isNullOrBlank()) { if (content.isNullOrBlank()) {
speechNewOrderLooper("您有新的中道救援订单,请尽快接单!") { playNewOrder() } speechNewOrderLocalSoundLooper()
return return
} }
val localResourceDao = RoomHelper.db?.localResourceDao() val localResourceDao = RoomHelper.db?.localResourceDao()
val localUrlResource = localResourceDao?.getLocalResourceByName(content) val localUrlResource = localResourceDao?.getLocalResourceByName(content)
if (localUrlResource != null && ! localUrlResource.resourceUrl.isNullOrBlank()) { if (localUrlResource != null && ! localUrlResource.resourceUrl.isNullOrBlank()) {
speechNewOrderLooper(content) { speechNewOrderNetLooper(content, localUrlResource.resourceUrl ?: "")
playNewOrderFromNet(localUrlResource.resourceUrl)
}
LogUtil.print("handlerNewOrderVoice", "播放本地语音"); LogUtil.print("handlerNewOrderVoice", "播放本地语音");
return return
} }
@ -191,28 +225,59 @@ object SpeechManager {
.subscribe(object : BaseObserver<String>() { .subscribe(object : BaseObserver<String>() {
override fun doSuccess(it : String?) { override fun doSuccess(it : String?) {
if (it == null) { if (it == null) {
speechNewOrderLooper(content) { playNewOrder() } speechNewOrderLocalSoundLooper()
return return
} }
localResourceDao?.insert(LocalResourceBean(resourceName = content, localResourceDao?.insert(LocalResourceBean(resourceName = content,
resourceType = 1, resourceType = 1,
resourceUrl = it)) resourceUrl = it))
speechNewOrderLooper(content) { playNewOrderFromNet(it) } speechNewOrderNetLooper(content, it)
} }
override fun doFailure(code : Int, msg : String?) { 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() val startTime = System.currentTimeMillis()
while (System.currentTimeMillis() - startTime < 1000 * 60 * 3 && GlobalData.isHandlerNewOrder == false) { while (System.currentTimeMillis() - startTime < 1000 * 60 * 3 && GlobalData.isHandlerNewOrder == false) {
play() try {
Thread.sleep(250L * content.length) 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
} }
} }
} }

View File

@ -30,7 +30,7 @@ interface PushListener {
fun importantTip(jpushBean : JpushBean) 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 { object ServiceManager {
@Volatile @Volatile
@ -115,21 +115,21 @@ object ServiceManager {
// 优化后的重复消息判断 // 优化后的重复消息判断
lastJPushBean?.let { 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})") LogUtil.print("MessageHandler", "Duplicate message detected (hash: ${msg})")
return return
} }
} }
if (msg.startsWith("broadcast:")) { if (msg.startsWith("broadcast:")) {
lastJPushBean = LastJPushBean(msg = msg.hashCode()) lastJPushBean = LastJPushBean(msg = msg)
handleBroadcast(msg) // PushMessageLiveData.postPushMessage(msg) handleBroadcast(msg)
return return
} }
try { try {
lastJPushBean = LastJPushBean(msg = msg.hashCode()) lastJPushBean = LastJPushBean(msg = msg)
val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java) val jpushOrderInfoBean = Gson().fromJson(msg, JpushBean::class.java)
sendSystemNotificationFromMessage(jpushOrderInfoBean) // PushMessageLiveData.postPushMessage(msg) sendSystemNotificationFromMessage(jpushOrderInfoBean)
when (jpushOrderInfoBean.pushType) { when (jpushOrderInfoBean.pushType) {
0 -> newOrderMsg(jpushOrderInfoBean) 0 -> newOrderMsg(jpushOrderInfoBean)
1 -> handleTypeOneMessage(jpushOrderInfoBean) 1 -> handleTypeOneMessage(jpushOrderInfoBean)
@ -138,7 +138,7 @@ object ServiceManager {
"Unknown push type: ${jpushOrderInfoBean.pushType}") "Unknown push type: ${jpushOrderInfoBean.pushType}")
} }
} catch (e : Exception) { } catch (e : Exception) {
if (msg.startsWith("broadcast:")) { // PushMessageLiveData.postPushMessage(msg) if (msg.startsWith("broadcast:")) {
handleBroadcast(msg) handleBroadcast(msg)
} }
LogUtil.print("JpushMessage", "Error handling message: ${e.message}") LogUtil.print("JpushMessage", "Error handling message: ${e.message}")

View File

@ -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)
}
}

View File

@ -18,7 +18,7 @@ class ConnectionOptionWrapper(
*/ */
@JvmField @JvmField
val mqttConnectOptions: MqttConnectOptions = MqttConnectOptions().apply { val mqttConnectOptions: MqttConnectOptions = MqttConnectOptions().apply {
isCleanSession = false isCleanSession = true
keepAliveInterval = 90 // Keep alive interval in seconds keepAliveInterval = 90 // Keep alive interval in seconds
isAutomaticReconnect = true isAutomaticReconnect = true
mqttVersion = MqttConnectOptions.MQTT_VERSION_3_1_1 mqttVersion = MqttConnectOptions.MQTT_VERSION_3_1_1

View File

@ -46,20 +46,25 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.blankj.utilcode.util.ActivityUtils import com.blankj.utilcode.util.ActivityUtils
import com.google.gson.Gson
import com.za.base.BaseActivity import com.za.base.BaseActivity
import com.za.base.theme.bgColor import com.za.base.theme.bgColor
import com.za.base.view.CommonButton import com.za.base.view.CommonButton
import com.za.base.view.EmptyView import com.za.base.view.EmptyView
import com.za.base.view.HeadView import com.za.base.view.HeadView
import com.za.base.view.LoadError import com.za.base.view.LoadError
import com.za.bean.JpushBean
import com.za.bean.db.order.OrderInfo import com.za.bean.db.order.OrderInfo
import com.za.common.GlobalData import com.za.common.GlobalData
import com.za.common.log.LogUtil
import com.za.common.util.DeviceUtil import com.za.common.util.DeviceUtil
import com.za.ext.convertToFlowName import com.za.ext.convertToFlowName
import com.za.ext.copy import com.za.ext.copy
import com.za.ext.finish import com.za.ext.finish
import com.za.ext.goStatusPage import com.za.ext.goStatusPage
import com.za.service.PushListener
import com.za.service.ServiceManager import com.za.service.ServiceManager
import com.za.service.ZdPushServiceReceive
import com.za.service.location.ZdLocationManager import com.za.service.location.ZdLocationManager
import com.za.servicing.R import com.za.servicing.R
import kotlinx.coroutines.launch 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 { companion object {
fun goToMain(context : Context, fun goToMain(context : Context,
driverName : String? = null, driverName : String? = null,
@ -116,6 +162,7 @@ private fun ServicingMainScreen(jobCode : String? = null,
scope.launch { scope.launch {
ServiceManager.initialize(GlobalData.application) ServiceManager.initialize(GlobalData.application)
ZdLocationManager.startContinuousLocation(GlobalData.application) ZdLocationManager.startContinuousLocation(GlobalData.application)
} }
} }

View File

@ -47,7 +47,7 @@ class ModifyMoneyViewModel : BaseVm<Action, UiState>() {
userOrderId = userOrderId, userOrderId = userOrderId,
taskId = taskId, taskId = taskId,
unitPrice = it?.unitPrice, unitPrice = it?.unitPrice,
mileage = it?.amount, mileage = it?.mileage,
mileageText = "${it?.mileage ?: ""}", mileageText = "${it?.mileage ?: ""}",
calculateAmount = it?.calculateAmount, calculateAmount = it?.calculateAmount,
adjustAmount = it?.adjustAmount?.toFloat(), adjustAmount = it?.adjustAmount?.toFloat(),