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>
<SelectionState runConfigName="app">
<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>
</selectionStates>
</component>

View File

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

View File

@ -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"

View File

@ -256,6 +256,14 @@
</intent-filter>
</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
android:name="cn.jpush.android.service.DaemonService"
android:enabled="true"

View File

@ -1,130 +1,58 @@
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.IntentFilter
import android.media.RingtoneManager
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.ActivityUtils
import com.google.gson.Gson
import com.za.base.BaseActivityLifecycleCallbacks.Companion.getCurrentActivity
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
import com.za.service.PushListener
import com.za.service.ServiceManager
import com.za.service.ZdPushServiceReceive
open class PushMessageActivity : AppCompatActivity() {
private var pushMessageReceiver : BroadcastReceiver? = null
private var currentDialog : AlertDialog? = null
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
if (! GlobalData.isMaster && pushMessageReceiver == null) {
registerPushMessageReceiver(this)
}
setupPushMessageReceiver()
}
private fun registerPushMessageReceiver(context : Context) {
pushMessageReceiver = object : BroadcastReceiver() {
override fun onReceive(context : Context?, intent : Intent?) {
if (intent?.action == "com.za.rescue.dealer.PUSH_MESSAGE") {
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}")
}
private fun setupPushMessageReceiver() { // 注册推送消息接收器
ServiceManager.registerPushListener(object : PushListener {
override fun broadcast(msg : String) {
sendMessageToMainProcess("broadcast", msg)
}
"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}")
}
override fun giveUpOrder(jpushBean : JpushBean) {
sendMessageToMainProcess("giveUp", Gson().toJson(jpushBean))
}
"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}")
}
override fun importantTip(jpushBean : JpushBean) {
sendMessageToMainProcess("importantTip", Gson().toJson(jpushBean))
}
"revoke" -> {
try {
handleRevokeOrder()
} catch (e : Exception) {
LogUtil.print("PushActivityLifecycleCallbacks",
"处理订单撤回消息失败: ${e.message}")
}
}
}
}
}
override fun newOrderMsg(jpushBean : JpushBean) {
sendMessageToMainProcess("newOrder", Gson().toJson(jpushBean))
}
ContextCompat.registerReceiver(context,
pushMessageReceiver,
IntentFilter("com.za.rescue.dealer.PUSH_MESSAGE"),
ContextCompat.RECEIVER_NOT_EXPORTED)
LogUtil.print("PushActivityLifecycleCallbacks", "注册推送消息接收器")
override fun reDispatchOrder(jpushBean : JpushBean) {
sendMessageToMainProcess("reDispatch", Gson().toJson(jpushBean))
}
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 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"

View File

@ -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()
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() {

View File

@ -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()
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 = MediaPlayer.create(mContext, R.raw.neworder)
// 开始播放
mediaPlayer?.start()
mediaPlayer?.setOnCompletionListener {
resetAudioVolume()
} 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<String>() {
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
}
}
}

View File

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

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
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

View File

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

View File

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