| | |
| | | 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 |
| | |
| | | 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") |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | @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:消息内容 |
| | |
| | | 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) |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | 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") |
| | |
| | | } |
| | | } |
| | | |
| | | 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}") |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |