From 05b86f677d5406e4a688c2ca50cdf4de731636ef Mon Sep 17 00:00:00 2001 From: yangyakun Date: Sat, 12 Oct 2024 15:34:38 +0800 Subject: [PATCH 1/4] =?UTF-8?q?[6.7.0]=20[fix]=20[=E5=88=B0=E7=AB=99?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=BF=AE=E6=94=B9]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mogo/och/weaknet/repository/impl/ShuttleSaasRepository.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OCH/shuttle/driver_weaknet/src/main/java/com/mogo/och/weaknet/repository/impl/ShuttleSaasRepository.kt b/OCH/shuttle/driver_weaknet/src/main/java/com/mogo/och/weaknet/repository/impl/ShuttleSaasRepository.kt index b4e1c233ef..cc61dfaf56 100644 --- a/OCH/shuttle/driver_weaknet/src/main/java/com/mogo/och/weaknet/repository/impl/ShuttleSaasRepository.kt +++ b/OCH/shuttle/driver_weaknet/src/main/java/com/mogo/och/weaknet/repository/impl/ShuttleSaasRepository.kt @@ -219,8 +219,8 @@ class ShuttleSaasRepository : IRepository { EventDb.saveEventTaskArriveSite( task.taskId!!, task.lineId!!, - start.siteId.toLong(), - start.seq, + end.siteId.toLong(), + end.seq, task.taskStartTime, lineInfo.lineName ) From 3b9061a2165205dc26dd5d0eb0ec2a1c9473101f Mon Sep 17 00:00:00 2001 From: yangyakun Date: Sat, 12 Oct 2024 16:18:44 +0800 Subject: [PATCH 2/4] =?UTF-8?q?[6.7.0]=20[fea]=20[=E5=BD=93=E5=89=8D?= =?UTF-8?q?=E7=9B=B8=E4=B9=98=20=E8=99=9A=E6=8B=9F=E5=8D=95=E5=B1=95?= =?UTF-8?q?=E7=A4=BA]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/module/wigets/CommonSlideView.kt | 2 +- .../module/wigets/autopilot/AutopilotState.kt | 10 +- .../wigets/autopilot/AutopilotStateModel.kt | 6 +- .../module/wigets/commonview/EmptyView.kt | 55 ++ .../wigets/map/orderstatus/OrderStatusView.kt | 22 +- .../map/orderstatus/OrderStatusViewModel.kt | 2 + .../common/src/main/res/values/attrs.xml | 4 + .../och/unmanned/taxi/ui/base/TaxiFragment.kt | 23 +- .../och/unmanned/taxi/ui/debug/DebugView.kt | 6 +- .../TaxiOperationalDialogFragment.kt | 3 +- .../unmanned/taxi/ui/task/TaxiTaskModel.kt | 16 +- .../itinerarycurrent/ItineraryCurrentModel.kt | 486 ++++++++++- .../itinerarycurrent/ItineraryCurrentView.kt | 767 +++++++++++++++++- .../taxi/wigets/TaxiSelectViewGroup.kt | 2 +- .../res/layout/unmanned_itinerary_current.xml | 203 ++++- .../res/layout/unmanned_switch_itinerary.xml | 6 +- .../layout/unmanned_taxi_base_fragment.xml | 9 + .../main/res/layout/unmanned_taxi_panel.xml | 7 - .../src/main/res/values/colors.xml | 1 + 19 files changed, 1550 insertions(+), 80 deletions(-) create mode 100644 OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/commonview/EmptyView.kt diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/CommonSlideView.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/CommonSlideView.kt index 69e257067f..1b4ea64f73 100644 --- a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/CommonSlideView.kt +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/CommonSlideView.kt @@ -130,7 +130,7 @@ class CommonSlideView @JvmOverloads constructor( if(isVisible){ lottie_bg.setAnimation("slide.json") lottie_bg.playAnimation() - draggableButton.setTextColor(ResourcesUtils.getColor(R.color.white)) + draggableButton.setTextColor(context.getColor(R.color.white)) ObjectAnimator.ofFloat( draggableButton, "translationX", draggableButton.translationX, 0f diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/autopilot/AutopilotState.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/autopilot/AutopilotState.kt index 762db267ba..1ae3d57c97 100644 --- a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/autopilot/AutopilotState.kt +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/autopilot/AutopilotState.kt @@ -50,11 +50,11 @@ class AutopilotState @JvmOverloads constructor( autopilotLoadingAnimator = ObjectAnimator.ofFloat(aciv_autopilot_state, "rotation", 0f, 360f); autopilotLoadingAnimator.interpolator = LinearInterpolator() autopilotLoadingAnimator.repeatCount = -1 //无限循环 - autopilotLoadingAnimator.duration = 1000 //无限循环 + autopilotLoadingAnimator.duration = 2000 //无限循环 autopilotChangeStateAnimator = ObjectAnimator.ofFloat(aciv_autopilot_state, "alpha", 1f, 0f,1f); autopilotChangeStateAnimator.interpolator = LinearInterpolator() - autopilotChangeStateAnimator.duration = 200 + autopilotChangeStateAnimator.duration = 300 onClick { startAutopilot() } @@ -127,7 +127,7 @@ class AutopilotState @JvmOverloads constructor( override fun inAutopilot() { CallerLogger.d(TAG,"展示自驾中UI") - updateImageInAnimator(R.drawable.common_autopilot_starting_new) + updateImageInAnimator(R.drawable.common_autopilot_running) actv_pxjs_state.visibility = GONE actv_autopilot_head.visibility = VISIBLE @@ -225,7 +225,7 @@ class AutopilotState @JvmOverloads constructor( actv_pxjs_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB)) AutopilotState@this.isEnabled = false - aciv_autopilot_running_ani.visibility = VISIBLE + aciv_autopilot_running_ani.visibility = GONE autopilotStateAnimator?.stop() autopilotLoadingAnimator.cancel() autopilotChangeStateAnimator.start() @@ -234,7 +234,7 @@ class AutopilotState @JvmOverloads constructor( private fun updateImageInAnimator(@DrawableRes resource:Int){ UiThreadHandler.postDelayed({ aciv_autopilot_state.setImageResource(resource) - },100,UiThreadHandler.MODE.QUEUE) + },150,UiThreadHandler.MODE.QUEUE) } } \ No newline at end of file diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/autopilot/AutopilotStateModel.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/autopilot/AutopilotStateModel.kt index 79b15bf807..f23f021a51 100644 --- a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/autopilot/AutopilotStateModel.kt +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/autopilot/AutopilotStateModel.kt @@ -12,6 +12,7 @@ import com.mogo.och.common.module.manager.autopilot.line.ILineCallback import com.mogo.och.common.module.manager.autopilot.line.LineManager import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager import com.mogo.och.common.module.manager.loop.BizLoopManager +import com.mogo.och.common.module.utils.RxUtils import java.util.concurrent.atomic.AtomicBoolean /** @@ -126,6 +127,9 @@ class AutopilotStateModel : ViewModel(), IOchAutopilotStatusListener, ILineCall OchChainLogManager.writeChainLog("自驾信息","启动自驾") if(AutopilotStateDebug.debugStatus){ startAutopilotSuccess() + RxUtils.createSubscribe(5_000) { + startAutopilotFail() + } }else { LineManager.startAutopilot() } @@ -159,7 +163,7 @@ class AutopilotStateModel : ViewModel(), IOchAutopilotStatusListener, ILineCall this.isPalyStartAni.set(false) UiThreadHandler.postDelayed({ autopilotStateChange() - },1000,UiThreadHandler.MODE.QUEUE) + },3000,UiThreadHandler.MODE.QUEUE) } } diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/commonview/EmptyView.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/commonview/EmptyView.kt new file mode 100644 index 0000000000..66252b9b03 --- /dev/null +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/commonview/EmptyView.kt @@ -0,0 +1,55 @@ +package com.mogo.och.common.module.wigets.commonview + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import androidx.constraintlayout.widget.ConstraintLayout +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger +import com.mogo.map.listener.IMogoMapListener +import com.mogo.och.common.module.R +import com.mogo.och.common.module.utils.ResourcesUtils +import kotlinx.android.synthetic.main.common_empty_view.view.no_order_data_tv + +class EmptyView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr),IMogoMapListener { + companion object { + const val TAG = "LoadingMapStatusView" + } + + private var emptyTitle:String = "" + + + init { + LayoutInflater.from(context).inflate(R.layout.common_empty_view, this, true) + try { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.EmptyView) + emptyTitle = typedArray.getString(R.styleable.EmptyView_empty_title)?:ResourcesUtils.getString(R.string.common_empty_data) + typedArray.recycle() + } catch (e: Exception) { + e.printStackTrace() + } + no_order_data_tv.text = emptyTitle + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + CallerLogger.d(TAG,"onAttachedToWindow") + + } + + override fun onVisibilityAggregated(isVisible: Boolean) { + super.onVisibilityAggregated(isVisible) + + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + CallerLogger.d(TAG,"onDetachedFromWindow") + } + + + +} \ No newline at end of file diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/map/orderstatus/OrderStatusView.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/map/orderstatus/OrderStatusView.kt index a7e1eb3083..9b2258896e 100644 --- a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/map/orderstatus/OrderStatusView.kt +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/map/orderstatus/OrderStatusView.kt @@ -54,16 +54,18 @@ class OrderStatusView : RelativeLayout, OrderStatusViewModel.IVisualCallback { override fun setImageViewResource(name: Int,ordering:Boolean) { iv_order_status.setImageResource(name) - if (iv_order_status.layoutParams is RelativeLayout.LayoutParams) { - val temp = iv_order_status.layoutParams as RelativeLayout.LayoutParams - temp.removeRule(RelativeLayout.ALIGN_PARENT_START) - temp.addRule(ALIGN_PARENT_END) - iv_order_status.layoutParams = temp - }else{ - val temp = iv_order_status.layoutParams as RelativeLayout.LayoutParams - temp.removeRule(RelativeLayout.ALIGN_PARENT_END) - temp.addRule(ALIGN_PARENT_START) - iv_order_status.layoutParams = temp + if (iv_order_status.layoutParams is LayoutParams) { + if(ordering){ + val temp = iv_order_status.layoutParams as LayoutParams + temp.removeRule(ALIGN_PARENT_START) + temp.addRule(ALIGN_PARENT_END) + iv_order_status.layoutParams = temp + }else{ + val temp = iv_order_status.layoutParams as LayoutParams + temp.removeRule(ALIGN_PARENT_END) + temp.addRule(ALIGN_PARENT_START) + iv_order_status.layoutParams = temp + } } } diff --git a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/map/orderstatus/OrderStatusViewModel.kt b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/map/orderstatus/OrderStatusViewModel.kt index 82f4b26dfe..ae2f82b5fd 100644 --- a/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/map/orderstatus/OrderStatusViewModel.kt +++ b/OCH/common/common/src/main/java/com/mogo/och/common/module/wigets/map/orderstatus/OrderStatusViewModel.kt @@ -2,6 +2,7 @@ package com.mogo.och.common.module.wigets.map.orderstatus import androidx.annotation.DrawableRes import androidx.lifecycle.ViewModel +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger import com.mogo.eagle.core.utilcode.util.UiThreadHandler import com.mogo.och.common.module.R import com.mogo.och.common.module.biz.login.ILoginCallback @@ -30,6 +31,7 @@ class OrderStatusViewModel : ViewModel(), ILoginCallback { } override fun onOpenOrderStatusEnumChange(businessEnum: OpenOrderStatusEnum?) { + CallerLogger.d(TAG,"新的接单状态${businessEnum}") UiThreadHandler.post({ if (LoginStatusManager.isOpenOrderType()) { this.viewCallback?.setImageViewResource(R.drawable.common_order_status,true) diff --git a/OCH/common/common/src/main/res/values/attrs.xml b/OCH/common/common/src/main/res/values/attrs.xml index 31323f4270..ab122895b6 100644 --- a/OCH/common/common/src/main/res/values/attrs.xml +++ b/OCH/common/common/src/main/res/values/attrs.xml @@ -14,6 +14,10 @@ + + + + diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/base/TaxiFragment.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/base/TaxiFragment.kt index 980ff8fb45..dbca0cc86f 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/base/TaxiFragment.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/base/TaxiFragment.kt @@ -40,8 +40,8 @@ import com.mogo.och.unmanned.taxi.ui.task.TaxiTaskTabFragment import com.mogo.och.unmanned.taxi.utils.TPRouteDataTestUtils import kotlinx.android.synthetic.main.unmanned_taxi_base_fragment.unmannedMapCL import kotlinx.android.synthetic.main.unmanned_taxi_base_fragment.module_mogo_och_navi_panel_container +import kotlinx.android.synthetic.main.unmanned_taxi_base_fragment.orderDebugView import kotlinx.android.synthetic.main.unmanned_taxi_base_fragment.taxi_close_navi_icon -import kotlinx.android.synthetic.main.unmanned_taxi_panel.orderDebugView import java.lang.ref.WeakReference /** @@ -64,8 +64,6 @@ class TaxiFragment :MvpFragment(), private val lineView = "LINEVIEW" private var personalDialogFragment: WeakReference? = null - private var taskTabFragment: WeakReference? = null - private fun updateOperationBtnStatusOnModeChange(isRoutingVerifyMode: Boolean) { if (MogoStatusManager.getInstance().isTaxiUnmanedDriverTakingOrders) { @@ -175,11 +173,6 @@ class TaxiFragment :MvpFragment(), unmannedMapCL.onPause() } - fun onChangeOperationStatus() { - if (null == taskTabFragment || taskTabFragment!!.get() == null) return - taskTabFragment!!.get()!!.onCarTakeOrderStatusChanged() - } - override fun onLowMemory() { super.onLowMemory() unmannedMapCL.onLowMemory() @@ -273,15 +266,6 @@ class TaxiFragment :MvpFragment(), } - /** - * 获取站点面板view,在[.initViews]时候添加到container中 - * - * @return 站点面板view - */ - fun getStationPanelViewId(): Int { - return R.layout.unmanned_taxi_panel - } - /** * 重新开启自动驾驶 */ @@ -419,8 +403,9 @@ class TaxiFragment :MvpFragment(), if (MogoStatusManager.getInstance().isTaxiUnmanedDriverLineRoutingVerifyMode) { showAmapNaviToStationFragment(isShow) } else { - if (null == taskTabFragment || taskTabFragment!!.get() == null) return - taskTabFragment!!.get()!!.onNaviToEndStationByAMap(isShow) + // TODO: 需要复原 +// if (null == taskTabFragment || taskTabFragment!!.get() == null) return +// taskTabFragment!!.get()!!.onNaviToEndStationByAMap(isShow) } } else if (isShow) { //使用routing数据 showRoutingToStationFragment(true) diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/debug/DebugView.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/debug/DebugView.kt index 6cd50de673..66579ebe65 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/debug/DebugView.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/debug/DebugView.kt @@ -22,6 +22,7 @@ import com.mogo.commons.module.status.StatusDescriptor import com.mogo.commons.utils.MogoAnalyticUtils import com.mogo.eagle.core.function.main.MainMoGoApplication import com.mogo.eagle.core.network.utils.GsonUtil +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d import com.mogo.eagle.core.utilcode.util.AppUtils import com.mogo.eagle.core.utilcode.util.UiThreadHandler @@ -74,16 +75,19 @@ public class DebugView @JvmOverloads constructor( private var logHistoryTextView: TextView? = null fun printInfoMsg(msg: String) { + CallerLogger.i(TAG,msg) printMsg("Info $msg", MainMoGoApplication.getApp().getColor(R.color.background_info)) trackEvent("Info", msg) } fun printWarnMsg(msg: String) { + CallerLogger.w(TAG,msg) printMsg("Warn $msg", MainMoGoApplication.getApp().getColor(R.color.background_debug)) trackEvent("Warn", msg) } fun printErrorMsg(msg: String) { + CallerLogger.e(TAG,msg) printMsg("Error $msg", MainMoGoApplication.getApp().getColor(R.color.background_error)) trackEvent("Error", msg) } @@ -155,7 +159,7 @@ public class DebugView @JvmOverloads constructor( initBroadcastReceiver() LayoutInflater.from(context).inflate(R.layout.unmanned_taxi_debug_order, this, true) debugLogHistoryTextView.movementMethod = ScrollingMovementMethod.getInstance() - visibility = GONE + visibility = VISIBLE logHistoryTextView = debugLogHistoryTextView initView() diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/operational/TaxiOperationalDialogFragment.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/operational/TaxiOperationalDialogFragment.kt index f7b40c9862..092926449b 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/operational/TaxiOperationalDialogFragment.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/operational/TaxiOperationalDialogFragment.kt @@ -26,7 +26,6 @@ import com.mogo.och.unmanned.taxi.bean.QueryServingDurationRespBean import com.mogo.och.unmanned.taxi.bean.QueryTaskRespBean import com.mogo.och.unmanned.taxi.constant.StationTypeEnum import com.mogo.och.unmanned.taxi.constant.TaskTypeEnum -import com.mogo.och.unmanned.taxi.ui.task.TaxiCurrentTaskFragment import kotlinx.android.synthetic.main.unmanned_taxi_operational_data_view.dayCompletedOrdersView import kotlinx.android.synthetic.main.unmanned_taxi_operational_data_view.dayTotalOrdersView import kotlinx.android.synthetic.main.unmanned_taxi_operational_data_view.itemDayTv @@ -115,7 +114,7 @@ class TaxiOperationalDialogFragment : DialogFragment(), lifecycleScope.launchWhenStarted { mViewModel.uiStateFlow.map { it.operationalDataUIState }.collect { operationalDataUIState -> - d(TaxiCurrentTaskFragment.TAG, "uiStateFlow-initViewModelObserver: $operationalDataUIState") + d(TAG, "uiStateFlow-initViewModelObserver: $operationalDataUIState") when (operationalDataUIState) { is OperationalDataStateUIState.Init -> { } diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/TaxiTaskModel.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/TaxiTaskModel.kt index 45372c123c..83f27f04c6 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/TaxiTaskModel.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/TaxiTaskModel.kt @@ -276,14 +276,14 @@ object TaxiTaskModel { private val mMogoAutopilotStatusListener: IOchAutopilotStatusListener = object : IOchAutopilotStatusListener { override fun onAutopilotIpcConnectStatusChanged(status: AdasConstants.IpcConnectionStatus, reason: String?) { - DebugView.printInfoMsg( - "[域控连接状态变化] status=$status, reason=${ - if (TextUtils.isEmpty( - reason - ) - ) "" else reason - }" - ) +// DebugView.printInfoMsg( +// "[域控连接状态变化] status=$status, reason=${ +// if (TextUtils.isEmpty( +// reason +// ) +// ) "" else reason +// }" +// ) } override fun onAutopilotGuardian(guardianInfo: MogoReportMessage?, lineId: Long) { diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarycurrent/ItineraryCurrentModel.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarycurrent/ItineraryCurrentModel.kt index c79a16007a..b843c773be 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarycurrent/ItineraryCurrentModel.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarycurrent/ItineraryCurrentModel.kt @@ -1,27 +1,505 @@ package com.mogo.och.unmanned.taxi.ui.itinerarycurrent import androidx.lifecycle.ViewModel +import com.mogo.commons.AbsMogoApplication +import com.mogo.commons.module.status.MogoStatusManager +import com.mogo.eagle.core.data.BaseData +import com.mogo.eagle.core.data.config.FunctionBuildConfig +import com.mogo.eagle.core.data.map.MogoLocation +import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager +import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager +import com.mogo.eagle.core.function.call.unmanned.CallerUnmannedListenerManager +import com.mogo.eagle.core.network.utils.GsonUtil +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d +import com.mogo.eagle.core.utilcode.util.NetworkUtils +import com.mogo.eagle.core.utilcode.util.ToastUtils +import com.mogo.eagle.core.utilcode.util.UiThreadHandler +import com.mogo.och.common.module.biz.order.TaxiOrderStatusEnum +import com.mogo.och.common.module.manager.autopilot.line.LineManager +import com.mogo.och.common.module.manager.autopilot.location.OchLocationManager +import com.mogo.och.common.module.manager.distance.TrajectoryAndDistanceManager +import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager +import com.mogo.och.common.module.network.OchCommonServiceCallback +import com.mogo.och.common.module.utils.ToastUtilsOch +import com.mogo.och.common.module.voice.VoiceNotice +import com.mogo.och.unmanned.taxi.R +import com.mogo.och.unmanned.taxi.base.BaseViewModel +import com.mogo.och.unmanned.taxi.base.IUiIntent +import com.mogo.och.unmanned.taxi.bean.QueryCurrentTaskRespBean import com.mogo.och.unmanned.taxi.bean.StartGrayAndQueryContrailRsp +import com.mogo.och.unmanned.taxi.bean.StartServiceRespBean +import com.mogo.och.unmanned.taxi.bean.TrajectoryListRespBean +import com.mogo.och.unmanned.taxi.callback.ITaxiCarServiceCallback +import com.mogo.och.unmanned.taxi.callback.ITaxiTaskWithOrderCallback +import com.mogo.och.unmanned.taxi.constant.TaskStatusEnum +import com.mogo.och.unmanned.taxi.constant.TaskTypeEnum +import com.mogo.och.unmanned.taxi.constant.TaxiUnmannedConst +import com.mogo.och.unmanned.taxi.network.TaxiTaskWithOrderServiceManager +import com.mogo.och.unmanned.taxi.ui.debug.DebugView +import com.mogo.och.unmanned.taxi.ui.task.TaskUiIntent +import com.mogo.och.unmanned.taxi.ui.task.TaskWithOrderUIState +import com.mogo.och.unmanned.taxi.ui.task.TaxiTaskModel +import com.mogo.och.unmanned.taxi.ui.task.UnmannedState +import com.mogo.och.unmanned.taxi.utils.TaxiTrajectoryManager /** * @author XuXinChao * @description BadCase录包管理页面 * @since: 2022/12/15 */ -class ItineraryCurrentModel : ViewModel() { +class ItineraryCurrentModel : BaseViewModel(), + ITaxiTaskWithOrderCallback, ITaxiCarServiceCallback { private val TAG = ItineraryCurrentModel::class.java.simpleName + init { + TaxiTaskModel.addTaskWithOrderListener(TAG, this) + TaxiTaskModel.setCarServiceCallback(this) + } + private var viewCallback: SwtichLineViewCallback? = null + fun setDistanceCallback(viewCallback: SwtichLineViewCallback) { + this.viewCallback = viewCallback + } + + override fun onCleared() { - + TaxiTaskModel.removeTaskWithOrderListener(TAG) + TaxiTaskModel.removeCarServiceCallback() + super.onCleared() } - fun setDistanceCallback(viewCallback: SwtichLineViewCallback) { - this.viewCallback = viewCallback + override fun initUiState(): UnmannedState { + return UnmannedState(TaskWithOrderUIState.Init) + } + + override fun handleIntent(intent: IUiIntent) { + when (intent) { + is TaskUiIntent.StartTaskWithOrderLooper -> { //开始轮询 + startOrStopCurrentTaskWithOrderLoop(true) + } + + is TaskUiIntent.StartOrEndTakeOrder -> {//开始、暂停接单 + TaxiTaskModel.updateCarServingStatus() + } + + is TaskUiIntent.JumpPassengerCheck -> { //手动点击跳过乘客验证 + jumpPassengerCheck() + } + + is TaskUiIntent.JourneyCompleted -> { //点击服务完成 + journeyCompleted() + } + + is TaskUiIntent.CloseOrderByDriver -> { + closeOrderByDriver() + } + + is TaskUiIntent.CancelOrder -> {// 取消订单, 暂未加取消类型和原因 + cancelOrder(intent.type) + } + + is TaskUiIntent.StartTask -> { + startTask() + } + } + } + + fun startOrStopCurrentTaskWithOrderLoop(start: Boolean) { + d(TAG, "startOrStopCurrentTaskWithOrderLoop(): isStart=$start") + if (start) { + DebugView.printInfoMsg("[查询TaskWithOrder信息] start loop") + TaxiTaskModel.startQueryCurrentTaskWithOrderLoop() + } else { + DebugView.printInfoMsg("[查询TaskWithOrder信息] stop loop") + TaxiTaskModel.stopQueryCurrentTaskWithOrderLoop() + } + } + + private fun cancelOrder(cancelType: Int) { + val currentTaskWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + currentTaskWithOrder?.order?.also { + TaxiTaskModel.cancelOrder(it.orderNo, cancelType) + } + } + + private fun jumpPassengerCheck() { + DebugView.printInfoMsg("[跳过乘客验证] 准备发送请求") + val currentTaskWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + currentTaskWithOrder?.order?.also { + TaxiTaskWithOrderServiceManager.jumpPassengerCheck( + AbsMogoApplication.getApp().applicationContext, + it.orderNo, + object : OchCommonServiceCallback { + override fun onSuccess(data: BaseData?) { + DebugView.printInfoMsg("[跳过乘客验证] 请求success") + d(TAG, "jumpPassengerCheck onSuccess:") + } + + override fun onFail(code: Int, msg: String?) { + DebugView.printInfoMsg("[跳过乘客验证] 请求fail, code=$code, msg=$msg") + d(TAG, "jumpPassengerCheck onFail: code=$code, msg=$msg") + } + }) + } + } + + private fun journeyCompleted() { + DebugView.printInfoMsg("[服务完成] 准备发送请求") + val currentTaskWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + currentTaskWithOrder?.order?.also { + TaxiTaskWithOrderServiceManager.orderCompleted( + AbsMogoApplication.getApp().applicationContext, + it.orderNo, + object : OchCommonServiceCallback { + override fun onSuccess(data: BaseData?) { + DebugView.printInfoMsg("[服务完成] 请求success") + d(TAG, "journeyCompleted onSuccess") + } + + override fun onFail(code: Int, msg: String?) { + DebugView.printInfoMsg("[服务完成] 请求fail, code=$code, msg=$msg") + d(TAG, "journeyCompleted onFail: code=$code, msg=$msg") + } + }) + } + } + + private fun closeOrderByDriver() { + val currentTaskWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + currentTaskWithOrder?.order?.orderEndSite?.also { + TaxiTaskModel.submitArriveSite(it.siteId, true) + } + } + + private fun startTask(isStartAutopilot: Boolean = true) { + if (!TaxiTaskModel.checkCurrentTaskCondition()) { + ToastUtils.showShort("无任务!") + return + } + + TaxiTaskModel.getCurrentTaskWithOrder()?.let { + TaxiTaskModel.startTask( + if (it.order != null && it.order!!.orderStatus >= TaxiOrderStatusEnum.ArriveAtStart.code) + it.order!!.orderLine //当前若是启动的送驾任务 , 则使用订单信息的lineId + else + it.lineId + ,isStartAutopilot) + } + } + + private fun updateTaskAndOrderUi(currentTaskWithOrder: QueryCurrentTaskRespBean.Result?) { + d( + TAG, "updateTaskAndOrderUi: currentTaskWithOrder=${ + GsonUtil.getGson().toJson(currentTaskWithOrder) + }" + ) + sendUiState { + copy( + taskWithOrderUIState = TaskWithOrderUIState.TaskWithOrder( + currentTaskWithOrder + ) + ) + } + } + + private fun updatePrepareTaskDelayUI(delayTime: Long, isStart: Boolean) { + d(TAG, "UpdatePrepareTaskDelay120SUI = $isStart") + sendUiState { + copy( + taskWithOrderUIState = TaskWithOrderUIState.UpdatePrepareTaskDelay(delayTime, + isStart + ) + ) + } + } + + private fun updateDriveToNearestStationTaskUI(driveToNearestStationTask: StartServiceRespBean.Result?) { + d(TAG, "updateDriveToNearestStationTaskUI = ${driveToNearestStationTask?.toString()}") + sendUiState { + copy( + taskWithOrderUIState = TaskWithOrderUIState.TaskDriveToNearestStationTask( + driveToNearestStationTask + ) + ) + } + } + + private fun updateOrderTripInfoUI(mileage: Float, duration: Int) { + d(TAG, "updateOrderTripInfoUI") + OchChainLogManager.writeChainLog("到达目的地计算距离和时间", "距离:${mileage} 时间:${duration}") + sendUiState { + copy( + taskWithOrderUIState = TaskWithOrderUIState.UpdateOrderTripInfo( + mileage, duration + ) + ) + } + } + + private fun updateTaskTripInfoLocalCalculateUI(meters: Long, timeInSecond: Long) { + d(TAG, "UpdateTaskTripInfoLocalCalculateUI") + sendUiState { + copy( + taskWithOrderUIState = TaskWithOrderUIState.UpdateTaskTripLocalCalculateInfo( + meters, timeInSecond + ) + ) + } + } + + override fun onTaskWithOrderQuerySuccess(timeMillis: Long) { + } + + override fun onTaskWithOrderDataChanged(result: QueryCurrentTaskRespBean.Result?) { + d(TAG, "onTaskWithOrderChanged = result = " + GsonUtil.jsonFromObject(result)) + DebugView.printInfoMsg("[查询TaskWithOrder信息] 更新数据, 刷新UI") + updateTaskAndOrderUi(result) + // 设置task执行相关状态,切换模式时判断使用 + if (result == null || result.taskType == TaskTypeEnum.None.code) { + MogoStatusManager.getInstance().setTaxiUnmanedDriverPerformTask(TAG, false) + LineManager.setLineInfo(null); + } else { + MogoStatusManager.getInstance().setTaxiUnmanedDriverPerformTask(TAG, true) + } + } + + override fun onTaskStarted(result: QueryCurrentTaskRespBean.Result?) { + updateLocalCalculateStation(result) + if (result?.endSite != null){ + TaxiTaskModel.setBeautificationMode(true) + updateAutopilotControlParameters() + }else{ + clearDemoModeAndACParameters() + } + } + + private fun updateLocalCalculateStation(result: QueryCurrentTaskRespBean.Result?) { + if (result?.startSite != null && result.endSite != null + ) { + d(TAG, "updateLocalCalculateStation start") + val curTaskAndOrder = TaxiTaskModel.getCurrentTaskWithOrder() ?: return + if (curTaskAndOrder.startSite != null && curTaskAndOrder.endSite != null) { + val startStation = MogoLocation() + startStation.longitude = curTaskAndOrder.startSite!!.gcjLon + startStation.latitude = curTaskAndOrder.startSite!!.gcjLat + val endStation = MogoLocation() + endStation.longitude = curTaskAndOrder.endSite!!.gcjLon + endStation.latitude = curTaskAndOrder.endSite!!.gcjLat + TrajectoryAndDistanceManager.setStationPoint( + startStation, + endStation, + curTaskAndOrder.lineId + ) + } + } else { + TaxiTaskModel.clearLocalCalculateStation() + } + } + + /** + * 开始倒计时120s或者10s拉取任务 + * 演练任务120s拉取 + * 接驾任务10s拉取 + */ + override fun onTaskCompleted(result: QueryCurrentTaskRespBean.Result?) { + d(TAG, "onTaskCompleted: ${result?.currentStatus}, siteId=${result?.endSite?.siteId}") + + if (result?.order != null && result.servingStatus == 1){ + if (result.taskType <= TaskTypeEnum.VirtualTask.code + && result.order!!.orderStatus < TaxiOrderStatusEnum.ArriveAtStart.code) { + VoiceNotice.showNotice("已为您接到订单") + } + if (result.order!!.orderStatus == TaxiOrderStatusEnum.ArriveAtStart.code){ + VoiceNotice.showNotice("已到达上车地点,等待乘客上车") + } + } + + /** + * 1、有订单下(表示当前有订单或者未来有要执行的订单): + * 接驾任务需要立即拉取, 拉取时机在车辆前往上车点状态 + * 送驾任务需要立即拉取, 拉取时机在乘客已上车状态 + * 2、在没有订单情况下 + * 若当前到站的是前往标定站点, 则45s去拉取任务 + * 若当前是演练任务到站, 45s去拉取任务 + * 若当前是接驾任务到站, 此情况肯定是有订单的, 则走1 + * 若当前到站的是送驾任务, 不去拉取, 需等待司机点击服务完成按钮后拉取 + */ + + if (result?.order != null){ //接到订单情况下 + if (QueryCurrentTaskRespBean.isOrderOnTheWayToStart(result)){ //接驾任务拉取 + TaxiTaskModel.startPrepareTaskDelay( + TaxiUnmannedConst.START_PREPARE_TO_START_TASK_INTERVAL, + result?.endSite!!.siteId) + }else if (QueryCurrentTaskRespBean.isOrderUserArriveAtStart(result)) { //送驾任务拉取 + TaxiTaskModel.startPrepareTaskDelay(0, result?.endSite!!.siteId) + }else{ // 在已经接到订单的其他情况下, 取消倒计时任务拉取 + TaxiTaskModel.removePrepareTaskDelay() + } + }else{ //演练任务拉取 6.2.0使用配置的时间拉取, 默认45s + TaxiTaskModel.startPrepareTaskDelay( + CallerUnmannedListenerManager.getVirtualTaskPullTaskIntervalF() * 1000L, result?.endSite!!.siteId) + } + + clearDemoModeAndACParameters() + } + + override fun onTaskTrajectoryDataChanged(data: TrajectoryListRespBean?) { + TaxiTrajectoryManager.getInstance().syncTrajectoryInfo() //同步轨迹信息 + } + + override fun onOrderCancel() { + VoiceNotice.showNotice("已取消行程") + //取消自驾,D档位会溜车 map3.6.0 修改 + TaxiTaskModel.cancelAutopilot() + // 设置task执行相关状态,切换模式时判断使用 + MogoStatusManager.getInstance().setTaxiUnmanedDriverPerformTask(TAG, false) + LineManager.setLineInfo(null); + } + + override fun onOrderArriveAtEnd(orderNo: String) { + TaxiTaskModel.queryOrderByOrderNo(orderNo) + } + + /** + * 更新总全程信息(公里和分钟), 后端返回的数据 + */ + override fun onOrderTripInfoChanged(mileage: Float, duration: Int) { + updateOrderTripInfoUI(mileage, duration) + } + + override fun onOrderJourneyCompleted() { + updateTaskAndOrderUi(null) + //获取新的任务 + val currentTaskWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + currentTaskWithOrder?.endSite?.also {//使用配置的时间拉取, 默认是45s + TaxiTaskModel.startPrepareTaskDelay( + CallerUnmannedListenerManager.getVirtualTaskPullTaskIntervalF() * 1000L, + it.siteId) + } + // 设置task执行相关状态,切换模式时判断使用 + MogoStatusManager.getInstance().setTaxiUnmanedDriverPerformTask(TAG, false) + LineManager.setLineInfo(null); + } + + override fun onStartAutopilot(postDelayTime: Long) { + UiThreadHandler.postDelayed(startTaskRunnable, postDelayTime) // 5s后或者倒计时结束开启自驾, 状态流转 + } + + private val startTaskRunnable: Runnable = Runnable { + startTask() //状态流转 + VoiceNotice.showNotice("车辆正在自动开启自动驾驶") + } + + /** + * 主动请求平行驾驶, 停止启动自驾命令, 任务状态需向下流转 + */ + override fun onStopAutopilot() { + UiThreadHandler.removeCallbacks(startTaskRunnable) + if (!TaxiTaskModel.checkCurrentTaskCondition()) { + d(TAG, "onStopAutopilot: 无任务无需流转状态") + return + } + startTask(false) + } + + override fun onPauseStartAutopilot() { + UiThreadHandler.removeCallbacks(startTaskRunnable) + } + + /** + * 开始倒计时120s或者10s拉取任务 + * 演练任务120s拉取 + * 接驾任务10s拉取 + */ + override fun onStartPrepareTaskUI(delayTime: Long, isStart: Boolean) { + updatePrepareTaskDelayUI(delayTime, isStart) + } + + /** + * 更新本次任务行程信息, 本地计算的数据 + */ + override fun onTaskTripInfoLocalCalculateChanged(meters: Long, timeInSecond: Long) { + updateTaskTripInfoLocalCalculateUI(meters, timeInSecond) + } + + override fun onCarEndServiceSuccess( + driveToNearestStationTask: StartServiceRespBean.Result?, + currentTaskWithOrder: QueryCurrentTaskRespBean.Result? + ) { + if (currentTaskWithOrder == null) return + if (currentTaskWithOrder.currentStatus < TaskStatusEnum.CompleteTask.code //任务未完成 + && currentTaskWithOrder.currentStatus > TaskStatusEnum.None.code // 暂停接单后查询到任务状态是0, 代表没任务 + || currentTaskWithOrder.order != null) {// 有订单未完成 + VoiceNotice.showNotice("暂停接单啦!要完成当前订单哦") + } else { + VoiceNotice.showNotice("暂停接单啦") + updateDriveToNearestStationTaskUI(driveToNearestStationTask) + } + } + + override fun onCarEndServiceFailed(code: Int, msg: String) { + ToastUtilsOch.showWithCodeMessage(code, msg) + } + + override fun onCarEndServiceError() { + val context = AbsMogoApplication.getApp().applicationContext + if (!NetworkUtils.isConnected(context)) { + ToastUtils.showShort(context.getString(R.string.network_error_tip)) + } else { + ToastUtils.showShort(context.getString(R.string.request_error_tip)) + } + } + + override fun onCarStartServiceSuccess( + driveToNearestStationTask: StartServiceRespBean.Result?, + currentTaskWithOrder: QueryCurrentTaskRespBean.Result? + ) { + VoiceNotice.showNotice("开始接单啦") + updateDriveToNearestStationTaskUI(driveToNearestStationTask) + } + + override fun onCarStartServiceFailed(code: Int, msg: String) { + val gcJ02Location = OchLocationManager.getGCJ02Location() + ToastUtilsOch.showWithCodeMessage( + code, + "$msg curLatitude = ${gcJ02Location.latitude}" + " curLongitude = ${gcJ02Location.longitude}" + ) + } + + override fun onCarStartServiceError() { + val context = AbsMogoApplication.getApp().applicationContext + if (!NetworkUtils.isConnected(context)) { + ToastUtils.showShort(context.getString(R.string.network_error_tip)) + } else { + ToastUtils.showShort(context.getString(R.string.request_error_tip)) + } + } + + private fun clearDemoModeAndACParameters(){ + if (FunctionBuildConfig.isDemoMode) { + d(TAG, "setIPCDemoMode:false") + CallerAutoPilotControlManager.setIPCDemoMode(false) + } + TaxiTaskModel.clearAutopilotControlParameters() + } + + /** + * 将业务订单信息保存,鹰眼可取用 + */ + private fun updateAutopilotControlParameters() { + val parameters = TaxiTaskModel.initAutopilotControlParameters() + if (null == parameters) { + CallerLogger.e(TAG, "AutopilotControlParameters is empty.") + return + } + d(TAG, "AutopilotControlParameters is update.") + DebugView.printInfoMsg("[启自驾] updateAutopilotControlParameters调用成功") + CallerAutoPilotStatusListenerManager.updateAutopilotControlParameters(parameters) } diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarycurrent/ItineraryCurrentView.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarycurrent/ItineraryCurrentView.kt index c43a3f0d3d..c0e72f1d27 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarycurrent/ItineraryCurrentView.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarycurrent/ItineraryCurrentView.kt @@ -1,17 +1,84 @@ package com.mogo.och.unmanned.taxi.ui.task.itinerarycurrent import android.content.Context +import android.graphics.Color +import android.os.CountDownTimer +import android.text.TextUtils import android.util.AttributeSet import android.view.LayoutInflater +import android.view.View import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope +import com.amap.api.navi.model.NaviLatLng +import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener +import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager.getState +import com.mogo.eagle.core.function.hmi.ui.widget.ItinerarySummaryDialog +import com.mogo.eagle.core.function.main.MainMoGoApplication +import com.mogo.eagle.core.network.utils.GsonUtil +import com.mogo.eagle.core.utilcode.kotlin.onClick +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.i +import com.mogo.eagle.core.utilcode.util.ClickUtils +import com.mogo.eagle.core.utilcode.util.ToastUtils +import com.mogo.eagle.core.utilcode.util.UiThreadHandler +import com.mogo.och.common.module.biz.order.TaxiOrderStatusEnum +import com.mogo.och.common.module.manager.autopilot.location.OchLocationManager +import com.mogo.och.common.module.map.AmapNaviToDestinationModel +import com.mogo.och.common.module.map.ICommonNaviChangedCallback +import com.mogo.och.common.module.map.MapMakerManager +import com.mogo.och.common.module.utils.DateTimeUtil +import com.mogo.och.common.module.utils.FlowBus +import com.mogo.och.common.module.utils.ResourcesUtils +import com.mogo.och.common.module.voice.VoiceNotice +import com.mogo.och.common.module.wigets.CommonSlideView +import com.mogo.och.common.module.wigets.OCHCommitDialog import com.mogo.och.unmanned.taxi.R import com.mogo.och.unmanned.taxi.TaxiUnmannedDriverProvider +import com.mogo.och.unmanned.taxi.bean.OrderDetail +import com.mogo.och.unmanned.taxi.bean.QueryCurrentTaskRespBean +import com.mogo.och.unmanned.taxi.bean.StartServiceRespBean +import com.mogo.och.unmanned.taxi.constant.TaskStatusEnum +import com.mogo.och.unmanned.taxi.constant.TaskTypeEnum +import com.mogo.och.unmanned.taxi.constant.TaxiDriverEventConst +import com.mogo.och.unmanned.taxi.constant.TaxiUnmannedConst.Companion.TAXI_END_MAP_MAKER +import com.mogo.och.unmanned.taxi.constant.TaxiUnmannedConst.Companion.TAXI_START_MAP_MAKER +import com.mogo.och.unmanned.taxi.constant.TaxiUnmannedConst.Companion.TYPE_MARKER_TAXI_ORDER +import com.mogo.och.unmanned.taxi.ui.debug.DebugView import com.mogo.och.unmanned.taxi.ui.itinerarycurrent.ItineraryCurrentModel +import com.mogo.och.unmanned.taxi.ui.task.TaskUiIntent +import com.mogo.och.unmanned.taxi.ui.task.TaskWithOrderUIState +import com.mogo.och.unmanned.taxi.ui.task.TaxiOrderCancelDialog +import com.mogo.och.unmanned.taxi.ui.task.TaxiTaskModel +import com.mogo.och.unmanned.taxi.utils.TaskUtils +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.aciv_task_type_exercise +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.aciv_task_type_order +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.actv_distance_end +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.actv_end_order +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.actv_submit_task +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.cancelOrder +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.commonSlideViewStartServer +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.gourp_order +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.group_itinerary_info +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.include_empty +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.naviToEnd +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.naviToStart +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.prepareTaskCountdownTv +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.taskStatus +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.actv_time_end +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.endStationName +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.startStationName +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.orderPhoneAndNum +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.pathwayPoint +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.trajectoryType +import kotlinx.android.synthetic.main.unmanned_itinerary_current.view.v_bg_route_point_station_name +import kotlinx.coroutines.flow.map -class ItineraryCurrentView: ConstraintLayout, ItineraryCurrentModel.SwtichLineViewCallback { +class ItineraryCurrentView: ConstraintLayout, ItineraryCurrentModel.SwtichLineViewCallback, + View.OnClickListener, ICommonNaviChangedCallback { constructor(context: Context) : super(context) @@ -27,6 +94,10 @@ class ItineraryCurrentView: ConstraintLayout, ItineraryCurrentModel.SwtichLineVi const val TAG = "SwitchBizView" } + private var mPrepareTasCountDownTimer: CountDownTimer? = null + + private var dialog: ItinerarySummaryDialog? = null + private var viewModel: ItineraryCurrentModel?=null private var fragment: LifecycleOwner?=null @@ -41,6 +112,272 @@ class ItineraryCurrentView: ConstraintLayout, ItineraryCurrentModel.SwtichLineVi fragment = TaxiUnmannedDriverProvider.getFragmentInfo() } + private fun initViewModelObserver() { + viewModel?.sendUiIntent(TaskUiIntent.StartTaskWithOrderLooper) + + //监听返回的数据状态 + fragment?.lifecycleScope?.launchWhenStarted { + viewModel?.uiStateFlow?.map { it.taskWithOrderUIState }?.collect { taskAndOrderUiState -> + d(TAG, "uiStateFlow-initViewModelObserver: $taskAndOrderUiState") + when (taskAndOrderUiState) { + is TaskWithOrderUIState.Init -> { + } + + // 开始接单后 需要将车开到最近的一个站点就位,然后才能开启 无人化 流程 + is TaskWithOrderUIState.TaskDriveToNearestStationTask -> { + if (taskAndOrderUiState.driveToNearestStationTask != null) { + updateViewByDriveToNearestStationTask(taskAndOrderUiState.driveToNearestStationTask) + } else { + updatePrepareTaskDelayUI(0, false) + initContainerView(false) + removeAllMapMarker() + } + } + + is TaskWithOrderUIState.TaskWithOrder -> { + val currentTaskWithOrder = taskAndOrderUiState.taskWithOrder + if (currentTaskWithOrder == null) { + initContainerView(false) + removeAllMapMarker() + } else { + updateViewByCurrentTaskWithOrder(currentTaskWithOrder) + } + updateNextTaskFragment(currentTaskWithOrder) + } + + is TaskWithOrderUIState.UpdateOrderTripInfo -> { + dialog?.setOrderMileage(taskAndOrderUiState.mileage*1000) + actv_distance_end.text = TaskUtils.getCurrentTaskDistance(taskAndOrderUiState.mileage.toLong()) + actv_time_end.text = TaskUtils.getCurrentTaskTime(taskAndOrderUiState.duration.toLong()) + } + + is TaskWithOrderUIState.UpdateTaskTripLocalCalculateInfo -> { + actv_distance_end.text = TaskUtils.getCurrentTaskDistance(taskAndOrderUiState.meters.toLong()) + actv_time_end.text = TaskUtils.getCurrentTaskTime(taskAndOrderUiState.timeInSecond.toLong()) + } + + is TaskWithOrderUIState.UpdatePrepareTaskDelay -> { + updatePrepareTaskDelayUI( + taskAndOrderUiState.delayTime, + taskAndOrderUiState.isStart + ) + } + } + } + } + } + + /** + * 更新拉取任务倒计时 + */ + private fun updatePrepareTaskDelayUI(millisInFuture: Long, isStart: Boolean) { + DebugView.printInfoMsg("距离任务获取还有 ${DateTimeUtil.second2MMSS(millisInFuture / 1000)}") + if (!isStart) { + prepareTaskCountdownTv.visibility = View.GONE + mPrepareTasCountDownTimer?.cancel() + mPrepareTasCountDownTimer = null + return + } + + if (mPrepareTasCountDownTimer != null) { + mPrepareTasCountDownTimer?.cancel() + mPrepareTasCountDownTimer = null + } + + mPrepareTasCountDownTimer = object : CountDownTimer(millisInFuture, 1000L) {// 倒计时后开启自驾 + + override fun onTick(millisUntilFinished: Long) { + DebugView.printInfoMsg("距离任务获取还有 ${DateTimeUtil.second2MMSS(millisUntilFinished / 1000)}") + // 倒计时 + UiThreadHandler.post { + prepareTaskCountdownTv.visibility = View.VISIBLE + prepareTaskCountdownTv.text = + "距离任务获取还有 ${DateTimeUtil.second2MMSS(millisUntilFinished / 1000)}" + } + } + + override fun onFinish() { + //倒计时结束了... + UiThreadHandler.post { + prepareTaskCountdownTv.visibility = View.INVISIBLE + } + mPrepareTasCountDownTimer?.cancel() + } + } + + mPrepareTasCountDownTimer?.start() + } + + /** + * 更新当前任务和订单信息 + */ + private fun updateViewByCurrentTaskWithOrder(taskAndOrder: QueryCurrentTaskRespBean.Result?) { + if (taskAndOrder == null) return + /** + * 根据任务类型判断任务显示, + * 虚拟单, 显示在进行中 前往上车点 + * 演练任务,显示演练任务, 同时有订单显示在待服务中 + * 当前任务是接驾任务,显示订单状态+运营单 订单显示在进行中, 任务不再显示 + */ + val taskType = taskAndOrder.taskType // 任务类型 + val order = taskAndOrder.order // 订单信息 + val endSite = taskAndOrder.endSite // 终点 + val currentStatus = taskAndOrder.currentStatus // 任务的状态 0:空闲 1:获取任务 2:开始任务 3:到达目的地 + + if ((endSite == null || currentStatus == TaskStatusEnum.CompleteTask.code) // 无任务或者任务已经完成的时候且无订单的时候 + && taskAndOrder.order == null + ) { + initContainerView(false) + removeAllMapMarker() + return + } + + initContainerView(true) + + when (taskType) { + TaskTypeEnum.None.code -> { + if (order != null && (currentStatus == TaskStatusEnum.CompleteTask.code + || currentStatus == TaskStatusEnum.None.code) + ) { //暂停接单会清空前往上车点任务 + updateOrderUI(order) + } + } + + TaskTypeEnum.VirtualTask.code -> { //演练任务 + + if (order != null && currentStatus == TaskStatusEnum.CompleteTask.code) { + updateOrderUI(order) + } else { + updateVirtualTaskUI(taskAndOrder) + } + } + + TaskTypeEnum.ToOrderEndTask.code, TaskTypeEnum.ToOrderStartTask.code -> {// 接驾任务 或 送驾任务 + order?.also { + updatePathwayPoint(taskType, endSite?.siteName) + updateOrderUI(it) + } + } + } + + updateMapMarkers(taskAndOrder) + updateRemainDistanceAndTime(false) + } + + // 第一个特殊任务 也是虚拟任务 + private fun updateViewByDriveToNearestStationTask(driveToNearestStationTask: StartServiceRespBean.Result?) { + if (driveToNearestStationTask == null) return + initContainerView(true) + // DriverToNearestStationTask 任务更新 + gourp_order.visibility = GONE + group_itinerary_info.visibility = VISIBLE + aciv_task_type_exercise.visibility = VISIBLE + // 更新任务状态,起点,终点 + taskStatus.text = resources.getString(R.string.task_start_to_virtual_site) + startStationName.text = resources.getString(R.string.task_current_loc) + endStationName.text = driveToNearestStationTask.siteName + + setOrRemoveMapMaker( + true, + TAXI_END_MAP_MAKER, + driveToNearestStationTask.wgs84Lat, + driveToNearestStationTask.wgs84Lon, + R.raw.end_marker + ) + + // 使用高德获取导航数据 + startNaviToStation( + false, + driveToNearestStationTask.gcjLat, + driveToNearestStationTask.gcjLon + ) + } + + private fun showEmptyView() { + gourp_order.visibility = GONE + aciv_task_type_exercise.visibility = GONE + group_itinerary_info.visibility = GONE + include_empty.visibility = VISIBLE + prepareTaskCountdownTv.visibility = GONE + } + + private fun initOnClickListener() { + naviToStart.setOnClickListener(this) + naviToEnd.setOnClickListener(this) + + cancelOrder.setOnClickListener(this) + taskStatus.setOnClickListener(this) + commonSlideViewStartServer.setSlideListener(object :CommonSlideView.SlideListener{ + override fun slideEnd() { + d(TAG, taskStatus.text.toString()) + startOrEndService() + } + }) + actv_submit_task.onClick { + startOrEndService() + } + actv_end_order.onClick { + startOrEndService() + } + } + + private fun initTaskDebugViewListener() { + fragment?.let { fr-> + aciv_task_type_exercise.setOnLongClickListener { + FlowBus.with(TaxiDriverEventConst.TaxiFragmentEvent.EVENT_TYPE_SHOW_DEBUG_VIEW) + .post(fr.lifecycleScope, true) + false + } + aciv_task_type_order.setOnLongClickListener { + FlowBus.with(TaxiDriverEventConst.TaxiFragmentEvent.EVENT_TYPE_SHOW_DEBUG_VIEW) + .post(fr.lifecycleScope, true) + false + } + } + } + + private fun startOrEndService() { + val currentTaskWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + if (currentTaskWithOrder?.order == null) return + val order = currentTaskWithOrder.order + if (TaxiOrderStatusEnum.ArriveAtStart.code == order!!.orderStatus) { //到达乘客上车点,司机可跳过乘客屏认证 + viewModel?.sendUiIntent(TaskUiIntent.JumpPassengerCheck) + } else if (TaxiOrderStatusEnum.UserArriveAtStart.code == order.orderStatus) { + viewModel?.sendUiIntent(TaskUiIntent.StartTask) + } else if (TaxiOrderStatusEnum.ArriveAtEnd.code == order.orderStatus) { //点击了完成服务,结束订单并更新订单信息 + viewModel?.sendUiIntent(TaskUiIntent.JourneyCompleted) + } else if (TaxiOrderStatusEnum.OnTheWayToEnd.code == order.orderStatus) { //前往目的地过程中可提前结束行程 + //自驾中提示,接管后才能结束 + if (getState() + == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING + ) { + ToastUtils.showLong(R.string.taxi_switch_line_btn_warning1) + } else { + closeOrderDialog() + } + } + } + + private fun closeOrderDialog() { + val builder = OCHCommitDialog.Builder() + val closeOrderDialog = builder + .title(ResourcesUtils.getString(R.string.dialog_order_close_title)) + .tips(ResourcesUtils.getString(R.string.dialog_order_close_content)) + .confirmStr(ResourcesUtils.getString(R.string.dialog_order_close_confirm)) + .cancelStr(ResourcesUtils.getString(R.string.dialog_order_close_cancel)) + .build(context) + closeOrderDialog!!.setClickListener(object : OCHCommitDialog.ClickListener { + override fun confirm() { + viewModel?.sendUiIntent(TaskUiIntent.CloseOrderByDriver) + } + + override fun cancel() { + closeOrderDialog.dismiss() + } + }) + closeOrderDialog.show() + } + override fun onAttachedToWindow() { super.onAttachedToWindow() @@ -48,6 +385,434 @@ class ItineraryCurrentView: ConstraintLayout, ItineraryCurrentModel.SwtichLineVi ViewModelProvider(it).get(ItineraryCurrentModel::class.java) } viewModel?.setDistanceCallback(this) + initOnClickListener() + showEmptyView() + initTaskDebugViewListener() + initViewModelObserver() + } + + override fun onClick(v: View?) { + if (!ClickUtils.isFastClick()) { + i(TAG, "view点击过快") + return + } + when (v?.id) { + + cancelOrder.id -> { + val currentWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + if (currentWithOrder?.order != null) { + val order = currentWithOrder.order + TaxiOrderCancelDialog(context, order!!.orderStatus) { type -> + viewModel?.sendUiIntent(TaskUiIntent.CancelOrder(type)) + }.show() + } + } + + naviToStart.id, + naviToEnd.id -> { + showNaviToEndStationFragment(true) + } + } + } + + /** + * 显示/隐藏 前往任务目的地的导航 + * + * @param isShow + */ + private fun showNaviToEndStationFragment(isShow: Boolean) { + fragment?.let { + FlowBus.with(TaxiDriverEventConst.TaxiFragmentEvent.EVENT_TYPE_START_NAVI_TO_END_STATION) + .post(it.lifecycleScope, isShow) + } + } + + /** + * 是否有正在进行的订单,进行UI显示 + * + * @param hasCurrentTask + */ + private fun initContainerView(hasCurrentTask: Boolean) { + d( + TAG, + "hasCurrentTask = $hasCurrentTask" + ) + if (hasCurrentTask) { + include_empty.visibility = View.GONE + prepareTaskCountdownTv.visibility = View.GONE + } else { + // 空页面 + include_empty.visibility = View.VISIBLE + // 倒计时 + prepareTaskCountdownTv.visibility = View.GONE + // 行程信息 + group_itinerary_info.visibility = View.GONE + // 订单信息 + gourp_order.visibility = View.GONE + // 途经点信息 + pathwayPoint.visibility = View.GONE + // 演练单标识 + aciv_task_type_exercise.visibility = View.GONE + // 轨迹标识 + trajectoryType.visibility = View.GONE + } + } + + private fun removeAllMapMarker() { + MapMakerManager.removeAllMapMarkerByOwner(TYPE_MARKER_TAXI_ORDER) + } + + private fun updateNextTaskFragment(result: QueryCurrentTaskRespBean.Result?) { + fragment?.let { + FlowBus.with(TaxiDriverEventConst.TabFragmentEvent.EVENT_TYPE_TASK_WITH_ORDER_CHANGED) + .post(it.lifecycleScope, result) + + if (result != null + && result.taskType == TaskTypeEnum.VirtualTask.code + && result.order != null + && result.currentStatus != TaskStatusEnum.CompleteTask.code + ) { + VoiceNotice.showNotice("已为您提前接到下一订单,待完成当前任务后服务") + FlowBus.with(TaxiDriverEventConst.TabFragmentEvent.EVENT_TYPE_SHOW_RED_POINT) + .post(it.lifecycleScope, true) + } else { + FlowBus.with(TaxiDriverEventConst.TabFragmentEvent.EVENT_TYPE_SHOW_RED_POINT) + .post(it.lifecycleScope, false) + } + } + + } + + private fun updateOrderUI(order: OrderDetail) { + cancelOrder.visibility = if (order.orderStatus == TaxiOrderStatusEnum.ArriveAtEnd.code + ) View.GONE else View.VISIBLE + gourp_order.visibility = View.VISIBLE + group_itinerary_info.visibility = View.VISIBLE + aciv_task_type_exercise.visibility = View.GONE +// orderStatus: 0 订单创建(为派单), 10 已派上司机(司机去往上车点), 20 司机到达上车点, +// 30 乘客到达上车点, 40 服务中(去往目的地), 50 到达目的地, 60 已完成, 70 已取消 + naviToStart.visibility = if (order.orderStatus + == TaxiOrderStatusEnum.OnTheWayToStart.code + ) View.VISIBLE else View.GONE + + naviToEnd.visibility = if (order.orderStatus + == TaxiOrderStatusEnum.OnTheWayToEnd.code + ) View.VISIBLE else View.GONE + + updatePrepareTaskDelayUI(0, false) + + orderPhoneAndNum.text = TaskUtils.getCurrentTaskPhoneNumAndPassengerCountHtml( + order.bookingUserPhone, + order.passengerSize + ) + startStationName.text = order.orderStartSite?.siteName + endStationName.text = order.orderEndSite?.siteName + TaxiTaskModel.getCurrentOrderTrajectoryList().also { + val orderTrajectory = it.firstOrNull {order.orderLine == it.lineId} + trajectoryType.visibility = if (orderTrajectory?.source == 2) + View.VISIBLE else View.GONE + } + + when (order.orderStatus) { + TaxiOrderStatusEnum.None.code -> { //无 + dismissDialog() + initContainerView(false) + removeAllMapMarker() + } + + TaxiOrderStatusEnum.ArriveAtEnd.code -> { //到达目的地 + taskStatus.text = resources.getString(R.string.task_start_end_site) + + actv_end_order.visibility = VISIBLE + actv_end_order.text = ResourcesUtils.getString(R.string.module_och_taxi_order_server_end) + commonSlideViewStartServer.visibility = GONE + actv_submit_task.visibility = GONE + + showDialog() + hideNaviBtns() + } + + TaxiOrderStatusEnum.OnTheWayToEnd.code -> { //送驾中 + dismissDialog() + taskStatus.text = resources.getString(R.string.task_start_end_site) + + actv_end_order.visibility = VISIBLE + actv_end_order.text = ResourcesUtils.getString(R.string.module_och_taxi_order_close) + commonSlideViewStartServer.visibility = GONE + actv_submit_task.visibility = GONE + + } + + TaxiOrderStatusEnum.UserArriveAtStart.code, TaxiOrderStatusEnum.ArriveAtStart.code -> { + //乘客到达上车点, 验证成功 ; 到达乘客上车点 + dismissDialog() + taskStatus.text = resources.getString(R.string.arrived_start_site) + + actv_end_order.visibility = GONE + commonSlideViewStartServer.visibility = GONE + actv_submit_task.visibility = VISIBLE + if (order.orderStatus == TaxiOrderStatusEnum.UserArriveAtStart.code) { + actv_submit_task.text = + ResourcesUtils.getString(R.string.module_och_taxi_order_server_start) + } + else { + actv_submit_task.text = + ResourcesUtils.getString(R.string.module_och_taxi_order_server_start_wait_check) + } + hideNaviBtns() + actv_time_end.text = TaskUtils.getCurrentTaskWaitTimeHtml() + } + + TaxiOrderStatusEnum.OnTheWayToStart.code -> { //前往上车地点 + dismissDialog() + taskStatus.text = resources.getString(R.string.task_start_start_site) + actv_end_order.visibility = GONE + commonSlideViewStartServer.visibility = VISIBLE + actv_submit_task.visibility = GONE + } + } + } + + //展示虚拟订单 + private fun updateVirtualTaskUI(taskAndOrder: QueryCurrentTaskRespBean.Result?) { + + if (taskAndOrder == null) return + + val startSite = taskAndOrder.startSite // 起点 + val endSite = taskAndOrder.endSite // 终点 + val currentStatus = taskAndOrder.currentStatus // 任务的状态 0:空闲 1:获取任务 2:开始任务 3:到达目的地 + + + + // 空页面 + include_empty.visibility = View.GONE + // 倒计时 + prepareTaskCountdownTv.visibility = View.GONE + // 行程信息 + group_itinerary_info.visibility = View.VISIBLE + // 订单信息 + gourp_order.visibility = View.GONE + // 途经点信息 + pathwayPoint.visibility = View.GONE + // 演练单标识 + aciv_task_type_exercise.visibility = View.VISIBLE + + naviToStart.visibility = View.GONE + naviToEnd.visibility = if (currentStatus >= TaskStatusEnum.StartTask.code) View.VISIBLE else View.GONE + + if (startSite == null || endSite == null) return + + taskStatus.text = resources.getString(R.string.task_start_end_site) + + + startStationName.text = startSite.siteName + endStationName.text = endSite.siteName + TaxiTaskModel.getCurrentOrderTrajectoryList().also { + val taskTrajectory = it.firstOrNull {taskAndOrder.lineId == it.lineId} + trajectoryType.visibility = if (taskTrajectory?.source == 2) + View.VISIBLE else View.GONE + } + updatePrepareTaskDelayUI(0, false) + } + + private fun updatePathwayPoint(taskType: Int, endSiteName: String?) { + if (TextUtils.isEmpty(endSiteName)) return + pathwayPoint.visibility = if (taskType == TaskTypeEnum.ToOrderStartTask.code) + View.VISIBLE else View.GONE + v_bg_route_point_station_name.text = "途径: $endSiteName" + } + + private fun updateMapMarkers(taskAndOrder: QueryCurrentTaskRespBean.Result?) { + if (taskAndOrder?.startSite != null + && taskAndOrder.endSite != null + ) { + when (taskAndOrder.currentStatus) { + TaskStatusEnum.GetTask.code -> { + setOrRemoveMapMaker( + true, + TAXI_START_MAP_MAKER, + taskAndOrder.startSite!!.wgs84Lat, + taskAndOrder.startSite!!.wgs84Lon, + R.raw.star_marker + ) + setOrRemoveMapMaker( + true, + TAXI_END_MAP_MAKER, + taskAndOrder.endSite!!.wgs84Lat, + taskAndOrder.endSite!!.wgs84Lon, + R.raw.end_marker + ) + } + + TaskStatusEnum.StartTask.code -> { + setOrRemoveMapMaker( + false, + TAXI_START_MAP_MAKER, + taskAndOrder.startSite!!.wgs84Lat, + taskAndOrder.startSite!!.wgs84Lon, + R.raw.star_marker + ) + setOrRemoveMapMaker( + true, + TAXI_END_MAP_MAKER, + taskAndOrder.endSite!!.wgs84Lat, + taskAndOrder.endSite!!.wgs84Lon, + R.raw.end_marker + ) + } + + TaskStatusEnum.CompleteTask.code -> { + setOrRemoveMapMaker( + false, + TAXI_START_MAP_MAKER, + taskAndOrder.startSite!!.wgs84Lat, + taskAndOrder.startSite!!.wgs84Lon, + R.raw.star_marker + ) + setOrRemoveMapMaker( + false, + TAXI_END_MAP_MAKER, + taskAndOrder.endSite!!.wgs84Lat, + taskAndOrder.endSite!!.wgs84Lon, + R.raw.end_marker + ) + } + } + } else { + d( + TAG, + "CurrentTaskWithOrder == " + GsonUtil.jsonFromObject(taskAndOrder) + ) + } + } + + /** + * 绘制地图起点终点 + * @param isAdd + * @param uuid + */ + private fun setOrRemoveMapMaker( + isAdd: Boolean, uuid: String, + lat: Double, lon: Double, resourceId: Int + ) { + if (isAdd) { + MapMakerManager.addMapMaker(TYPE_MARKER_TAXI_ORDER, uuid, lat, lon, resourceId) + } else { + MapMakerManager.removeMapMaker(uuid, lat, lon) + } + } + + /** + * 根据任务状态计算剩余历程和时间 + */ + private fun updateRemainDistanceAndTime(isVoicePlay: Boolean) { + val currentTaskWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() ?: return + d(TAG, "updateRemainDistanceAndTime ${currentTaskWithOrder.currentStatus}") + if (currentTaskWithOrder.currentStatus == TaskStatusEnum.StartTask.code) { + if (currentTaskWithOrder.endSite != null) { + startNaviToStation( + isVoicePlay, currentTaskWithOrder.endSite!!.gcjLat, + currentTaskWithOrder.endSite!!.gcjLon + ) + } + + } else if (currentTaskWithOrder.currentStatus == TaskStatusEnum.CompleteTask.code && + currentTaskWithOrder.taskType <= TaskTypeEnum.ToOrderStartTask.code + ) { + actv_distance_end.text = "已到达 ${currentTaskWithOrder.endSite?.siteName}" + } else { + actv_distance_end.text = "距离 -- 公里, 用时 -- 分钟" + } + } + + private fun startNaviToStation(isVoicePlay: Boolean, stationLat: Double, stationLng: Double) { + AmapNaviToDestinationModel.getInstance(context).destroyAmaNavi() + val gcJ02Location = OchLocationManager.getGCJ02Location() + val mCurLatitude = gcJ02Location.latitude + val mCurLongitude = gcJ02Location.longitude + d(TAG, "currentLatLng=$mCurLatitude $mCurLongitude") + val startNaviLatLng = NaviLatLng(mCurLatitude, mCurLongitude) + val endNaviLatLng = NaviLatLng(stationLat, stationLng) + AmapNaviToDestinationModel.getInstance(context).initAMapNavi(startNaviLatLng, endNaviLatLng) + AmapNaviToDestinationModel.getInstance(context).setVoiceIsMute(isVoicePlay) + AmapNaviToDestinationModel.getInstance(context).setTaxiNaviChangedCallback(this) + } + + private fun showDialog(){ + if(dialog==null&&context!=null){ + + } + context?.let { + if(dialog==null) { + dialog = ItinerarySummaryDialog(it, true, R.style.summary_dialog) + } + dialog?.let { dialogInner -> + if(!dialogInner.isShowing){ + dialogInner.show() + } + } + } + + } + + private fun dismissDialog(){ + dialog?.let { + if(it.isShowing){ + it.dismiss() + } + } + dialog = null + } + + private fun hideNaviBtns() { + naviToStart.visibility = View.GONE + naviToEnd.visibility = View.GONE + AmapNaviToDestinationModel.getInstance(context).destroyAmaNavi() + fragment?.let { + FlowBus.with(TaxiDriverEventConst.TaxiFragmentEvent.EVENT_TYPE_SHOW_AMAP_NAVI_TO_STATION_FRAGMENT) + .post(it.lifecycleScope, false) + FlowBus.with(TaxiDriverEventConst.TaxiFragmentEvent.EVENT_TYPE_SHOW_ROUTING_TO_STATION_FRAGMENT) + .post(it.lifecycleScope, false) + } + + } + + override fun onCurrentNaviDistAndTimeChanged(meters: Int, timeInSecond: Long) { + actv_distance_end.text = TaskUtils.getCurrentTaskDistance(meters.toLong()) + actv_time_end.text = TaskUtils.getCurrentTaskTime(timeInSecond) + } + + override fun reInitNaviAmap(isPlay: Boolean, isRestart: Boolean) { + d(TAG, "isPlay = $isPlay, isRestart=$isRestart") + if (!isRestart) { + fragment?.let { + FlowBus.with(TaxiDriverEventConst.TaxiFragmentEvent.EVENT_TYPE_SHOW_AMAP_NAVI_TO_STATION_FRAGMENT) + .post(it.lifecycleScope, false) + } + return + } + val currentTaskWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + + UiThreadHandler.postDelayed({ + updateRemainDistanceAndTime(false) + }, 2000) + + UiThreadHandler.postDelayed({ + if (currentTaskWithOrder?.currentStatus == TaskStatusEnum.GetTask.code + ) { + if (naviToStart.visibility == View.GONE) { + naviToStart.visibility = View.VISIBLE + } + } + if ((currentTaskWithOrder?.currentStatus == TaskStatusEnum.StartTask.code + && currentTaskWithOrder.order == null) + || (currentTaskWithOrder?.order?.orderStatus == TaxiOrderStatusEnum.OnTheWayToEnd.code) + ) { + if (naviToEnd.visibility == View.GONE) { + naviToEnd.visibility = View.VISIBLE + } + } + }, 3000) } } diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/wigets/TaxiSelectViewGroup.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/wigets/TaxiSelectViewGroup.kt index 9c04f15962..6e73abf9f6 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/wigets/TaxiSelectViewGroup.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/wigets/TaxiSelectViewGroup.kt @@ -61,7 +61,7 @@ class TaxiSelectViewGroup @JvmOverloads constructor( override fun onVisibilityAggregated(isVisible: Boolean) { super.onVisibilityAggregated(isVisible) if(isVisible){ - + textCurrentItinerary.setCheck(true) }else{ } diff --git a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_itinerary_current.xml b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_itinerary_current.xml index fd6679d196..c473742cc3 100644 --- a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_itinerary_current.xml +++ b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_itinerary_current.xml @@ -8,7 +8,7 @@ xmlns:tools="http://schemas.android.com/tools"> - + + + @@ -144,14 +158,14 @@ android:textSize="@dimen/dp_32" android:textColor="@color/taxi_color_CCCCCC" app:layout_constraintTop_toBottomOf="@+id/actv_distance_end" - app:layout_constraintStart_toStartOf="@+id/actv_current_itinerary_start_name" - app:layout_constraintBottom_toTopOf="@+id/actv_current_itinerary_end_name" + app:layout_constraintStart_toStartOf="@+id/startStationName" + app:layout_constraintBottom_toTopOf="@+id/endStationName" android:layout_width="wrap_content" android:layout_height="wrap_content"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_switch_itinerary.xml b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_switch_itinerary.xml index 2c807a6f4e..d2790945a0 100644 --- a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_switch_itinerary.xml +++ b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_switch_itinerary.xml @@ -28,19 +28,21 @@ app:layout_constraintStart_toStartOf="parent" android:layout_marginTop="@dimen/dp_28" app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" android:visibility="gone" app:layout_constraintTop_toBottomOf="@+id/taxiServerSelector" android:layout_width="match_parent" - android:layout_height="wrap_content"/> + android:layout_height="0dp"/> + android:layout_height="0dp"/> \ No newline at end of file diff --git a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_taxi_base_fragment.xml b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_taxi_base_fragment.xml index d1f28311ba..daf8149f23 100644 --- a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_taxi_base_fragment.xml +++ b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_taxi_base_fragment.xml @@ -75,4 +75,13 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> + + + \ No newline at end of file diff --git a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_taxi_panel.xml b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_taxi_panel.xml index 277e864b33..84ba5addce 100644 --- a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_taxi_panel.xml +++ b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_taxi_panel.xml @@ -9,11 +9,4 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" /> - - \ No newline at end of file diff --git a/OCH/taxi/unmanned-driver/src/main/res/values/colors.xml b/OCH/taxi/unmanned-driver/src/main/res/values/colors.xml index cd63f6153e..510aaafcfb 100644 --- a/OCH/taxi/unmanned-driver/src/main/res/values/colors.xml +++ b/OCH/taxi/unmanned-driver/src/main/res/values/colors.xml @@ -43,4 +43,5 @@ #80000000 #2EACFF #FF4E41 + #91A1EA \ No newline at end of file From 6223e3bb591c615a7ad20a585e210c045d4e8245 Mon Sep 17 00:00:00 2001 From: yangyakun Date: Sat, 12 Oct 2024 16:43:23 +0800 Subject: [PATCH 3/4] =?UTF-8?q?[6.7.0]=20[fea]=20[=E5=BE=85=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E9=A1=B5=E9=9D=A2]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/itinerarynext/ItineraryNextView.kt | 92 ++++++++++++++++++- .../itinerayswitch/ItinerarySwitchView.kt | 4 - .../res/layout/unmanned_itinerary_next.xml | 33 ++++--- 3 files changed, 113 insertions(+), 16 deletions(-) diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarynext/ItineraryNextView.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarynext/ItineraryNextView.kt index 964d18fa25..84faaa5f93 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarynext/ItineraryNextView.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerarynext/ItineraryNextView.kt @@ -3,14 +3,30 @@ package com.mogo.och.unmanned.taxi.ui.task.itinerarynext import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater +import android.view.View import androidx.constraintlayout.widget.ConstraintLayout import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeViewModelStoreOwner -import com.mogo.och.common.module.wigets.WindowRelativeLayout +import com.mogo.eagle.core.utilcode.kotlin.onClick +import com.mogo.och.common.module.utils.FlowBus import com.mogo.och.unmanned.taxi.R import com.mogo.och.unmanned.taxi.TaxiUnmannedDriverProvider +import com.mogo.och.unmanned.taxi.bean.OrderDetail +import com.mogo.och.unmanned.taxi.bean.QueryCurrentTaskRespBean +import com.mogo.och.unmanned.taxi.constant.TaskStatusEnum +import com.mogo.och.unmanned.taxi.constant.TaskTypeEnum +import com.mogo.och.unmanned.taxi.constant.TaxiDriverEventConst import com.mogo.och.unmanned.taxi.ui.itinerarynext.ItineraryNextModel +import com.mogo.och.unmanned.taxi.ui.task.TaxiOrderCancelDialog +import com.mogo.och.unmanned.taxi.ui.task.TaxiTaskModel +import kotlinx.android.synthetic.main.unmanned_itinerary_next.view.aciv_order_close +import kotlinx.android.synthetic.main.unmanned_itinerary_next.view.actv_end_station_name +import kotlinx.android.synthetic.main.unmanned_itinerary_next.view.actv_order_count +import kotlinx.android.synthetic.main.unmanned_itinerary_next.view.actv_order_phone +import kotlinx.android.synthetic.main.unmanned_itinerary_next.view.actv_start_station_name +import kotlinx.android.synthetic.main.unmanned_itinerary_next.view.group_order_next +import kotlinx.android.synthetic.main.unmanned_itinerary_next.view.include_empty class ItineraryNextView: ConstraintLayout, ItineraryNextModel.SwtichLineViewCallback { @@ -40,6 +56,17 @@ class ItineraryNextView: ConstraintLayout, ItineraryNextModel.SwtichLineViewCall private fun initView() { fragment = TaxiUnmannedDriverProvider.getFragmentInfo() + + aciv_order_close.onClick { //取消待服务订单 + val currentWithOrder = TaxiTaskModel.getCurrentTaskWithOrder() + if (currentWithOrder?.order != null) { + val order = currentWithOrder.order + TaxiOrderCancelDialog(context, order!!.orderStatus) { type -> + TaxiTaskModel.cancelOrder(order.orderNo,type) + }.show() + } + } + showNoNextTaskView() } @@ -49,6 +76,69 @@ class ItineraryNextView: ConstraintLayout, ItineraryNextModel.SwtichLineViewCall ViewModelProvider(it)[ItineraryNextModel::class.java] } viewModel?.setDistanceCallback(this) + initTaskDebugViewListener() + } + + fun onTaskDataChanged(taskWithOrder: QueryCurrentTaskRespBean.Result?) { + if (taskWithOrder?.order == null) { + showNoNextTaskView() + return + } + + if (taskWithOrder.taskType == TaskTypeEnum.VirtualTask.code && + taskWithOrder.currentStatus != TaskStatusEnum.CompleteTask.code + ) { + val order = taskWithOrder.order + updateTaskUI(order!!) + } else { + showNoNextTaskView() + } + } + + private fun showNoNextTaskView() { + include_empty.visibility = View.VISIBLE + group_order_next.visibility = View.GONE + } + + private fun showNextTaskView() { + include_empty.visibility = View.GONE + group_order_next.visibility = View.VISIBLE + } + + private fun updateTaskUI(order: OrderDetail) { + showNextTaskView() + + var tempPhone = order.bookingUserPhone + + tempPhone.let { + if (it.length > 8) { + //截取电话号码前三位 + val phoneNumPre = it.substring(0, 3) + //截取电话号码后四位 + val phoneNumFix = it.substring(7) + tempPhone = "$phoneNumPre****$phoneNumFix" + } + } + actv_order_phone.text = tempPhone + + order.passengerSize + actv_order_count.text = "${order.passengerSize}人" + + order.orderStartSite?.let { + actv_start_station_name.text = it.siteName + } + order.orderEndSite?.let { + actv_end_station_name.text = it.siteName + } + } + + private fun initTaskDebugViewListener() { + fragment?.let { fr-> + FlowBus.with(TaxiDriverEventConst.TabFragmentEvent.EVENT_TYPE_TASK_WITH_ORDER_CHANGED) + .register(fr) { taskWithOrder -> + onTaskDataChanged(taskWithOrder) + } + } } } diff --git a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerayswitch/ItinerarySwitchView.kt b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerayswitch/ItinerarySwitchView.kt index bc24b02e20..5a4ed664ae 100644 --- a/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerayswitch/ItinerarySwitchView.kt +++ b/OCH/taxi/unmanned-driver/src/main/java/com/mogo/och/unmanned/taxi/ui/task/itinerayswitch/ItinerarySwitchView.kt @@ -84,10 +84,6 @@ class ItinerarySwitchView: ConstraintLayout, ItinerarySwitchModel.SwtichLineView .register(it) { show -> taxiServerSelector.setNextItineraryRedBagVisable( if (show) View.VISIBLE else View.GONE) } -// FlowBus.with(TaxiDriverEventConst.TabFragmentEvent.EVENT_TYPE_TASK_WITH_ORDER_CHANGED) -// .register(it) { taskWithOrder -> -// nextTaskFragment?.onTaskDataChanged(taskWithOrder) -// } } } diff --git a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_itinerary_next.xml b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_itinerary_next.xml index 52d197d77d..154b09b2c4 100644 --- a/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_itinerary_next.xml +++ b/OCH/taxi/unmanned-driver/src/main/res/layout/unmanned_itinerary_next.xml @@ -72,17 +72,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"/> - - - + + + + \ No newline at end of file From 59cb483509f37e4b46456070a7f740f90aa33ccf Mon Sep 17 00:00:00 2001 From: xuxinchao Date: Sat, 12 Oct 2024 16:43:40 +0800 Subject: [PATCH 4/4] =?UTF-8?q?[6.7.0]=E6=9B=BF=E6=8D=A2=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E7=9B=92=E5=AD=90=E4=B8=8A=E6=8A=A5ICON=E8=B5=84=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../res/drawable-xhdpi/icon_report_error.png | Bin 15683 -> 16207 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/core/function-impl/mogo-core-function-hmi/src/main/res/drawable-xhdpi/icon_report_error.png b/core/function-impl/mogo-core-function-hmi/src/main/res/drawable-xhdpi/icon_report_error.png index 54f4c05d775c3a3e406e915dcf5a4599cd8ff61b..766d4332f7fbf589bf9619f999c673466ed2062c 100644 GIT binary patch literal 16207 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 b-Fjx1>a}tRukb?s4`!E_R*|ZbFb(-%as4vh literal 15683 zcmYLwXFMC;_rKO^YZa{(qpGUItUc4#s;bedy~W;pMkqz?mYPA-tlE2Tg2q-e2tw=; zF@lKS=lkUUe{k+O_ulh*-TUC)Gv4=}Xl+eZntN>bNJvO%)ZQt5`j7knkM251|M6S@ z8_T`@|7QJjN&mn6KgBz1x(6gAa^-4DZ$A6q>dy`sFE`X(Mx8GsspW<5Sn<576M0Ye z@P0}jJsahRd!P?rUp!|_{`}ZR`g7jH_*9#i&oQ4rscmrGo`9!+0zG>B!sdgTQYLHG zeX&6cC5pS@&t8eWQ$$^5%XQk7+jU-L9fBL(crusHv)a#%#&Bizvk=sI9BSL=;%3}; z>{=C8n*}`Aj(`l-$okzRpRQlpG%kji`kkAlp2{p$n5HiI?ADmo8vE+s^)qXvI;k<8 z+xOl1x~AE;q`7q0Z(5JW#&i++yfsMVU#t)*=!l}Ryh!h%?aAwy{I1s6*5oB#tkB;O zwFKnGb<<*_VCyLeF*H=Z>v^Dv2s~C8K?x0BrEVN`zSFvemKreFy=z z?A5$;R;l-WprkHQSH4pzrFFJCx8Cw+Tg>v+fY}!8y)2Uch+=|yId;g_Ur~Zu!7XWT z#xY^IU*GSkd8X=xJHw$T;p{mvDg_7V57@7f4LJ2|EH$eqj*oEtxEd=D(11CdE+qo5 zcQ$&&wWf|?d4Iv>5|Oo4_evkm*bcq^k^xcaAZ zZUsa2)kj~>DHN%rp4DG)QeVdinmbW$wiPgH?yZNp9*c(b)j7q|g-pzM8oPZo6EYb! z^gWf!d7_&yODez>r!iaq5vd#Y6t-O~@3~hPRBDTAOxINI+1?v)7iui+n4 z@FT9(&e{=%DcwV!X4c=7y={dNh?F_}e89YcoX*m?Llw9}jNzNc;VERwR+roiN56b5 zs(&zpabJ%3_=me`1U{WPVLFrQ2;vLTG9J@-D>^qlgy&timmQt zZj=ne8-2Mnaddc(5pLajO7JLM_Tl6`bNR5DdX0cyzMC`M#Y`?VQpK#}MW`$fv0JxS zL~H)!tBc=pJ_6|)G;k=yW(n=ZEQ7+0E+>(!t{p5#*bfVuJ)*h z{A|rgoDo(9+<&=#*n~h8{i=mcIACYhaqwk_g-|Up@Q})~n=^@i1 zKFqJeH0Gd1mybiTX={orZ+41NT`~vGvVL}VSNv|UR}c&7MnYs{;#pqR4+g`MYD2Hm z0O@eLr#R7zyh0ZH%ez`J@GQq|mCaUo8cjYv@}HEy-M4H}6qpdaVyoNRc#}`f;3>sI4J=4@81_=*&Fa4S0cEGxqPxA9F|5_(>fq1_hCPnOR}}Cj z%Jv-)ap%jR^C2ps@w}XMJ*-|~7bzfQ=4zhP zqOfZRjOpVLA86+JE_trQZ1#)ifPTJ-J=l6jWM0#yHXz{4xdEg76GV7*zV&7|8#?~BK{z09wOi~%Hm14wHFe8j93N`^v94p+&lRhF{!nWXX_{`- z!q9{AMUJl32G%!C)oQE%9A?q!d%$7lo}J)R<1j-56j^}|n&Ow;*Ql0phd;Z?sfmj>(&DmL%QAM&e!z>w5N#X$EQm*K zuvBk=k-8>&q=MA=s^XhaSz++JlPN>2d_+yT#DI8_aMzi8CX2r5g)!r7AJ%{b_eFR` zW!DvBXCj}@L;1Ila`Tm8MqNQtW=~uS%lBVy=Y8j2Od+10Rwpge#JLVv(TP{ZFWGUVphEQfhV|14BLH?~UT;`m=f z-1hFL=b_7Oyp<2Sz-#HoXZB&;^ktjrRZlBKSgKOwJ9-Xrl!OPU2tQ)@VVh0yVfs<< z>tO=@xi{FHOZHg{2KRACGwAeld#Qf`$C5?o^>0a$GqYpwjbJ_B`vj0;3QNXIXPTx> znLJx0H?04Q$nEDRb5+R>6&^i&gIeQp4nAS}oWG`6^M~ zpsHmE#YVr`5a?bC={YT#$)Y9r2taKZQvv%C4yG&@vp zwKYp0m)l#aU2$>$%hFe61zjw7XPVdT^KYq_)H^!c=tpToQzfgn)CT%32xdH4&iHCM4?C|Cs+AvTrkilYK?7yED@aCMl7!U$ICM1}B(S_W)+ z`Fx!G{F0q?Gqi(F-^IDgN}<2MHk3E4q`yA-iM?6X$Agp#04SzM*6>GjjS4@O4j79jR6xV=IUH zxES63Gj01Bc7FoFR^}j#edOT;3GClq9kj!wogF-%E>1n2o&hpUqabxvd7FE0htL8} zI6I^q?J$+as-q!wM_g&zl*AG?aqo0Yq&Pro#xV$gqP&c^nIha<3({dHBwR!YESeEU zq2UoB3BZkQaT~j*vPIG%zHYU;M8jB)o}l{Mypi>?t~fXIG1%4KDTWzbFuKwT-oY-L zP)_NcZp;m(=4@Fjze>{h-XOhlWashZUR9U$-*+#d1_wPB=t$IRaKYc%hVI4^F@YsH z3C|;0IA>IRLh3+vrt@4k=W#V+J1dX3w~JSyshiox7~~Kh3j_(f_JzVu|27Ty-GB46 z6Zb&Ch&ob%;M45b?0xKi;uP+;8~;I@>EBHNj|-s0AB_zI0J1&s?JTv_(}S_kXrt@; zY}4)n%4b?Rvis(xelv~pbU7v{SC*K?u(?4o7`lWq#sND&HG>8*NriDEzMd-(W)#8t zdG9EC)x?(e;$kqR8j3z=ah1Y=VED7zPf3n<*REo2kIt>(jf-0QgLY_^1^6TrXq+tF z3gCC>-c;TrE&BA~Ry=f+`j}KRk-+t;mwY%RPaNA&%A9xpq-un_1r9M@A)2_tC}zcK!|hSVi+Wbd4JYaors z`afN+pL`AwH=nCrYm95*0nalu;&;iK>Zt zOSC}G62OKD7iBVK;(;LZU=cDE7Tt#+8@-XHg#ew2$IMO*Ba4WmY{ugh#>;4*^pK19 zPJL2vJ_m%Z);;@76dG|nME;)ZcSQu*MTfrh+~<=Qy2j!{$Zf)Yr~CFL6(Pm^UK0{X z1_xD0COxxaeVOI5UI4IK*OwT(wEq)s@kEb7J1f1Vd^)-!0JmQf4*-92rA>|Ydi|5V zhUxRpGi|-+A6Q5{B%?f_CfYL}3%|#(Ruu~cH$8w_Ijj;HYQdqVs{*koDXAaJWcQ{cKks*7#US$^W& z)BIHXt-@f)mQ=@=nWuAEA?5TO40FewiV*Lu*r4zn>+)G+^6&?L#NIjl_0b+_VAFey z_NY%tyPfh_t>3YQx_;%Dr?*%^>?Grmx%gt{R^hF@j#a0Q>ov)Qury6^oHXCdVopifrD%>x z*!HPsiZ%cQKO|@=H1XeTh6o`_&GUftn{GXuq$=zQzSxV(9g|ZpUO?=VX1(~Ad~ZWV zRFnTzT0tio*sdTezUP~UC#N7>nV|af)Zg%w8AzAFk1&AZ_28W^njaYX#`T6>cjZrJ z3$?C(*)$`Tke(47mkHe9OKq(ShW${+oGa2{5p(x{5!g7aLBiN%V8>38&BY?6&@K)F zwut#u@0F<|<((cj_+XYZMtr#Yy%fKCoX^D5y-w`LN0tou+AYe1UGq=< z6L~g8%G=q$26QQ=F7{=4CklhGD5=vZ-Oe9_9OVn;h+i{ z{wqmXWokv9tx3!y!%+=uvB}C~FMesgTB>*QKAmv0!e3k58mC z%3@s@2c*D^H;xd+qTGL^n)X?b;#?YIGZ952J@9xV&oG)Sia3W26kbX~lQ04_tZaX@ z>GKl^KpL+;x)X?wa%Xg@Y!!HPx7+o@ZLgmttOI{oeQJD74n|8Er2NN}EfnDUPhJoF zv0|ij9b{DPwFF|$Z8=sM)1}@~pbrlo|7I#raQY2DS^V2+3aBkQ%}cBh}7 zrv1QoE_=8ysQ0%D5I)6w^0N}FtM-kkfBi*Iz&lIeor{P}jwda@!q~=v%a3N^Y90`5 z51Ufwj-lTavyJsy;6vyJ@~Lx4-RXz_B>7SI@%^XNiMDNyXOy8%o2@N!bRA@rA(#*7 zA9LTne|deWAHn)n3OQQmfu+_2ylBq!H~IMhd=j3>`FKeai%cVRt&~{{h2g0;#W`n$ z(pDFSuP-lFUs0+>$@iZZZCF^4vg!}BKYNvB=XG%^6|cTsr@iTNoOK6n;`)`>d#nw#DM*1@&--CDdwU6{233ZRhA~;ds zuB}nZd)WJvrg*j{rZ4I|{aW#Z6Lv;b&%|6YMi(_;?4>iXr!#ltluIanQ7UN=A-+A48ak1f66FVcnW&%L}|^w{SPjZlJ9UfdP{36?e#oztP?*O zo3Abg+*5OU&07%K74(G!qr@m?a1}8G@0E?^5(tzW&_3#Iu(P`rFFiwN981#9(80^+ z67!nsX014NW7fQ$zw`3dwJYj6b;GISquaRLZ;_)ePgR^X;PP?)3u-^N$L~DaAg8R9 zjnj3Uw3{;Ye@_94Ve$wR)~t&wUttUUQXk zQ&mQHJ!uorBgv1DKgbv)6FL}9^ZD1lpV6wdsHYZxPjo4qs#AL63!4BgDg=G?QWmmD zE}2pBQIn4z;ATSmBOTn>xx7mG~_p?vPKqqRQKNCX$UT8JN23&Py<{ z&A;dX8u$uba3PL!{B*%XHth$fV;1I3$t z!*J*Q!uXrPESIXL|AjGJn8c^s&3cfj;*P#z^%hVG6IdH@Txy%Jcp)KR#VKozW52&I_%=nsmSx;sy>e)Een)Db3&3jRzil%%zhj+_@N4C?3)$cBnSxuY6L*(RKK>UO zH}NNe_i7+`t<;Ue8?X8H{qhvxi7~LHtP|g3a}MpSDKp2-rf%Rk`CpccdA_zlRCAywSmLtx{=mo-@b8uWtpnc*p)Vy==M5@&>-*KPGaK`IW{HxIQ z`|Gb4vjT{vNhDqmi)uB7^uYlSmr<(z z40>;H-g_x{#%}jjY1$h}uqEQJ3iKk*Ve?evlE*s|7iW~%-#WX^5L^wxrd95i9r{CI zokvF)4JRXsCqAQwIpN&(WwmTX&}sf%$^{&U4Z?zf`g1Kep%{_wfgwvo_7!fYejONqCh|z(h^QzMrZ9 z{OMV;(Cq6I%gP_KKQ33H13>{vUd1ANWu`Zn^NL!*SCsl=dEDyRc6Rp8YCmbJNQ=`o zioQqP9jY4}urTIOvTwF5E`$x3kiUpNrcCbm7I6#81Z=;ML4<5dp6(eW)^hC*7+QL4 zfAO7{-w{Dv&Q(s(>1}zWrt*2zCJdwvJ-lf@+2}3?BWt#XP0cPlQ>PQh{uS-00+*xs z#1YeK$uu6C_N#o%@#lE)UQvRs>@T+6=6XqDL>tA~3_SXM|8W;|E@DxZpj;zY#ai@4fHsV-CJ|JjAK<2lCrgbuI5+0CSqL;Mr4pFr_ zFDLB+G6Tzsl$xp<*@7!<)X5%CadTuO(-e^oNAj+y z%d5_p&-=SZ;?ra#A;I#aJCkw?W&L<}&Xp>vZJv+75~m@LKeKvHpY8=uUC!&IpbO^xMcNOp*j?I1D(HpE39&jnSl zUd5$xRO^6|{CeGGJUs2h0D8=;Aq~X=iwfI4F$!h~w_}AzHl5LcqIUx%nUm|VNGn_u zODCpkX~aC@9yzAMPSg+H6cc8=F$-kggTh+MLXIkI0+zLm=~x8xyK`J93uxXxZ>V0+ zm4I`&viC^=YEvrk%sa14-uj4DSFe0r)q5R?be^5-xM$S1W(~?#1kuMWt!Yl*`J^)? zWfxhz50EDLF@odt1Fv8P2I8tu20fcm*NJYfM+bZ^2nU70_!4LLIXXHSkk50s1+$!J zTrOpXetk`@P3#{NQ%t$$qxY(%M@BK>CSdgOvPj%Z?m3M4pi1*Io3r5P(2_FwsC=lR z#us5*jvp-k?BD;L`LooNaJQn46#uaayaJv1Pdb@cG2Yf`Zv=NG;;Is#p3!ZOeIy>X zrfz^5cNED@Jr7I#YB^6VLjJ}CAu73wNA{(HAoE6&&C)`kz}_eH=7EbxaFbVNRHgnBZo1wbYp`n*~zm4YkZaWbTzx+CwQeWLI{T&p&q5-(7!~t=ZCWcu^(o zuiulL4+eU7O^?K?NEihLTe3fFt^@kFD!d>R+MMSEQsgw5HA)1Y4^|q?49J<8-yYw! z$P#6aPcZd_di{$RP3rrHE@ubWK?#{moRzMV)TqmE$W%|Uke)BIAor-MM=LD^J?#Vf z)b{JGnJ{5tOahtX-_~aZrfG@MT$zEAIQTl=hEB4{V#*66TJePM1MeG(X_Bo_S=^~h zFGDy{->iebjeO{-ziHv!$yNN5?OC%{emRFrrfc?xA<0VUdEaAoH(A2pV#i4tn@`y+ z?zgugbB`Qz5H5Q*6+zAz_gS#q2i3|OW{${fo?Z%MRkEK;#om#PVpqV0Yi<3+=-OHL zt!3z^cIMlD9o(9w!qCdH;6$$$j$`QV+_7#apLWZDSZKQ@pm?^Dh||MoX(Zb^+9+R( zuO4*ns4=D;w*Aahl$Mk*g)bhtg^i9qm^aZO1@IU<+8WJ{(K*eOo@N||R|il~t>9ag zL%=hDZ=sozKixCeaG`fyky1u4uq}8d*Jb}vtKCdmX3te{#Je{OW<>ZZ&MHQyzn4$8 zoRJI7wqnl(o8JLyIJvl-8?sXeJ9Vq#6hq3=L~ju2zh+=vyKD8!qg6OGb>pM?M(woz z?~cc1T-U#sKRh2De0kBVMZI=v`?6Ffbg=Tp-h;V1h$$pg5 z14R{##ldn%W*LRq&L&tSX`$d0txBLaYjsn`ZCb#^SjXfs{IT~S3`6^*LvTWX#7uR9 z|J!#`D$zZGtbCWb6b_JBOs|3qK|s^aGCM29kX=pc#{nO|op}6cI()v#jSq6~KS=LZ zP+Vfpe=q^gyweQEe~L-)-y9R#o&C?6Hs6qHCw-po=b~SDt=a4WXBbWp!Tg*1z*Z~x zp`$*h$Ziwmuf%{rXI`x+#TIoe&rDb_S^x6tdchD_A8;0XxUWbJ2`o~@OPpf#8SYav zz3E^1K=#$3)p4=m{qO4& zMM&_+Ooa*1e1<=kLEo0bZOIk;@0Z2z{1Kee|mJn(zv0_hD>pd z8@dyulkEgQF0A0~T&3VD^CEkLw)sSP`ztW9J~g=;Kzi4XEo-^@+tgIp*t|>o84bgHwO~;{t{BEas zx;E7AN#M_^z*BKFNlA-`@|U@4lQO27uTt{lAV03Jx{^$C@*&#PsR~kk)IbmkEni~g z)037J7JEaV$vR8kL%e}BSF9kh#_>FBlVOnHZ6 z6Gh7^w7eeZy_LSgPG^BTRX7cVUHQIP`-qzDVBovmPj$lx8%I3Pv9_Gy*!Mb3XD?r2 z01Zbtfm5r_@v$VHaNz@uMfCj#A6%&e!C&bP*wrw4Ya^~feelS5@-$)ctc%kgQ?@5F zL(-HIY&sIdDpn;lWw$6B&eD8(8iED+hc36uvl{Rmlr&Zz4HoYqCm|c^j?2w^5Z#c8 zqn$J=ge$4u6`GE}b@L{!BdYMGJ33R*EA_@EIM-6ZySp?%kgS-m%QYZg9>YN==ff^T zB3FD^-Kwx6*0ALCJm*2CqIoFsFD;`{c0heb%EQWl?u1$+JHEZ@)wAu02sAdQ#y1^bA4Pe8Pu1)nZ|OJ|xfE73uhi&>{@RU{MJTP0JoAhxifQ^v?Ne}DH_x?#o4CVULy4cUPE3k==U2wRDO@gpN z%lQKvKzd!1WBq~5RDkO^^id+nI>=#GtOh+4BQY=D^=7ogM}V@EskU9&P+$I~t^5Ha z$lS>!W3U2?+_mtip`ZU_C7*3j#rrh`&CGN!>zGEttE)2bqbbqz(Og1o!QHo@ z4wWWE$7mKzAwoPNRWh7(=bPqkuR_;uJE}NOE_z;G+C}%4-#-HH0Q(bqY(u zSt*wR@?CSn2OORI*W8h;zPa`*4#aT}Y+T>)l$n&=GfU)YlUH|A1Z&f(#OsIsc6R-M z3*-C5f`K^~GW$;2P)a#>!wyAM5N3af_{h?Jn+n?sAWqhROP5yzk0qxEB7=+=PIaud zTkJTYftKZk+#t86S-p#4g-s{+@zL{8)LQ%5c zdNeKWFY5LMYT6T^NmRfd=TRi8g&%k}Y~zY9q=1}4@@ zWv2%U&%199$qXONnRbqX@N942+Yg_;qjT{hk8^EG8B-=oZ0Eg$Xt8;^Jlnd*+3oBt zEbW#3T1lvvQdJPMyLF5%5K?9fIq4;eYFJD4E^Qf@jL30FBOh(JoyRI*xAWbv{?Y2}m5G)@1jAPD{m4QeHsy(Rc%5DuwAZQiju zWZUuOAxv1loF@}M@w4h8TW4F6c++O6AA?d^XVvk4rg}8&xDnx zYH&!1dG~U{mbZ;u1GWcIa>CC_#K42bS7-Be4b>@d|llyt>#N_<(C1M zM|XwEa(UdwKGrl#l&&UMO0DXft0}vUMdd6M33OzN7_A$E@7?F}$2)iUoHXzM8FTT2 z-9%gpo=7^wE?MlWW>;GIA6aFLrEo}){YwuYv$XFT)#O}R&Jso;c|i9on_#-j@=ICXsVj>< z%8)Hy&g(BZ9yVU>d%zy8ieG$miK>rR;Tu41+yPjAMs_)o?Y4tyjeTdW zb9QUhf)f0OqO-p3PXmuK?y(y_?D}i6TzVXp2}6_w_|EYp271zY9Q040ztwz(E3*Bt zy-?`zvKwfY3cH$LSmA@T6QANc3R5CR)7~=pO?6cpkbvZ$wh?JATgDKlzE5G#5elBv zdP*L-hEH2QylU{h%BPcoAlV*3(TpElIMX81xzNGQ#=&X_E9g56zXYc}=Ts#G;NL{^se?~A`b(hq|VYX}XaD$Dqcoulz3!j#3sIexaF!`|Zud?rc`GPpA_jQ0@36)w|kv_+~El z;}aL8uKIfYwp0^PD=S)RoJ1Q$PvzY9OUZ=WXTI{>L05-D2;-B>jLw%W=j|M$0Y@W= zX`Nt02r*Z!(W)N2wXxK@vcDQ`hGpWbpQOcssG>hVJX;+q*MCazUk z1{BG08KSJX|J8ftVL}uPgB~21f?ub9E4r|`-r!~G1(!^Ma{{mz) zwD`C+TG2zc;a60L&vk%2Mde{>#U9vFzWZ^j5OHR$S2(FI8O9se6mQ*^9vn zMg}I)TlCemNhR8sJHrQ^H_YZUA6a9cD%F)Gj*)3INvv;nc9s+-43{P8a&|?4Ujl;l)2-09j9pPT)h79xi54*-32ElwZ9*+u{I=o z@p(>S>7wTGbXGN)1^t7+-RySA-(&(K(QX2-N+e*-f-Q_fOOwnM=t_3`1?s)}uAY(R zzBC$d!Vx*Uhz#ulHOI>Z+UEXS-Irdu^bj#T!|x`xI@h%z+T8nQREzDTdD2WhLyK7f zdv~B5>;g@h)7Q?>f%(1UWWO9^!R*=kC_O4(%iBR22OdA7ohuTeIuJ$klw3O-c#tL`MPv+}BKMkKe)jl2RC>jk|z z!67@^v_uaQ1ZJq#@#A#E%DXl84w?t59;^25+5}G}EAEL&yr}-`Ng#?H^zI_e;5U+L zE=$*=y7)qMd ze0kW9bDr92OCmEeA&~~+u0#C%@8Zo4OE?gD25u+zC5;KN6uGcvT7>Q5L7W_?Jj??s zc2mC82Q?1Zeu**CW#`KN0{s)m)(!FMLoUm9;WzLfP>=JJM`^|HhY6z-0CZT*kdMx$ z2zhrA@51d6_)l{t?u&Zi? zu!OnUFS$7F;6WhzcOXzZkv8($v*Bnd=^bWL9npA-~*@UfY?9jBg~^z z`#ro6&!MZv(YwxE{2NiR6Mjp&;IbjqxAk;Ro)M46EQ<{bRxWaG5%)m) ziE1GDc03Cufd~71bS06-JJ+bJPK6-9Z-iKJdaW?=IBId{_1Cf@mLb{p{dg{LWwjVB zI_-cdTqKF|3g?VEPH+#gifPScRTv-CS>ZX>558dp7L1j05N z8Z(#&4z|Nlu;~nV{O!(s=-P~J)3-ic$cotLh?u`B-kH-j=8jo(3yH-v%WT`lZJXZq zFPc_sRKZMK;A7RGBl(I@TY#O?#)A-?6L(Brz%ggq+=G^N{v-k;%Ah9B>}0za$Szvk z{7rljIsnPK**j&1Sc2XBovX+ND{jnUn_OhW4IX~MtU1LQzVU4M7fH%58$0~A6o%gy zEtU*CGG@+}SD>iBD3j^mR!-CieZ5?uTjD}V>cQU1&cmGh+zhw<$$ryX5iJvPwaT5g z+;*#iTzavfhPl+!A@Yck7KGWZ&c>jMP`E&ein?|hvzz@f@ar0&ZvRUK%XFRlyVbNb zRRd`qv|snt-W}af#Hu|uN^sD?OC5dD4j;3gdkMK`1T#$QguCayu`t2LpMM4?6%Faz zdEyMGbK|?s2x||rt6%;wSOq1-=Y)S@LcV+5Pzzr{Qwas{iK+xOcHsLjo4=IryHt&& z2`cDN5e=Ar(74MRf&ZzfrDgNZz}Zk-iL2^ASU0gkcDi-X?eWo21v~5hvd6r(b<7e>lY*7~?+r^bTV@}}ya=x4){@Z*VA7<3(Bm@A-@javXan6uSSxks? zv-ft!mqK8D_ikqpvGpNVwR~6kJBhS+_X(stRzo#Q(}$!rY}u~T={+8VnHTa(yAm_>3c+lE5#X9}8^^#JmEF5b zXU+#lb@On!Gf)RBV9`0emLK>ZRFSgKdhP2Nz7Lr#&MCxUeK^l=?@<5xeQGWdM>EF> z5Z`^=hv^H}OR4lOSSCkMFuWAmdF1XrLBA&|#?sKvEVI@(i;bnBCap~vo3_DF+B;eP zG%@7340Wq;&%SA!9SS+h*Px98c-lj++Z(#2`|?`df|fjCS8^{`F*(zmF0F?*Gpd5E zUY);gZ-)Spr$3*?=Gs3m8#vSFemqFQBorJ?t?;omv|Hp{#x7`=rBoIN+HD$DmX5}x zb83g;(0|4)wvNd$(;gL7@?MC}R5IcZ!I>;m^L@f=SDy0MIb^-t5k=XS)_PYg$FQapgU_Nym`2v!KkPp__t_0zR7ZY{>YmF~+)?0PCUckLr#ZFK%JF)S`++#S4}_KYU+}sE z%F405VY+KobpGlHbQYe+-tEVufGOkB5*`88uUgD~!)L&zM|-kugt9kxL{Y^s-stOL zQUC7;*BytGZ%;N*4ddr;U3SS6dk|^0^Gzl_nFsN;N>oJ_+Z#ya;!uJaC!5N|5s&yP zLoas}s#e3;chlStfHI1xFQv(ty;LB|O%OEjSTeVu{)i4M@+)hs#rAt!_c8&~U%FT=Ozzs8lS3cz6 zsV9E@+u5xC)esGI@8`hYqTl`Q_WHuTSBRZd{28XOl7RTwOd`8r;M8=By|&+P4hAQt z5}C=h49rg+U)x-6AUR;&Yn5Y{hS%mSLUM0BPBuMNnJTA)Ky`QS^~PgzdfdIypfbI) z9ElNC1j3`M3N!R&613Tnk5bI<|Yi zTyn;mWHCT}u65wW`%#ku``~ZW>+cW!P!~VB9-Nxrt2F1GXy2W%8#M4PZPxy0CmRk= zW_nZtmB^Q9rNP$cTy(3v)IL@0#7<<>-LVq`ss8tYcrB6!vL{`2WSbR#uP7ZUPpk#! zR%C^!ZE?*o{t2m#HlN>`TCgbiZw-OO#q2Uk^_7V-%R3H9qNK|!ISv;I znSqPSsolk5^Mk_ite#bTjL<|>-{}Am37DZPPn@8u4u+*3oL5MT?(UtCPetGgnLU{s zVssrVKry9v``6(!WRimI6o~Lv>HiAR=sP6el)>XW@4jkUOSppjC%_2t>L;UQSw7RE zqbliAOWoP%VX3{0V%Tra1KcT;ZueZ^y!{ZQur#9VWjthAB`bouzV5_!ZS-7l8EVdS zmXa@z)X!u~>PL9o=RI7=-Hq@jiBtZ)7Vq(EfRzl!#_IZMwbv~}!#IIBPC9FQSREf% ze6gG!ok#yuV$&pbp$b1=7l-c5hhUsR*Ad8L9k6?Dy!`3%F^3xGrD>%ix>y!#-{Nx)2=;}w=dnL(hmU z88SH5sktr}p1-~`*W7}*lj49&n3%xW#Afpi@rb8$YW!s*SVdCG(ogGVS6>JwcO#bV zr8O~T2`RtlHQnZ+Yx1_#qOG!WmC*MR{YA%eAhPT4tvB8Cj}}&081)kce@lx>*lp&F zyr0@&;Tt0V2Wbh&PlyMp13$|1%K}5aVu^k05r}Dhpd0ZioSQI}ebelGlr?;?t<}DH z9IYj9c0-j6NK00i{PT)VzxyP4TetCjXtY5P^xuo;u7vde>IP7$P z(pX(R0jvKZs?w=nS^}}M2#&v;wFjAJxB(A(6})PJ=QIkbI|mM^0$|gU{Aa!a8bskn z;ORN@L16Gqpqsoa4)JoHQ7u m4fmWtBjJ~D$glmITe&NDhVtYe`2DYdSxs3}sp73g*#7}M&tP5v