puzhibing
2026-08-21 5fa94b3718b4a78a4fcea4c97652b0ef20b7de3a
app/src/main/java/com/xw/yunwei/push/MessageHandler.kt
@@ -1,28 +1,27 @@
package com.xw.yunwei.push
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
import android.view.View.inflate
import android.widget.ImageView
import android.widget.TextView
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.lifecycleScope
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView
import com.afollestad.materialdialogs.customview.getCustomView
import com.google.android.material.tabs.TabLayout
import com.blankj.utilcode.util.AppUtils
import com.xw.common.net.UpdateManager
import com.xw.common.net.interceptor.ApiException
import com.xw.common.utils.KeyPreferences
import com.xw.common.utils.LogUtils
import com.xw.common.utils.MMKVManager
import com.xw.common.utils.ToastUtilCommon
import com.xw.yunwei.MyApp
import com.xw.yunwei.R
import com.xw.yunwei.databinding.DialogProtocolBinding
import com.xw.yunwei.model.AgreeInfo
import com.xw.yunwei.model.SwitchTabMain
import com.xw.yunwei.model.SwitchTabTask
import com.xw.yunwei.net.TaskRepository
import com.xw.yunwei.net.UserRepository
import com.xw.yunwei.ui.MainActivity
import com.xw.yunwei.ui.task.TaskVM
@@ -30,24 +29,35 @@
import com.xw.yunwei.ui.task.normal.NormalTaskDetailActivity
import com.xw.yunwei.ui.user.mine.protocol.ProtocolActivity
import com.xw.yunwei.ui.user.mine.scheduling.SchedulingActivity
import com.xw.yunwei.ui.work.car.detail.StandardDescriptionActivity
import com.xw.yunwei.utils.DialogFactory
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.json.JSONObject
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import kotlin.getValue
import java.io.IOException
import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
object MessageHandler : KoinComponent {
    var TAG = "zmc-${this.javaClass.simpleName}"
    val userRepository: UserRepository by inject()
    val taskRepository: TaskRepository by inject()
    fun handlePushEvent(content: String?, isPush: Boolean = false) {
        try {
            handlePushEvent(JSONObject(content ?: ""), isPush)
        } catch (e: Exception) {
            ToastUtilCommon.showError("未获取到消息详情")
            LogUtils.e("zmc-MessageHandler","handlePushEvent content = $content error = $e")
            LogUtils.e("zmc-MessageHandler", "handlePushEvent content = $content error = $e")
        }
    }
@@ -59,7 +69,7 @@
    @OptIn(DelicateCoroutinesApi::class)
    fun handlePushEvent(jsob: JSONObject, isPush: Boolean) {
        LogUtils.i("zmc-MessageHandler","handlePushEvent $jsob isPush = $isPush")
        LogUtils.i("zmc-MessageHandler", "handlePushEvent $jsob isPush = $isPush")
        TaskVM.needRefresh.value = true
        /**
         * content:消息内容
@@ -71,67 +81,88 @@
        val strategy = jsob.optInt("strategy")
        val type = jsob.optInt("type")
        val id = jsob.optString("id")
        GlobalScope.launch {
        handleMsg(strategy, id, isPush)
    }
    private fun handleMsg(
        strategy: Int,
        id: String,
        isPush: Boolean
    ) {
        runOnMainThread {
            val currentShowActivity = MyApp.myApp.getCurrentShowActivity()
            LogUtils.i(
                "zmc-MessageHandler",
                "handlePushEvent ${currentShowActivity.javaClass.name}"
                "zmc-MessageHandler", "handlePushEvent ${currentShowActivity.javaClass.name}"
            )
            handleMsg(currentShowActivity, strategy, id, isPush)
            when (strategy) {
                1, 2, 3, 5 -> { // 1=新增事故,2=事故即将超时提示,3=事故超时提示,5=事故状态变更 点击都跳事故详情
                    if (strategy == 5 && isPush) return@runOnMainThread
                    checkTaskCouldInDetail(id) {
                        if (!it) {
                            ToastUtilCommon.showSuc("事故处理完成,已归档")
                            return@checkTaskCouldInDetail
                        }
                        currentShowActivity.startActivity(
                            Intent(currentShowActivity, AccidentDetailActivity::class.java).apply {
                                putExtra("taskId", id.toLong())
                            })
                    }
                }
                4 -> { // 排班信息变动
                    currentShowActivity.startActivity(
                        Intent(currentShowActivity, SchedulingActivity::class.java)
                    )
                }
                6, 7 -> { // 6=新增任务,7=任务过期提示  点击都跳任务详情
                    if (isPush) return@runOnMainThread
                    if (strategy == 6 && id == "0") { // 多条任务 跳转任务中心待处理页面
                        EventBus.getDefault().post(SwitchTabMain(1))
                        EventBus.getDefault().post(SwitchTabTask(2))
                        currentShowActivity.startActivity(
                            Intent(
                                currentShowActivity,
                                MainActivity::class.java
                            )
                        )
                    } else {
                        currentShowActivity.startActivity(
                            Intent(
                                currentShowActivity,
                                NormalTaskDetailActivity::class.java
                            ).apply {
                                putExtra("taskId", id.toLong())
                            })
                    }
                }
                8, 12 -> { // 升级
                    if (strategy == 8 && isPush) return@runOnMainThread
                    showUpdateDialog(currentShowActivity)
                }
                9, 10 -> { // 变更协议内容
                    if (strategy == 9 && isPush) return@runOnMainThread
                    if (strategy == 9) {
                        currentShowActivity.startActivity(
                            Intent(currentShowActivity, ProtocolActivity::class.java)
                        )
                    } else {
                        MMKVManager.getInstance().saveBoolean(KeyPreferences.NEED_READ, true)
                        showProtocolDialog(currentShowActivity, true)
                    }
                }
            }
        }
    }
    private fun handleMsg(currentShowActivity: Activity, strategy: Int, id: String, isPush: Boolean) {
        when (strategy) {
            1, 2, 3, 5 -> { // 1=新增事故,2=事故即将超时提示,3=事故超时提示,5=事故状态变更 点击都跳事故详情
                if(strategy == 5 && isPush) return
                currentShowActivity.startActivity(
                    Intent(currentShowActivity, AccidentDetailActivity::class.java).apply {
                        putExtra("taskId", id.toLong())
                    }
                )
            }
            4 -> { // 排班信息变动
                currentShowActivity.startActivity(
                    Intent(currentShowActivity, SchedulingActivity::class.java)
                )
            }
            6, 7 -> { // 6=新增任务,7=任务过期提示  点击都跳任务详情
                if (isPush) return
                if (strategy == 6 && id == "0") { // 多条任务 跳转任务中心待处理页面
                    EventBus.getDefault().post(SwitchTabMain(1))
                    EventBus.getDefault().post(SwitchTabTask(2))
                    currentShowActivity.startActivity(
                        Intent(
                            currentShowActivity,
                            MainActivity::class.java
                        )
                    )
                } else {
                    currentShowActivity.startActivity(
                        Intent(currentShowActivity, NormalTaskDetailActivity::class.java).apply {
                            putExtra("taskId", id.toLong())
                        }
                    )
                }
            }
            8, 12 -> { // 升级
                if(strategy == 8 && isPush) return
                showUpdateDialog(currentShowActivity)
            }
            9, 10 -> { // 变更协议内容
                if (strategy == 9 && isPush) return
                if (strategy == 9) {
                    currentShowActivity.startActivity(
                        Intent(currentShowActivity, ProtocolActivity::class.java)
                    )
                } else {
                    MMKVManager.getInstance().saveBoolean(KeyPreferences.NEED_READ, true)
                    showProtocolDialog(currentShowActivity, true)
    //检测任务是否能够进入详情页
    private fun checkTaskCouldInDetail(taskId: String, block: (could: Boolean) -> Unit) {
        launchRequest {
            taskRepository.vehicleAccidentDetail(taskId)?.also {
                runOnMainThread {
                    block.invoke(it.status != 2)
                }
            }
        }
@@ -148,12 +179,12 @@
    }
    fun showUpdateDialog(currentShowActivity: Activity) {
        (currentShowActivity as AppCompatActivity).lifecycleScope.launch {
        launchRequest {
            userRepository.getVersion()?.let {
                Handler(Looper.getMainLooper()).post {
                runOnMainThread {
                    UpdateManager.instance!!
                        .setContext(currentShowActivity)
                        .apkUrl(it.androidFile ?: "")
                        .apkUrl(it.getDownloadUrl() ?: "")
                        .updateContent(it.updateDescription ?: "")
                        .updateVersionCode(it.versionCode ?: 1)
                        .updateVersionName(it.version ?: "1.0")
@@ -164,113 +195,188 @@
        }
    }
    fun showProtocolDialog(currentShowActivity: Activity, forced: Boolean) {
        Handler(Looper.getMainLooper()).post {
            MaterialDialog(currentShowActivity).cornerRadius(15f).cancelable(!forced).show {
                customView(
                    com.xw.yunwei.R.layout.dialog_protocol,
                    noVerticalPadding = true,
                    dialogWrapContent = true
                )
                DataBindingUtil.bind<DialogProtocolBinding>(getCustomView())?.let { bind ->
//                    val tab1 = bind.protocolLayout.tabLayout.newTab().apply { text = "用户协议" }
//                    val tab2 = bind.protocolLayout.tabLayout.newTab().apply { text = "隐私条款" }
                    val tab1 = bind.protocolLayout.tabLayout.newTab().apply { text = "小鹏Robotaxi...隐私政策" }
                    val tab2 = bind.protocolLayout.tabLayout.newTab().apply { text = "第三方共享信息清单" }
                    val tab3 = bind.protocolLayout.tabLayout.newTab().apply { text = "个人信息收集清单" }
                    val tab4 = bind.protocolLayout.tabLayout.newTab().apply { text = "权限调用" }
                    bind.protocolLayout.tabLayout.addTab(tab1, true)
                    bind.protocolLayout.tabLayout.addTab(tab2, false)
                    bind.protocolLayout.tabLayout.addTab(tab3, false)
                    bind.protocolLayout.tabLayout.addTab(tab4, false)
                    // bind.btnLeft.text = if (forced) "取消并退出" else "关闭"
                    bind.btnLeft.text = "关闭"
                    bind.btnLeft.setOnClickListener {
                        dismiss()
                        if (forced) {
                            currentShowActivity.finishAffinity()
                            android.os.Process.killProcess(android.os.Process.myPid())
                        }
                    }
                    bind.btnRight.setOnClickListener {
                        MMKVManager.getInstance().saveBoolean(KeyPreferences.NEED_READ, false)
                        dismiss()
                    }
                    initWeb(bind.protocolLayout.web)
                    val getProtocol: (Int) -> Unit = { type ->
                        (currentShowActivity as AppCompatActivity).lifecycleScope.launch {
                            val data = userRepository.getProtocol(type)
                            Handler(Looper.getMainLooper()).post {
                                setContent(bind.protocolLayout.web, data?.content ?: "")
                            }
                        }
                    }
                    getProtocol(8)
                    bind.protocolLayout.tabLayout.addOnTabSelectedListener(object :
                        TabLayout.OnTabSelectedListener {
                        override fun onTabSelected(tab: TabLayout.Tab?) {
                            tab?.let {
                                when (it.text) {
                                    "用户协议" -> getProtocol(1)
                                    "隐私条款" -> getProtocol(2)
                                    "小鹏Robotaxi...隐私政策" -> getProtocol(8)
                                    "第三方共享信息清单" -> getProtocol(9)
                                    "个人信息收集清单" -> getProtocol(10)
                                    "权限调用" -> getProtocol(11)
                                    else -> {}
                                }
                            }
                        }
                        override fun onTabUnselected(tab: TabLayout.Tab?) {}
                        override fun onTabReselected(tab: TabLayout.Tab?) {}
                    })
    fun showProtocolDialog(
        currentShowActivity: Activity,
        forced: Boolean,
        dismissBlock: (() -> Unit)? = null
    ) {
        launchRequest {
            userRepository.getAgreeConfig()?.also { list ->
                runOnMainThread {
                    showProtocolDialog(currentShowActivity, forced, list, dismissBlock)
                }
            }
        }
    }
    @SuppressLint("SetJavaScriptEnabled")
    private fun initWeb(webView: WebView) {
        //声明WebSettings子类
        val webSettings = webView.settings
        webSettings.domStorageEnabled = true //设置适应Html5
        //如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
        webSettings.javaScriptEnabled = true //允许js调用
        webSettings.allowFileAccess = true //在File域下,能够执行任意的JavaScript代码,同源策略跨域访问能够对私有目录文件进行访问等
        webSettings.allowContentAccess = true //是否允许在WebView中访问内容URL(Content Url),默认允许
        //缩放操作
        webSettings.setSupportZoom(true) //支持缩放,默认为true。是下面那个的前提。
        webSettings.builtInZoomControls = true //设置内置的缩放控件。若为false,则该WebView不可缩放
        webSettings.displayZoomControls = false //隐藏原生的缩放控件
        //其他细节操作
        webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK //关闭webview中缓存
        webSettings.allowFileAccess = true //设置可以访问文件
        webSettings.javaScriptCanOpenWindowsAutomatically = true //支持通过JS打开新窗口
        webSettings.loadsImagesAutomatically = true //支持自动加载图片
        webSettings.defaultTextEncodingName = "utf-8" //设置编码格式
    }
    @SuppressLint("SetJavaScriptEnabled")
    private fun setContent(webView: WebView, content: String?) {
        content?.let {
            // 加载并显示HTML代码
            webView.loadDataWithBaseURL(
                null,
                content,
                "text/html",
                "utf-8",
                null
    //显示隐私弹窗
    private fun showProtocolDialog(
        currentShowActivity: Activity,
        forced: Boolean,
        list: List<AgreeInfo>,
        dismissBlock: (() -> Unit)? = null
    ) {
        DialogFactory.produce(currentShowActivity).cornerRadius(15f).cancelable(!forced).show {
            customView(
                com.xw.yunwei.R.layout.dialog_protocol,
                noVerticalPadding = true,
                dialogWrapContent = true
            )
            cornerRadius(8f)
            DataBindingUtil.bind<DialogProtocolBinding>(getCustomView())?.let { bind ->
                val checked = mutableSetOf<AgreeInfo>()
                fun refreshCheck(imageView: ImageView, checked: Boolean) {
                    imageView.setImageResource(if (checked) R.mipmap.icon_tick_circle_blue else R.mipmap.icon_circle_gray2)
                }
                fun checkAllChecked() {
                    (0..<bind.llAgree.childCount).forEachIndexed { index, i ->
                        val checked = checked.contains(list[index])
                        refreshCheck(
                            bind.llAgree.getChildAt(index).findViewById<ImageView>(R.id.iv_check),
                            checked
                        )
                    }
                    refreshCheck(bind.ivCheckAll, checked.size == list.size)
                }
                list.forEach { info ->
                    val itemView = inflate(
                        currentShowActivity,
                        R.layout.item_protocol,
                        null
                    ).apply {
                        findViewById<ImageView>(R.id.iv_check).apply {
                            setOnClickListener {
                                if (checked.contains(info)) {
                                    checked.remove(info)
                                } else {
                                    checked.add(info)
                                }
                                checkAllChecked()
                            }
                        }
                        findViewById<TextView>(R.id.tv_name).apply {
                            text = info.name
                            setOnClickListener {
//                                if (!checked.contains(info)) {
//                                    checked.add(info)
//                                    checkAllChecked()
//                                }
                                currentShowActivity.startActivity(
                                    Intent(
                                        currentShowActivity,
                                        StandardDescriptionActivity::class.java
                                    ).apply {
                                        putExtra("type", info.id?.toInt())
                                        putExtra("webType", 1)
                                        putExtra("title", info.name)
                                    }
                                )
                            }
                        }
                    }
                    bind.llAgree.addView(itemView)
                }
                bind.llCheckAll.setOnClickListener {
                    if (checked.size != list.size) {
                        checked.addAll(list)
                    } else {
                        checked.clear()
                    }
                    checkAllChecked()
                }
                bind.btnLeft.setOnClickListener {
                    dismiss()
                    if (forced) {
                        currentShowActivity.finishAffinity()
                        android.os.Process.killProcess(android.os.Process.myPid())
                    }
                }
                bind.btnRight.setOnClickListener {
                    if (checked.size != list.size) {
                        ToastUtilCommon.showError("请勾选全部协议后再确认同意")
                        return@setOnClickListener
                    }
                    MMKVManager.getInstance().saveBoolean(KeyPreferences.NEED_READ, false)
                    dismiss()
                }
            }
            setOnDismissListener { dismissBlock?.invoke() }
        }
    }
    private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
        LogUtils.e(TAG, "Uncaught coroutine exception: ${throwable.message}")
        runOnMainThread {
            ToastUtilCommon.showError(getNetworkErrorMessage(throwable))
        }
    }
    private suspend fun errorHandling(
        e: Throwable,
        error: suspend (code: Int, msg: String) -> Unit
    ) {
        if (e is ApiException) {
            when {
                e.isTokenExpired() -> {
                    MMKVManager.getInstance().clear()
                    ToastUtilCommon.showWarn("登录已过期,请重新登录")
                    CoroutineScope(Dispatchers.IO + exceptionHandler).launch {
                        delay(1000)
                        AppUtils.relaunchApp(true)
                    }
                }
                else -> {
                    error.invoke(e.code, e.message ?: "")
                }
            }
        } else {
            val errorMsg = getNetworkErrorMessage(e)
            error.invoke(-1, errorMsg)
        }
    }
    /**
     * 获取网络异常的友好提示信息
     */
    private fun getNetworkErrorMessage(e: Throwable): String {
        return when (e) {
            is UnknownHostException -> "网络连接失败,请检查网络设置"
            is ConnectException -> "无法连接到服务器,请检查网络"
            is SocketTimeoutException -> "连接超时,请稍后重试"
            is SocketException -> "网络异常,请检查网络连接"
            is IOException -> "网络请求失败,请检查网络"
            else -> e.message ?: "未知错误"
        }
    }
    //运行在主线程
    private fun runOnMainThread(block: suspend () -> Unit) {
        CoroutineScope(Dispatchers.Main + exceptionHandler).launch {
            block()
        }
    }
    private fun launchRequest(
        error: suspend (code: Int, msg: String) -> Unit = { code, msg ->
            ToastUtilCommon.showError(msg)
            LogUtils.e(TAG, "launchRequest: code = $code msg = $msg")
        }, request: suspend () -> Unit
    ) {
        CoroutineScope(Dispatchers.IO + exceptionHandler).launch {
            try {
                request()
            } catch (e: Throwable) {
                try {
                    errorHandling(e, error)
                } catch (handlingError: Throwable) {
                    LogUtils.e(TAG, "Error handling failed: ${handlingError.message}")
                }
            }
        }
    }
}