[shuttle]
[变更文件夹]
This commit is contained in:
yangyakun
2023-08-23 15:37:11 +08:00
parent b6c4777ad4
commit 0b17fd5ca3
430 changed files with 6 additions and 6 deletions

View File

@@ -0,0 +1,19 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.och.bus">
<application>
<!--这里是为了测试增加的广播-->
<receiver android:name=".receiver.TestBusBroadcastReceiver">
<intent-filter>
<action android:name="com.bus.test_control" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<activity android:name=".ui.BusSwitchLineActivity"
android:theme="@style/SwitchLineDialogStyle"
android:launchMode="singleTask"
android:screenOrientation="landscape" />
</application>
</manifest>

View File

@@ -0,0 +1,101 @@
package com.mogo.och.bus;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS;
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager;
import com.mogo.eagle.core.function.call.setting.CallerMoGoUiSettingManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.och.bus.constant.BusConst;
import com.mogo.och.bus.fragment.BusFragment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* 网约车小巴业务实现入口
*
* @author tongchenfei
*/
@Route(path = BusConst.PATH)
public class BusProvider implements IMogoOCH {
private static final String TAG = "BusProvider";
private BusFragment busFragment;
private int containerId;
private FragmentActivity activity;
/**
* 进入鹰眼模式,设置手势缩放地图失效
*/
private void stepIntoVrMode() {
CallerLogger.INSTANCE.d(M_BUS + TAG, "进入vr模式");
CallerMoGoUiSettingManager.INSTANCE.stepInNightMode();//夜间模式 状态栏字体颜色变黑
}
@Override
public void init(Context context) {
}
private void showFragment() {
FragmentManager supportFragmentManager = activity.getSupportFragmentManager();
if (busFragment == null) {
CallerLogger.INSTANCE.d(TAG, "准备add fragment======");
Fragment fragmentByTag = supportFragmentManager.findFragmentByTag(BusFragment.TAG);
if (fragmentByTag instanceof BusFragment) {
busFragment = (BusFragment) fragmentByTag;
} else {
busFragment = new BusFragment();
}
if(!busFragment.isAdded()) {
supportFragmentManager.beginTransaction().add(containerId, busFragment, BusFragment.TAG).commitAllowingStateLoss();
}
return;
}
CallerLogger.INSTANCE.d(TAG, "准备show fragment");
supportFragmentManager.beginTransaction().show(busFragment).commitAllowingStateLoss();
}
private void hideFragment() {
if (busFragment != null) {
CallerLogger.INSTANCE.d(TAG, "准备hide fragment");
activity.getSupportFragmentManager().beginTransaction().hide(busFragment).commitAllowingStateLoss();
}
}
@Override
public void createCoverage(FragmentActivity activity, int containerId) {
}
@NotNull
@Override
public String getFunctionName() {
return null;
}
@Nullable
@Override
public Fragment createCoverage(@Nullable FragmentActivity fragmentActivity, @Nullable Integer integer) {
this.containerId = integer;
this.activity = fragmentActivity;
showFragment();
return null;
}
@Override
public void onDestroy() {
//若不调用finish, 设置中打开关闭UITouch,会造成och fragment 重叠
if (activity == null) return;
activity.finish();
}
}

View File

@@ -0,0 +1,24 @@
package com.mogo.och.bus;
import androidx.annotation.IdRes;
import androidx.fragment.app.FragmentActivity;
import com.mogo.eagle.core.function.api.base.IMoGoFunctionProvider;
public
/**
* @author congtaowang
* @since 2021/1/15
*
* 网约车抽象接口
*/
interface IMogoOCH extends IMoGoFunctionProvider {
/**
* 初始化网约车容器
*
* @param activity
* @param containerId 容器ID
*/
void createCoverage(FragmentActivity activity, @IdRes int containerId);
}

View File

@@ -0,0 +1,23 @@
package com.mogo.och.bus.bean;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.och.common.module.utils.DateTimeUtil;
/**
* 中止/结束任务请求
*/
public class BusCloseTaskRequest {
private String sn;
private int taskId;
private long writeVersion;
public BusCloseTaskRequest(int taskId) {
this.sn = MoGoAiCloudClientConfig.getInstance().getSn();
this.taskId = taskId;
this.writeVersion = DateTimeUtil.getCurrentTimeStamp();
}
public String getSn() {
return sn;
}
}

View File

@@ -0,0 +1,42 @@
package com.mogo.och.bus.bean;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
public
/**
* @author congtaowang
* @since 2021/3/22
*
* 小巴车运营状态请求参数
*/
class BusOperationStatusRequest {
private String sn;
private double lat;
private double lon;
public BusOperationStatusRequest(double lon, double lat) {
this.sn = MoGoAiCloudClientConfig.getInstance().getSn();
this.lat = lat;
this.lon = lon;
}
public void setLat(double lat) {
this.lat = lat;
}
public void setLon(double lon) {
this.lon = lon;
}
public String getSn() {
return sn;
}
public double getLat() {
return lat;
}
public double getLon() {
return lon;
}
}

View File

@@ -0,0 +1,86 @@
package com.mogo.och.bus.bean;
/**
* @author congtaowang
* @since 2021/3/23
* <p>
* 小巴订单
*/
public class BusOrderBean {
/**
* orderNo number
* passengerPhone string 下单用户电话
* startStationId integer 开始站点
* startStationName string
* endStationId integer 结束站点
* endStationName string
*/
private String orderNo;
private String passengerPhone;
private int startStationId;//乘客上车点
private String startStationName;
private String endStationName;
private int endStationId;//乘客下车点
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public void setPassengerPhone(String passengerPhone) {
this.passengerPhone = passengerPhone;
}
public void setStartStationId(int startStationId) {
this.startStationId = startStationId;
}
public void setStartStationName(String startStationName) {
this.startStationName = startStationName;
}
public void setEndStationName(String endStationName) {
this.endStationName = endStationName;
}
public void setEndStationId(int endStationId) {
this.endStationId = endStationId;
}
public String getOrderNo() {
return orderNo;
}
public String getPassengerPhone() {
return passengerPhone;
}
public int getStartStationId() {
return startStationId;
}
public String getStartStationName() {
return startStationName;
}
public String getEndStationName() {
return endStationName;
}
public int getEndStationId() {
return endStationId;
}
@Override
public String toString() {
return "BusOrderBean{" +
"orderNo=" + orderNo +
", passengerPhone='" + passengerPhone + '\'' +
", startStationId=" + startStationId +
", startStationName='" + startStationName + '\'' +
", endStationName='" + endStationName + '\'' +
", endStationId=" + endStationId +
'}';
}
}

View File

@@ -0,0 +1,23 @@
package com.mogo.och.bus.bean;
import com.mogo.eagle.core.data.BaseData;
import java.util.List;
/**
* @author: wangmingjun
* @date: 2021/10/19
*/
public class BusOrdersResponse extends BaseData {
public Result data;
public static class Result{
public List<BusOrderBean> orders;
}
@Override
public String toString() {
return "BusOrdersResponse{" +
"data=" + data +
'}';
}
}

View File

@@ -0,0 +1,17 @@
package com.mogo.och.bus.bean;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
/**
* @author congtaowang
* @since 2021/3/22
*
* 根据车机行驶线路站点信息
*/
public class BusQueryLineStationsRequest {
private String sn;
public BusQueryLineStationsRequest() {
this.sn = MoGoAiCloudClientConfig.getInstance().getSn();
}
}

View File

@@ -0,0 +1,19 @@
package com.mogo.och.bus.bean;
import com.mogo.eagle.core.data.BaseData;
import java.util.List;
/**
* @author: wangmingjun
* @date: 2022/2/9
*/
public class BusQueryLineTaskResponse extends BaseData {
public List<Result> data;
public static class Result {
public int id;
public long taskStartTime;
}
}

View File

@@ -0,0 +1,27 @@
package com.mogo.och.bus.bean;
import com.mogo.eagle.core.data.BaseData;
import java.util.List;
/**
* @author: wangmingjun
* @date: 2022/2/9
*/
public class BusQueryLinesResponse extends BaseData {
public List<Result> data;
public static class Result {
public int lineId;//线路id
public String name;//线路名字
public int choose; // 1:绑定 2:未被绑定
public String startSiteName;//始发站名称
public String endSiteName;//终点名称
public boolean open;//true 打开状态 false
public boolean haveTask;
public List<BusQueryLineTaskResponse.Result> taskList;
}
}

View File

@@ -0,0 +1,13 @@
package com.mogo.och.bus.bean;
import com.mogo.eagle.core.data.BaseData;
import java.util.List;
/**
* @author: wangmingjun
* @date: 2021/10/19
*/
public class BusQueryWriteOffPassengersResponse extends BaseData {
public List<WriteOffPassenger> data;
}

View File

@@ -0,0 +1,20 @@
package com.mogo.och.bus.bean;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.och.common.module.utils.DateTimeUtil;
/**
* @author: wangmingjun
* @date: 2021/10/18
*/
public class BusResetDrivingLineRequest {
public String sn;
public int taskId; //切换到的线路id
public long writeVersion; //更新时间戳
public BusResetDrivingLineRequest(int taskId) {
this.sn = MoGoAiCloudClientConfig.getInstance().getSn();
this.taskId = taskId;
this.writeVersion = DateTimeUtil.getCurrentTimeStamp();
}
}

View File

@@ -0,0 +1,24 @@
package com.mogo.och.bus.bean;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
public
/**
* @author congtaowang
* @since 2021/3/22
*
* 小巴车重置路线状态请求参数
*/
class BusResetLineStatusRequest {
private String sn;
public BusResetLineStatusRequest() {
this.sn = MoGoAiCloudClientConfig.getInstance().getSn();
}
public String getSn() {
return sn;
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.och.bus.bean;
import java.util.List;
/**
* Created by pangfan on 2021/8/19
*
* 订单状态更新请求数据结构
*/
public class BusRoutePlanningUpdateReqBean {
public String sn;
public int lineId;
public int startSiteId;
public int endSiteId;
public List<Result> points;
public static class Result {
public Double latitude;
public Double longitude;
}
public BusRoutePlanningUpdateReqBean(String sn, int lineId, int startSiteId
, int endSiteId, List<Result> points) {
this.sn = sn;
this.lineId = lineId;
this.startSiteId = startSiteId;
this.endSiteId = endSiteId;
this.points = points;
}
}

View File

@@ -0,0 +1,28 @@
package com.mogo.och.bus.bean;
import com.mogo.eagle.core.data.BaseData;
import com.mogo.och.data.bean.BusRoutesResult;
/**
* 网约车小巴路线接口请求响应结果
*
* @author tongchenfei
*/
public class BusRoutesResponse extends BaseData {
private BusRoutesResult data;
public BusRoutesResult getResult() {
return data;
}
public void setResult(BusRoutesResult data) {
this.data = data;
}
@Override
public String toString() {
return "BusRoutesResponse{" +
"data=" + data +
'}';
}
}

View File

@@ -0,0 +1,27 @@
package com.mogo.och.bus.bean;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.och.common.module.utils.DateTimeUtil;
/**
* @author congtaowang
* @since 2021/3/22
*
* 小巴车运营状态请求参数
*/
public class BusUpdateSiteStatusRequest {
public String sn;
public int taskId;
public int seq;//站点序号
public int siteId;//站点id
public long writeVersion; //更新时间戳
public BusUpdateSiteStatusRequest(int taskId,int siteId,int seq, long writeVersion) {
this.sn = MoGoAiCloudClientConfig.getInstance().getSn();
this.seq = seq;
this.siteId = siteId;
this.taskId = taskId;
this.writeVersion = writeVersion;
}
}

View File

@@ -0,0 +1,24 @@
package com.mogo.och.bus.bean;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
/**
* 查询核销乘客
*/
public class BusWriteOffPassengersQueryRequest {
private String sn;
private int taskId;
private int siteId;
private long verificationTime;
public BusWriteOffPassengersQueryRequest(int taskId, int siteId,long prePassengerTime) {
this.sn = MoGoAiCloudClientConfig.getInstance().getSn();
this.taskId = taskId;
this.siteId = siteId;
this.verificationTime = prePassengerTime;
}
public String getSn() {
return sn;
}
}

View File

@@ -0,0 +1,26 @@
package com.mogo.och.bus.bean;
import com.mogo.och.bus.constant.BusConst;
import java.util.UUID;
/**
* Created on 2021/9/16
*
* 上传车机心跳信息请求数据
*/
public class CarHeartbeatReqBean {
public String sn;
public double lon; //经度
public double lat; //纬度
public String msgId; //心跳信息唯一标识
public int interval; //上报间隔单位秒非必传默认60秒
public CarHeartbeatReqBean(String sn, double lon, double lat) {
this.sn = sn;
this.lon = lon;
this.lat = lat;
this.msgId = UUID.randomUUID().toString();
this.interval = (int) (BusConst.LOOP_PERIOD_60S / 1000);
}
}

View File

@@ -0,0 +1,36 @@
package com.mogo.och.bus.bean;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
/**
* 查询下车乘客请求参数
*
* @author tongchenfei
*/
public class QueryLeaveAwayPassengersRequest {
private String sn;
private int seq; //站点在线路中的序号
private int siteId; //站点id
public QueryLeaveAwayPassengersRequest(int seq, int siteId) {
this.sn = MoGoAiCloudClientConfig.getInstance().getSn();
this.seq = seq;
this.siteId = siteId;
}
public void setSeq(int seq) {
this.seq = seq;
}
public void setSiteId(int siteId) {
this.siteId = siteId;
}
public int getSeq() {
return seq;
}
public int getSiteId() {
return siteId;
}
}

View File

@@ -0,0 +1,60 @@
package com.mogo.och.bus.bean;
import com.mogo.eagle.core.data.BaseData;
import java.util.List;
public
/**
* @author congtaowang
* @since 2021/3/26
*
* 到站查询下车乘客
*/
class QueryLeaveAwayPassengersResponse extends BaseData {
public Result data;
public static class Result {
public List< LeaveAwayPassenger > orders;
}
public static class LeaveAwayPassenger {
/**
* orderNo: 订单no
* orderStatus: 订单状态
* orderType订单类型0及时1预约
* bookingTime预计用车时间
* businessType订单运营类型 9:taxi10:bus
* startSiteId 起点站点id
* userPhone: 乘客联系方式
* startSitePoint开始站点坐标
* startSiteAddr开始地址
* endSiteId结束站点id
* endSitePoint结束站点坐标
* carNumber车牌号
* createTime: 创建时间
* startTime开始时间
* startSiteGcjPoint高精坐标
* endSiteGcjPoint
*/
//todo 目前是需要乘客电话来通知乘客下车 目前后台没有乘客信息userPhone
public String orderNo;
public int orderStatus;
public int orderType;
public long bookingTime;
public int businessType;
public int startSiteId;
public String passengerPhone;
public List<Double> startSitePoint;
public String startSiteAddr;
public int endSiteId;
public List<Double> endSitePoint;
public String carNumber;
public long createTime;
public long startTime;
public List< Double > startSiteGcjPoint;
public List< Double > endSiteGcjPoint;
}
}

View File

@@ -0,0 +1,14 @@
package com.mogo.och.bus.bean;
import java.io.Serializable;
/**
* @author: wangmingjun
* @date: 2022/9/23
*/
public class WriteOffPassenger implements Serializable {
public String phone;
public String orderNo;
public int passengerSize;
public long writeOffTime;
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.bus.callback;
/**
* Created on 2021/9/8
*
* Model->Presenter回调ADAS相关自动驾驶状态回调到达终点等等
*/
public interface IBusADASStatusCallback {
//自驾返回失败
void onStartAdasFailure();
}

View File

@@ -0,0 +1,17 @@
package com.mogo.och.bus.callback;
import com.mogo.eagle.core.data.map.MogoLocation;
/**
* Created on 2021/9/10
*
* Model->Presenter回调状态控制器监听accOn、adas ui show、voice ui show、push ui show、v2x ui show等等
*/
public interface IBusControllerStatusCallback {
// 是否vr map模式
void onVRModeChanged(boolean isVRMode);
// 自车定位
void onCarLocationChanged(MogoLocation location);
//开始开启自动驾驶
void startOpenAutopilot();
}

View File

@@ -0,0 +1,14 @@
package com.mogo.och.bus.callback;
import com.mogo.och.bus.bean.BusQueryLineTaskResponse;
import com.mogo.och.bus.bean.BusQueryLinesResponse;
/**
* @author: wangmingjun
* @date: 2022/2/9
*/
public interface IBusLinesCallback {
void onBusLinesChange(BusQueryLinesResponse lines);
void onChangeLineIdSuccess();
void onBusLineTasks(BusQueryLineTaskResponse o, int position,boolean autoRefresh);
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.bus.callback;
import com.mogo.och.bus.bean.WriteOffPassenger;
/**
* @author: wangmingjun
* @date: 2022/9/26
*/
public interface IPassengerCallback {
void playPassenger(WriteOffPassenger passenger);
}

View File

@@ -0,0 +1,23 @@
package com.mogo.och.bus.callback;
import com.mogo.och.data.bean.BusStationBean;
import java.util.List;
/**
* @author: wangmingjun
* @date: 2021/10/22
*/
public interface IRefreshBusStationsCallback {
void updateBusTaskStatus(String lineName,String lintTime,
List<BusStationBean> stationList,
int arrivingOrArrivedIndex,
boolean isArrived);
/**
* 结束清理一遍、选择任务后清理一遍
*/
void clearBusStationsMarkers();
void updateEmptyUi();
}

View File

@@ -0,0 +1,9 @@
package com.mogo.och.bus.callback;
/**
* @author: wangmingjun
* @date: 2021/10/22
*/
public interface ISlidePannelHideCallback {
void hideSlidePanel();
}

View File

@@ -0,0 +1,77 @@
package com.mogo.och.bus.constant
import com.mogo.commons.debug.DebugConfig
/**
* Created on 2021/12/6
*/
class BusConst {
companion object {
// OCH arouter 路由path
const val PATH = "/driver/api"
// 测试用的广播
const val BROADCAST_TEST_BUS_CONTROL_TYPE_EXTRA_KEY = "sceneType"
// 无状态
const val STATION_STATUS_IDLE = 0
// 已过站(历史站)
const val STATION_STATUS_LEAVING = 1
// 到站(当前站)
const val STATION_STATUS_STOPPED = 2
// 未到站(未到站)
const val STATION_STATUS_ARRIVING = 3
// 上报心跳轮询ms
const val LOOP_PERIOD_60S = 60 * 1000L
// 开始服务启动自动驾驶等待时间(埋点上传)
const val LOOP_PERIOD_15S = 15 * 1000L
const val LOOP_PERIOD_1S = 1 * 1000L
const val LOOP_DELAY = 100L
// 下发给MEC轨迹信息间隔时间 10秒
const val LOOP_PERIOD_10S = 10 * 1000L
// 尝试下发给MEC轨迹最多10次
const val LOOP_SEND_TRAJ_TIMES = 10
//起点UUID
const val BUS_START_MAP_MAKER = "bus_start_map_maker";
//终点UUID
const val BUS_END_MAP_MAKER = "bus_end_map_maker";
// 埋点key接管后点击'自动驾驶'按钮启动
const val EVENT_KEY_RESTART_AUTOPILOT = "event_key_och_bus_restart_autopilot"
// 埋点key开始服务开启自动驾驶成功/失败)
const val EVENT_KEY_START_SERVICE = "event_key_och_bus_start_service"
const val EVENT_PARAM_SN = "sn"
const val EVENT_PARAM_TIME = "time"
const val EVENT_PARAM_START_NAME = "start_name"
const val EVENT_PARAM_END_NAME = "end_name"
const val EVENT_PARAM_LINE_ID = "line_id"
const val EVENT_PARAM_START_RESULT = "start_autopilot" // true/false
const val EVENT_PARAM_START_FAILURE_CODE = "start_autopilot_failure_code" // 启动自驾失败code
const val EVENT_PARAM_START_FAILURE_MSG = "start_autopilot_failure_msg" // 启动自驾失败原因
const val EVENT_PARAM_PLATE_NUM = "plate_number" // 车牌号
const val EVENT_PARAM_ENV_ONLINE = "env_online" // 是否线上环境true/false
// 埋点key开启自动驾驶前已识别的异常会导致无法开启自驾
const val EVENT_KEY_AP_UNABLE_START_REASON = "event_key_och_bus_ap_unable_start_reason"
const val EVENT_PARAM_UNABLE_START_REASON = "unable_start_reason";
/**
* 订单起终点Marker类型
*/
const val TYPE_MARKER_BUS_ORDER = "TYPE_MARKER_BUS_ORDER"
const val TIMER_START_AUTOPILOT_INTERVAL = 20 * 1000L
//围栏到站 暂定10米
const val ARRIVE_AT_END_STATION_DISTANCE = 10
// 轮询
const val LOOP_PASSENGER_5S = 5 * 1000L
const val LOOP_PASSENGER_2S = 2 * 1000L
const val LOOP_PASSENGER_1S = 1 * 1000L
const val LOOP_DELAY_500 = 500L
const val DELAY_10S = 10 * 1000L
}
}

View File

@@ -0,0 +1,637 @@
package com.mogo.och.bus.fragment;
import static com.mogo.och.bus.constant.BusConst.TIMER_START_AUTOPILOT_INTERVAL;
import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import com.mogo.commons.mvp.IView;
import com.mogo.commons.mvp.MvpFragment;
import com.mogo.commons.mvp.Presenter;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotRecordListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotRecordListenerManager;
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager;
import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxBubbleView;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxButtonView;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxListView;
import com.mogo.eagle.core.function.hmi.ui.widget.TrafficDataView;
import com.mogo.eagle.core.function.smp.view.SmallMapView;
import com.mogo.eagle.core.function.view.MapBizView;
import com.mogo.eagle.core.utilcode.mogo.view.OnPreventFastClickListener;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.map.listener.IMogoMapListener;
import com.mogo.map.listener.MogoMapListenerHandler;
import com.mogo.map.uicontroller.IMogoMapUIController;
import com.mogo.map.uicontroller.VisualAngleMode;
import com.mogo.och.bus.R;
import com.mogo.och.data.bean.BusRoutesResult;
import com.mogo.och.bus.model.OrderModel;
import com.mogo.och.bus.util.BDRouteDataTestUtils;
import com.mogo.och.bus.view.SlidePanelView;
import com.mogo.och.common.module.utils.SoundPoolHelper;
import org.greenrobot.eventbus.EventBus;
import java.util.Objects;
import bag_manager.BagManagerOuterClass;
import mogo.telematics.pad.MessagePad;
import record_cache.RecordPanelOuterClass;
/**
* 网约车基础Fragment主要负责布局通用界面处理站点面板和通话面板互斥情况
* <p>
* 部分业务放在了此处处理
*
* @author tongchenfei
*/
public abstract class BaseBusTabFragment<V extends IView, P extends Presenter<V>> extends MvpFragment<V, P> implements IMogoMapListener, IMoGoAutopilotRecordListener {
private static final String TAG = "BaseBusTabFragment";
protected SlidePanelView slidePanelView;
private RelativeLayout ctvAutopilotStatus;
private ImageView ctvAutopilotStatusIv;
private TextView ctvAutopilotStatusTv;
protected TextView tvArrived;
protected RelativeLayout mSettingBtn;
protected RelativeLayout mBadcaseBtn;
protected RelativeLayout mAICollectBtn;
private FrameLayout flStationPanelContainer;
private MapBizView mapBizView;
private Group groupTestPanel;
private TrafficDataView mTrafficDataView;
// private BusTrafficLightView mTrafficLightView;
//远景和中景的切换
private ImageView mSwitchMapModeImage;
private LinearLayout mSwitchMapModeLayout;
protected SmallMapView smallMapView;
//消息盒子
private DriverMsgBoxButtonView viewDriverMsgBoxButton;
private DriverMsgBoxListView viewDriverMsgBoxList;
private DriverMsgBoxBubbleView viewDriverMsgBoxBubble;
private ObjectAnimator autopilotLoadingAnimator;
public boolean isAnimateRunning = false;
/**
* 滑动按钮触发的事件
*/
private final SlidePanelView.OnSlidePanelMoveToEndListener onSlideToEndListener = () -> {
// 此处做一个代理,处理一下共有情况
if (getSlidePanelOnEndListener() != null) {
getSlidePanelOnEndListener().moveToEnd();
}
};
@Override
protected int getLayoutId() {
return R.layout.bus_base_fragment;
}
@Override
protected void initViews() {
mapBizView = findViewById(R.id.mapBizView);
groupTestPanel = findViewById(R.id.groupTestPanel);
slidePanelView = findViewById(R.id.module_mogo_och_slide_panel);
ctvAutopilotStatus = findViewById(R.id.module_mogo_och_autopilot_status);
ctvAutopilotStatusIv = findViewById(R.id.bus_autopilot_btn_iv);
ctvAutopilotStatusTv = findViewById(R.id.bus_autopolot_btn_tv);
flStationPanelContainer = findViewById(R.id.module_mogo_och_station_panel_container);
// mTrafficLightView = findViewById(R.id.bus_traffic_light_view);
// CallerHmiManager.INSTANCE.setProxyTrafficLightView(mTrafficLightView);
// tvOperationStatus = findViewById(R.id.module_mogo_och_operation_status);
tvArrived = findViewById(R.id.module_mogo_och_arrived_tv);
mTrafficDataView = (TrafficDataView) findViewById(R.id.bus_arc);
LayoutInflater.from(getContext()).inflate(getStationPanelViewId(), flStationPanelContainer);
slidePanelView.setOnSlidePanelMoveToEndListener(onSlideToEndListener);
mSwitchMapModeLayout = findViewById(R.id.bus_switch_model_layout);
mSwitchMapModeImage = findViewById(R.id.bus_switch_model_icon);
updateSwitchMapIcon();
mSwitchMapModeLayout.setOnClickListener(new OnPreventFastClickListener() {
@Override
public void onClickImpl(View v) {
IMogoMapUIController controller = CallerMapUIServiceManager.INSTANCE.getMapUIController();
if (controller != null) {
//切换地图的远近视图
if (controller.getCurrentMapVisualAngle().isLongSight()) {
//Objects.requireNonNull(CallerMapUIServiceManager.INSTANCE.getMapUIController()).setLockMode(true);
controller.changeMapVisualAngle(VisualAngleMode.MODE_MEDIUM_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.bus_switch_map_medium);
} else if (controller.getCurrentMapVisualAngle().isMediumSight()) {
//Objects.requireNonNull(CallerMapUIServiceManager.INSTANCE.getMapUIController()).setLockMode(false);
controller.changeMapVisualAngle(VisualAngleMode.MODE_LONG_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.bus_switch_map_long);
} else {
controller.changeMapVisualAngle(VisualAngleMode.MODE_MEDIUM_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.bus_switch_map_medium);
}
}
}
});
initListener();
setAutopilotBtnStatus(CallerAutoPilotStatusListenerManager.INSTANCE.getState());
ctvAutopilotStatus.setOnClickListener(new OnPreventFastClickListener() {
@Override
public void onClickImpl(View v) {
restartAutopilot();
}
});
// 模拟 不可自动驾驶目前场景是刚开机adas还未和工控机连接
findViewById(R.id.btnAutopilotDisable).setOnClickListener(view ->
debugAutoPilotStatus(IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE)
);
// 模拟 可自动驾驶,工控机连接正常,且处于人工干预状态
findViewById(R.id.btnAutopilotEnable).setOnClickListener(view ->
debugAutoPilotStatus(IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE)
);
// 模拟 自动驾驶能力,自动驾驶中,可能是停车,可能是行进,但是是机器在处理车的前进后退,不是人
findViewById(R.id.btnAutopilotRunning).setOnClickListener(view ->
debugAutoPilotStatus(IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING)
);
findViewById(R.id.btnAutopilotPingxing).setOnClickListener(view ->
debugAutoPilotStatus(IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING)
);
// 模拟 自动驾驶网约车回调数据
findViewById(R.id.btnAutopilotArrive).setOnClickListener(view ->
debugArrivedStation()
);
findViewById(R.id.btnAutopilotRoute).setOnClickListener(view -> debugArrivedRoute());
tvArrived.setOnClickListener(view -> {
onArriveStation();
});
mSettingBtn = findViewById(R.id.module_mogo_och_setting_layout);
mSettingBtn.setOnClickListener(v -> {
// TODO: 2021/12/9
CallerHmiManager.INSTANCE.showToolsView();
});
// mBadcaseBtn的visible显示逻辑在showBadcaseEntrance内处理
mBadcaseBtn = findViewById(R.id.module_mogo_och_badcase_rl);
if (mBadcaseBtn != null) {
CallerDevaToolsManager.INSTANCE.initBadCase(mBadcaseBtn);
CallerAutopilotRecordListenerManager.INSTANCE.addListener(TAG, this);
}
mAICollectBtn = findViewById(R.id.module_mogo_och_ai_collet_rl);
if (mAICollectBtn != null) {
CallerDevaToolsManager.INSTANCE.initAiCollect(mAICollectBtn);
}
//消息盒子
viewDriverMsgBoxButton = findViewById(R.id.viewDriverMsgBoxButton);
viewDriverMsgBoxList = findViewById(R.id.viewDriverMsgBoxList);
viewDriverMsgBoxBubble = findViewById(R.id.viewDriverMsgBoxBubble);
viewDriverMsgBoxButton.setClickListener(show -> {
if(show){
viewDriverMsgBoxList.setVisibility(View.VISIBLE);
viewDriverMsgBoxList.notifyData();
viewDriverMsgBoxBubble.setVisibility(View.GONE);
viewDriverMsgBoxBubble.isShowData(false);
}else{
viewDriverMsgBoxList.setVisibility(View.GONE);
viewDriverMsgBoxBubble.setVisibility(View.VISIBLE);
viewDriverMsgBoxBubble.isShowData(true);
}
});
smallMapView = findViewById(R.id.smallMapView);
}
@Override
protected void initViews(Bundle savedInstanceState) {
super.initViews(savedInstanceState);
mapBizView.onCreate(savedInstanceState);
smallMapView.onCreateView(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
mapBizView.onResume();
smallMapView.onResume();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container
, @Nullable Bundle savedInstanceState) {
EventBus.getDefault().register(this);
return super.onCreateView(inflater, container, savedInstanceState);
}
protected abstract void onArriveStation();
private void updateSwitchMapIcon() {
IMogoMapUIController controller = CallerMapUIServiceManager.INSTANCE.getMapUIController();
if (controller != null) {
if (controller.getCurrentMapVisualAngle().isLongSight()) {
mSwitchMapModeImage.setImageResource(R.drawable.bus_switch_map_long);
} else if (controller.getCurrentMapVisualAngle().isMediumSight()) {
mSwitchMapModeImage.setImageResource(R.drawable.bus_switch_map_medium);
} else {
mSwitchMapModeImage.setImageResource(R.drawable.bus_switch_map_medium);
}
}
}
private void debugArrivedRoute() {
BDRouteDataTestUtils.converToRouteData();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
mapBizView.onSaveInstanceState(outState);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapBizView.onLowMemory();
}
@Override
public void onPause() {
super.onPause();
mapBizView.onPause();
smallMapView.onPause();
}
@Override
public void onDestroyView() {
mapBizView.onDestroy();
super.onDestroyView();
CallerAutopilotRecordListenerManager.INSTANCE.removeListener(TAG);
EventBus.getDefault().unregister(this);
}
@Override
public void onAutopilotRecordResult(@Nullable RecordPanelOuterClass.RecordPanel recordPanel) {
}
@Override
public void onAutopilotRecordConfig(@NonNull MessagePad.RecordDataConfig config) {
}
@Override
public void onBagManagerResult(@NonNull BagManagerOuterClass.BagManager bagManager) {
}
/**
* 测试到站
*/
protected abstract void debugArrivedStation();
private void initListener() {
MogoMapListenerHandler.Companion.getMogoMapListenerHandler().registerHostMapListener(TAG, this);
}
/**
* 展示滑动按钮
*
* @param text 指定的文字
*/
public void showSlidePanel(String text) {
getActivity().runOnUiThread(() -> {
slidePanelView.setText(text);
slidePanelView.setVisibility(View.VISIBLE);
});
setArrivedClikable(false);
}
/**
* 设置进站按钮状态
*
* @param isClickable
*/
public void setArrivedClikable(boolean isClickable) {
getActivity().runOnUiThread(() -> {
tvArrived.setEnabled(isClickable);
if (isClickable) {
tvArrived.setTextColor(getResources().getColor(R.color.bus_white));
} else {
tvArrived.setTextColor(getResources().getColor(R.color.bus_arrived_btn_un_clickable_color));
}
});
}
/**
* 隐藏滑动按钮
*/
public void hideSlidePanel() {
getActivity().runOnUiThread(() -> {
slidePanelView.setVisibility(View.GONE);
});
}
public void playDI() {
SoundPoolHelper.getSoundPoolHelper().playSoundWithRedId(getContext(), R.raw.bus_di);
}
/**
* 改变自动驾驶状态
*
* @param autopilotStatus 0:不可用 1:可用状态 2:自动驾驶中
*/
public void onAutopilotStatusChanged(int autopilotStatus) {
getActivity().runOnUiThread(() -> {
changeAutopilotBtnView(autopilotStatus, isAnimateRunning);
});
}
public void setAutopilotBtnStatus(int autopilotStatus) {
if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE
== autopilotStatus) {//0不可用
ctvAutopilotStatusTv.setTextColor(getResources().getColor(R.color.bus_autopilot_text_color_disable));
ctvAutopilotStatusTv.setText(getResources().getString(R.string.bus_loading_autopilot_runnig_tv));
ctvAutopilotStatusIv.setImageResource(R.drawable.bus_disable_autopilot_icon);
// ctvAutopilotStatus.setSelected(false);
ctvAutopilotStatus.setClickable(true);
ctvAutopilotStatus.setBackground(getContext().getDrawable(R.drawable.bus_autopilot_0_1_status_bg));
} else {
ctvAutopilotStatusTv.setTextColor(getResources().getColor(R.color.bus_autopilot_text_color_normal));
ctvAutopilotStatusIv.setImageResource(R.drawable.bus_ic_autopilot);
if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE == autopilotStatus) { //1可用
// ctvAutopilotStatus.setSelected(false);
ctvAutopilotStatus.setClickable(true);
ctvAutopilotStatusTv.setText(getResources().getString(R.string.bus_loading_autopilot_runnig_tv));
ctvAutopilotStatus.setBackground(getContext().getDrawable(R.drawable.bus_autopilot_0_1_status_bg));
} else if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING == autopilotStatus) {
ctvAutopilotStatusTv.setText(R.string.bus_loading_autopilot_runnig_tv);
ctvAutopilotStatus.setClickable(true);
ctvAutopilotStatus.setBackground(getContext().getDrawable(R.drawable.bus_autopilot_2_status_bg));
// ctvAutopilotStatus.setSelected(true);
} else if (IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING
== autopilotStatus){
ctvAutopilotStatusTv.setText(R.string.bus_pingxing_driver);
ctvAutopilotStatus.setClickable(false);
ctvAutopilotStatus.setBackground(getContext().getDrawable(R.drawable.pingxingjiashi));
}
}
}
public void updateAutopilotStatus(int autopilotStatus) {
if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING
== autopilotStatus) {//2 running
ctvAutopilotStatusIv.setImageResource(R.drawable.bus_right_autopilot_icon);
ctvAutopilotStatusTv.setTextColor(getResources().getColor(R.color.bus_autopilot_text_color_normal));
ctvAutopilotStatusTv.setText(getResources().getString(R.string.bus_loading_autopilot_success_tv));
// ctvAutopilotStatus.setSelected(false);
ctvAutopilotStatus.setClickable(false);
} else {
ctvAutopilotStatusIv.setImageResource(R.drawable.bus_wrong_autopilot_icon);
ctvAutopilotStatusTv.setTextColor(getResources().getColor(R.color.bus_autopilot_text_color_normal));
ctvAutopilotStatusTv.setText(getResources().getString(R.string.bus_loading_autopilot_failure_tv));
ctvAutopilotStatus.setClickable(false);
// ctvAutopilotStatus.setSelected(false);
}
UiThreadHandler.postDelayed(new Runnable() {
@Override
public void run() {
setAutopilotBtnStatus(autopilotStatus);
}
}, 1000);
}
private void changeAutopilotBtnView(int autopilotStatus, boolean isAnimateRunning) {
if (isAnimateRunning && IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING
!= autopilotStatus) {
// 主动开启自动驾驶中不为2为0、1则继续loading
return;
}
if (isAnimateRunning) {
stopAnimAndUpdateBtnStatus();
} else {
setAutopilotBtnStatus(autopilotStatus);
}
}
public void stopAnimAndUpdateBtnStatus() {
stopAutopilotAnimation();
updateAutopilotStatus(CallerAutoPilotStatusListenerManager.INSTANCE.getState());
}
/**
* 隐藏【自动驾驶】按钮
*/
public void hideAutopilotBiz() {
getActivity().runOnUiThread(() -> {
// ctvAutopilotStatus.setVisibility(View.GONE);
// slidePanelView.setVisibility(View.GONE);
});
}
/**
* 展示【自动驾驶】按钮
*/
public void showAutopilotBiz() {
// getActivity().runOnUiThread(() -> {
// ctvAutopilotStatus.setVisibility(View.VISIBLE);
// });
}
public SlidePanelView.OnSlidePanelMoveToEndListener getSlidePanelOnEndListener() {
return null;
}
/**
* 获取站点面板view在{@link #initViews()}时候添加到container中
*
* @return 站点面板view
*/
public abstract int getStationPanelViewId();
/**
* 重新开启自动驾驶
*/
public abstract void restartAutopilot();
/**
* 模拟自动驾驶返回状态
*
* @param status
*/
public abstract void debugAutoPilotStatus(int status);
/**
* 开启自动驾驶中间动画
*/
public void startAutopilotAnimation() {
isAnimateRunning = true;
ctvAutopilotStatusTv.setText(getResources().getString(R.string.bus_loading_autopilot_tv));
ctvAutopilotStatusTv.setTextColor(getResources().getColor(R.color.bus_autopilot_text_color_normal));
// ctvAutopilotStatus.setSelected(false);
ctvAutopilotStatus.setClickable(true);
ctvAutopilotStatusIv.setImageResource(R.drawable.bus_loading_autopilot_icon);
if (autopilotLoadingAnimator == null) {
autopilotLoadingAnimator = ObjectAnimator.ofFloat(ctvAutopilotStatusIv, "rotation", 0f, 360f);
autopilotLoadingAnimator.setInterpolator(new LinearInterpolator());
autopilotLoadingAnimator.setRepeatCount(-1);//无限循环
autopilotLoadingAnimator.setDuration(1000);//设置持续时间
}
autopilotLoadingAnimator.start();//动画开始
startingAutoApilotCountDown();
}
private void startingAutoApilotCountDown() {
//10s 若自动驾驶没有开启,则结束动画
UiThreadHandler.postDelayed(new Runnable() {
@Override
public void run() { //未启动成功做处理
if (isAnimateRunning) {// 只判断动画是否在进行,根据自动驾驶当前状态去设置自动驾驶状态
stopAutopilotAnimation();
updateAutopilotStatus(CallerAutoPilotStatusListenerManager.INSTANCE.getState());
}
}
}, TIMER_START_AUTOPILOT_INTERVAL);
}
/**
* 停止自动驾驶中间动画
*/
protected void stopAutopilotAnimation() {
if (autopilotLoadingAnimator != null) {
autopilotLoadingAnimator.end();
ctvAutopilotStatusIv.clearAnimation();
autopilotLoadingAnimator = null;
isAnimateRunning = false;
}
}
/**
* 迈速表实时更新
*
* @param newSpeed
*/
public void updateSpeedView(float newSpeed) {
int speed = (int) (Math.abs(newSpeed) * 3.6F); // 倒车时工控机反馈定位信息中speed为负值
if (mTrafficDataView != null) {
mTrafficDataView.updateSpeedWithValue(speed);
}
}
@Override
public void onDestroy() {
super.onDestroy();
smallMapView.onDestroy();
MogoMapListenerHandler.Companion.getMogoMapListenerHandler().unregisterHostMapListener(TAG);
}
@Override
public void onMapVisualAngleChanged(VisualAngleMode visualAngleMode) {
if (visualAngleMode.isMediumSight()) {
mSwitchMapModeLayout.setVisibility(View.VISIBLE);
} else if (visualAngleMode.isLongSight()) {
mSwitchMapModeLayout.setVisibility(View.VISIBLE);
} else if (visualAngleMode.isCloseSight()) {
mSwitchMapModeLayout.setVisibility(View.GONE);
}
}
/**
* bus调试面板打开关闭
*/
public void debugTestBar() {
if (groupTestPanel.getVisibility() == View.VISIBLE) {
groupTestPanel.setVisibility(View.GONE);
} else {
groupTestPanel.setVisibility(View.VISIBLE);
}
}
/**
* Bus调试信息线路、轨迹等信息
* <p>
* START
*/
private View busTestBar;
private TextView lineIdTV;
private TextView trajMd5TV;
private TextView stopMd5TV;
private TextView trajMd5DPQPTV;
private TextView stopMd5DPQPTV;
public void showHideTestBar() {
if (busTestBar == null) {
busTestBar = findViewById(R.id.module_mogo_och_bus_test_bar);
lineIdTV = findViewById(R.id.bus_test_bar_current_line_id);
trajMd5TV = findViewById(R.id.bus_test_bar_current_traj_md5);
stopMd5TV = findViewById(R.id.bus_test_bar_current_stop_md5);
trajMd5DPQPTV = findViewById(R.id.bus_test_bar_current_traj_md5_dpqp);
stopMd5DPQPTV = findViewById(R.id.bus_test_bar_current_stop_md5_dpqp);
}
if (busTestBar.getVisibility() == View.VISIBLE) {
busTestBar.setVisibility(View.GONE);
} else {
BusRoutesResult routesResult = OrderModel.getInstance().getBusRoutesResult();
lineIdTV.setText("lineId:" + (routesResult == null ? "" : String.valueOf(routesResult.getLineId())));
trajMd5TV.setText("TMd5:" + (routesResult == null ? "" : routesResult.csvFileMd5));
stopMd5TV.setText("SMd5:" + (routesResult == null ? "" : routesResult.txtFileMd5));
trajMd5DPQPTV.setText("TMd5DPQP:" + (routesResult == null ? "" : routesResult.csvFileMd5DPQP));
stopMd5DPQPTV.setText("SMd5DPQP:" + (routesResult == null ? "" : routesResult.txtFileMd5DPQP));
busTestBar.setVisibility(View.VISIBLE);
}
}
public void updateBusTestBarInfo() {
if (busTestBar != null && busTestBar.getVisibility() == View.VISIBLE) {
BusRoutesResult routesResult = OrderModel.getInstance().getBusRoutesResult();
lineIdTV.setText("lineId:" + (routesResult == null ? "" : String.valueOf(routesResult.getLineId())));
trajMd5TV.setText("TMd5:" + (routesResult == null ? "" : routesResult.csvFileMd5));
stopMd5TV.setText("SMd5:" + (routesResult == null ? "" : routesResult.txtFileMd5));
trajMd5DPQPTV.setText("TMd5DPQP:" + (routesResult == null ? "" : routesResult.csvFileMd5DPQP));
stopMd5DPQPTV.setText("SMd5DPQP:" + (routesResult == null ? "" : routesResult.txtFileMd5DPQP));
}
}
/**
* END
*/
}

View File

@@ -0,0 +1,569 @@
package com.mogo.och.bus.fragment;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Group;
import androidx.annotation.Nullable;
import com.alibaba.android.arouter.launcher.ARouter;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.map.CenterLine;
import com.mogo.eagle.core.data.temp.EventLogout;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant;
import com.mogo.eagle.core.utilcode.util.ToastUtils;
import com.mogo.map.overlay.IMoGoOverlayManager;
import com.mogo.map.overlay.core.Level;
import com.mogo.map.overlay.point.Point;
import com.mogo.map.uicontroller.IMogoMapUIController;
import com.mogo.och.bus.R;
import com.mogo.och.data.bean.BusStationBean;
import com.mogo.och.bus.constant.BusConst;
import com.mogo.och.bus.net.login.LoginBusImpl;
import com.mogo.och.bus.presenter.BusPresenter;
import com.mogo.och.bus.ui.BusStationCommonItem;
import com.mogo.och.bus.ui.BusSwitchLineActivity;
import com.mogo.och.bus.view.SlidePanelView;
import com.mogo.och.common.module.biz.constant.OchCommonConst;
import com.mogo.och.common.module.biz.provider.LoginService;
import com.mogo.och.common.module.utils.BlinkAnimationUtil;
import com.mogo.och.common.module.utils.OCHThreadPoolManager;
import com.mogo.och.common.module.utils.QRUtilsKt;
import com.mogo.och.common.module.wigets.BindQRCodeDialog;
import com.mogo.och.common.module.wigets.MarqueeTextView;
import com.mogo.och.common.module.wigets.OCHCommitDialog;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
import java.util.Objects;
import me.jessyan.autosize.utils.AutoSizeUtils;
/**
* 网约车小巴界面
*
* @author tongchenfei
*/
public class BusFragment extends BaseBusTabFragment<BusFragment, BusPresenter>
implements SlidePanelView.OnSlidePanelMoveToEndListener, View.OnClickListener {
public static final String TAG = "BusFragment";
private TextView mSwitchLine; //切换路线
private MarqueeTextView mLineName;
private TextView mTaskTime;
private Group groupStationsPanel;
private ConstraintLayout noDataView;
private BusStationBean startStation = null;
private BusStationBean endStation = null;
private BusStationCommonItem firstStationItem;
private BusStationCommonItem secondStationItem;
private BusStationCommonItem thirdStationItem;
private LoginService loginService;
@Override
public String getTagName() {
return "BusFragment";
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
loginService = (LoginService) ARouter.getInstance().build(OchCommonConst.LOGINSERVICE).navigation();
if(loginService!=null){
loginService.registerFragment(this, getPresenter(),new LoginBusImpl());
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
loginService.unRegisterFragment();
loginService = null;
}
@Override
protected void initViews() {
super.initViews();
mSwitchLine = findViewById(R.id.switch_line_btn);
mSwitchLine.setTag(0);
mLineName = findViewById(R.id.module_och_bus_line_name);
firstStationItem = findViewById(R.id.bus_panel_first_station);
secondStationItem = findViewById(R.id.bus_panel_second_station);
thirdStationItem = findViewById(R.id.bus_panel_third_station);
mTaskTime = findViewById(R.id.bus_task_time_tv);
groupStationsPanel = findViewById(R.id.group_stations_panel);
noDataView = findViewById(R.id.no_line_data_view);
CallerLogger.INSTANCE.d(M_BUS + TAG, "initView: " + CallerAutoPilotStatusListenerManager.INSTANCE.getState());
// 初始化的时候设置 UI 按钮状态
showAutopilotBiz();
mSwitchLine.setOnClickListener(this);
mLineName.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
showHideTestBar();
return false;
}
});
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void changeOverview(EventLogout eventLogout){
if (eventLogout.getMessgae() == EventLogout.LOGOUT_TYPE){
CallerLogger.INSTANCE.d(M_BUS + TAG,"changeOverview Event消息去登出");
mPresenter.logout();
}else if (eventLogout.getMessgae() == EventLogout.SHOW_QR_TYPE){ //显示二维码
CallerLogger.INSTANCE.d(M_BUS + TAG,"changeOverview Event qrcodesn = "
+ MoGoAiCloudClientConfig.getInstance().getSn());
String qrUrl = String.format(FunctionBuildConfig.urlJson.getBindDriverQRUrl(),
MoGoAiCloudClientConfig.getInstance().getSn());
Bitmap bmQr = QRUtilsKt.createQRCodeWithPicture(
BitmapFactory.decodeResource(getResources(), R.drawable.icon_qr_center_logo)
,qrUrl, AutoSizeUtils.dp2px(getContext(),340f),
AutoSizeUtils.dp2px(getContext(),340f),true);
if (bmQr != null){
BindQRCodeDialog.Builder builder = new BindQRCodeDialog.Builder();
builder.title(getString(R.string.bind_driver_qr_title))
.cancelStr(getString(R.string.qr_cancel))
.qrBm(bmQr).build(getContext()).show();
}else {
CallerLogger.INSTANCE.d(M_BUS + TAG,"bmQr = null ");
}
}
}
@Override
protected void onArriveStation() {
mPresenter.onAutopilotArriveAtStation(null);
}
@Override
protected void debugArrivedStation() {
mPresenter.onAutopilotArriveAtStation(null);
}
@NonNull
@Override
protected BusPresenter createPresenter() {
return new BusPresenter(this);
}
@Override
public void onResume() {
super.onResume();
}
public void hideStationsPanel(){
groupStationsPanel.setVisibility(View.GONE);
noDataView.setVisibility(View.VISIBLE);
}
public void showStationsPanel(){
groupStationsPanel.setVisibility(View.VISIBLE);
noDataView.setVisibility(View.GONE);
}
public void updateLineEmptyUI(){
setArrivedClikable(false);
showOrHideSwitchLineBtn(true);
hideStationsPanel();
hideSlidePanel();
resetStationBlinkAnim();
}
private void resetStationBlinkAnim() {
BlinkAnimationUtil.clearAnimation(firstStationItem.getCircleImageView());
BlinkAnimationUtil.clearAnimation(secondStationItem.getCircleImageView());
BlinkAnimationUtil.clearAnimation(thirdStationItem.getCircleImageView());
}
public void updateBusTaskStatus(String lineName, String lineTime,
List<BusStationBean> stationList,
int arrivingOrArrivedIndex,
boolean isArrived){
if (getActivity() == null) {
return;
}
getActivity().runOnUiThread(() -> {
if (stationList == null) {
// 获取小巴数据失败
return;
}
showStationsPanel();
showOrHideSwitchLineBtn(false);
mLineName.setText(lineName);
mTaskTime.setText(getString(R.string.bus_line_time_tag)+ lineTime);
// 渲染小巴路线数据
updateBusStationStatus(stationList,arrivingOrArrivedIndex,isArrived);
});
}
private void updateBusStationStatus(List<BusStationBean> stationList,
int arrivingOrArrivedIndex,
boolean isArrived) {
startStation = stationList.get(0);
endStation = stationList.get(stationList.size() - 1);
if (arrivingOrArrivedIndex == stationList.size() - 1 && isArrived){
//切换路线和结束路线按钮切换
showSlidePanel("单程结束");
setOrRemoveMapMaker(false, BusConst.BUS_END_MAP_MAKER, endStation.getLat()
, endStation.getLon(),R.raw.end_marker);
}else if (arrivingOrArrivedIndex == 0 && isArrived){
showSlidePanel("滑动出发");
setOrRemoveMapMaker(true, BusConst.BUS_START_MAP_MAKER,
startStation.getLat(), startStation.getLon(),R.raw.star_marker);
setOrRemoveMapMaker(true, BusConst.BUS_END_MAP_MAKER,
endStation.getLat(), endStation.getLon(),R.raw.end_marker);
}else{
if (isArrived){
// 重置滑动按钮文字
showSlidePanel("滑动出发");
}
setOrRemoveMapMaker(false, BusConst.BUS_START_MAP_MAKER, startStation.getLat()
, startStation.getLon(),R.raw.star_marker);
setOrRemoveMapMaker(true, BusConst.BUS_END_MAP_MAKER, endStation.getLat()
, endStation.getLon(),R.raw.end_marker);
}
if (stationList.size() > 2){ //只有两个站点
updateMoreThanTwoStationsUI(stationList,arrivingOrArrivedIndex,isArrived);
}else {
updateTwoStationsUI(stationList,arrivingOrArrivedIndex,isArrived);
}
updateBusTestBarInfo();
}
/**
* 有两个以上站点的路线
* @param stationList
* @param arrivingOrArrivedIndex
* @param isArrived
*/
private void updateMoreThanTwoStationsUI(List<BusStationBean> stationList,
int arrivingOrArrivedIndex,
boolean isArrived) {
secondStationItem.setStationTag("");
secondStationItem.showOrHideStationArrowBg(true);
thirdStationItem.setStationTag("");
secondStationItem.setVisibility(View.VISIBLE);
thirdStationItem.showOrHideStationArrowBg(false);
if (arrivingOrArrivedIndex == 0 || arrivingOrArrivedIndex -1 == 0
|| (arrivingOrArrivedIndex -2 == 0 && stationList.size() == 3)){
firstStationItem.setStationTag(getResources().getString(R.string.bus_station_txt_tag_start));
}else {
firstStationItem.setStationTag("");
}
if (arrivingOrArrivedIndex + 1 == stationList.size() - 1 || arrivingOrArrivedIndex == stationList.size() - 1
|| (arrivingOrArrivedIndex == 0 && arrivingOrArrivedIndex + 2 == stationList.size() - 1)){ //确认是否显示 "终"
thirdStationItem.setStationTag(getResources().getString(R.string.bus_station_txt_tag_end));
}else {
thirdStationItem.setStationTag("");
}
//圆点: 0:灰色 过站 1绿色 到站或者即将到站 2蓝色未到站
if (arrivingOrArrivedIndex == 0 && isArrived){
firstStationItem.setStationNameColor(getResources().getColor(R.color.bus_line_station_color_selected));
secondStationItem.setStationNameColor(getResources().getColor(R.color.bus_arrived_station_name_text_color));
thirdStationItem.setStationNameColor(getResources().getColor(R.color.bus_arrived_station_name_text_color));
firstStationItem.setStationName(stationList.get(0).getName());
secondStationItem.setStationName(stationList.get(1).getName());
thirdStationItem.setStationName(stationList.get(2).getName());
firstStationItem.setStationPointBg(1);
secondStationItem.setStationPointBg(2);
thirdStationItem.setStationPointBg(2);
firstStationItem.setStationArrowBg(2);
secondStationItem.setStationArrowBg(2);
}else if (arrivingOrArrivedIndex == stationList.size() - 1){
firstStationItem.setStationNameColor(getResources().getColor(R.color.bus_station_tag_txt_un_color));
secondStationItem.setStationNameColor(getResources().getColor(R.color.bus_station_tag_txt_un_color));
thirdStationItem.setStationNameColor(getResources().getColor(R.color.bus_line_station_color_selected));
firstStationItem.setStationName(stationList.get(arrivingOrArrivedIndex -2).getName());
secondStationItem.setStationName(stationList.get(arrivingOrArrivedIndex -1).getName());
thirdStationItem.setStationName(stationList.get(arrivingOrArrivedIndex).getName());
firstStationItem.setStationPointBg(0);
secondStationItem.setStationPointBg(0);
thirdStationItem.setStationPointBg(1);
firstStationItem.setStationArrowBg(0);
if (isArrived){
secondStationItem.setStationArrowBg(0);
}else {
secondStationItem.setStationArrowBg(1);
}
}else {
firstStationItem.setStationNameColor(getResources().getColor(R.color.bus_station_tag_txt_un_color));
secondStationItem.setStationNameColor(getResources().getColor(R.color.bus_line_station_color_selected));
thirdStationItem.setStationNameColor(getResources().getColor(R.color.bus_arrived_station_name_text_color));
firstStationItem.setStationName(stationList.get(arrivingOrArrivedIndex -1).getName());
secondStationItem.setStationName(stationList.get(arrivingOrArrivedIndex).getName());
thirdStationItem.setStationName(stationList.get(arrivingOrArrivedIndex + 1).getName());
firstStationItem.setStationPointBg(0);
secondStationItem.setStationPointBg(1);
thirdStationItem.setStationPointBg(2);
secondStationItem.setStationArrowBg(2);
if (isArrived){
firstStationItem.setStationArrowBg(0);
}else {
firstStationItem.setStationArrowBg(1);
}
}
}
/**
* 只有两个站点的路线
* @param stationList
* @param arrivingOrArrivedIndex
* @param isArrived
*/
private void updateTwoStationsUI(List<BusStationBean> stationList,
int arrivingOrArrivedIndex,
boolean isArrived) {
secondStationItem.setVisibility(View.GONE);
secondStationItem.showOrHideStationArrowBg(false);
thirdStationItem.showOrHideStationArrowBg(false);
firstStationItem.setStationTag(getResources().getString(R.string.bus_station_txt_tag_start));
thirdStationItem.setStationTag(getResources().getString(R.string.bus_station_txt_tag_end));
firstStationItem.setStationName(stationList.get(0).getName());
thirdStationItem.setStationName(stationList.get(1).getName());
//圆点: 0:灰色 过站 1绿色 到站或者即将到站 2蓝色未到站
if (arrivingOrArrivedIndex == 0 && isArrived){//到站
firstStationItem.setStationNameColor(getResources().getColor(R.color.bus_line_station_color_selected));
thirdStationItem.setStationNameColor(getResources().getColor(R.color.bus_arrived_station_name_text_color));
firstStationItem.setStationPointBg(1);
firstStationItem.setStationArrowBg(2);
thirdStationItem.setStationPointBg(0);
}else {
firstStationItem.setStationNameColor(getResources().getColor(R.color.bus_station_tag_txt_un_color));
thirdStationItem.setStationNameColor(getResources().getColor(R.color.bus_line_station_color_selected));
if (isArrived){ //到终点
firstStationItem.setStationPointBg(0);
firstStationItem.setStationArrowBg(0);
thirdStationItem.setStationPointBg(1);
}else { //到终点途中
firstStationItem.setStationPointBg(0);
firstStationItem.setStationArrowBg(1);
thirdStationItem.setStationPointBg(1);
}
}
}
private void showOrHideSwitchLineBtn(boolean isShow) {
if (isShow){//显示切换路线
mSwitchLine.setTag(0);
mSwitchLine.setText(getResources().getString(R.string.bus_switch_line_btn));
}else {//显示结束路线
mSwitchLine.setTag(1);
mSwitchLine.setText(getResources().getString(R.string.bus_close_line_btn));
}
}
public void hideOchBus() {
// tvNotice.setVisibility(View.GONE);
}
@Override
public int getStationPanelViewId() {
return R.layout.fragment_och_bus;
}
@Override
public void restartAutopilot() {
if (!isAnimateRunning) {
mPresenter.restartAutopilot();
}
}
@Override
public SlidePanelView.OnSlidePanelMoveToEndListener getSlidePanelOnEndListener() {
return this;
}
@Override
public void moveToEnd() {
// 开启自动驾驶到下一站
if (isAnimateRunning){
stopAutopilotAnimation();
}
mPresenter.autoDriveToNextStation();
}
/**
* 设置自动驾驶可用状态
*/
public void onAutopilotEnableChange(boolean isEnable) {
if (isEnable) {
showAutopilotBiz();
} else {
hideAutopilotBiz();
}
}
public void clearBusStationsMarkers(){
CallerLogger.INSTANCE.d(M_BUS + TAG,"clearBusStationsMarkers()");
if (null != startStation) {
setOrRemoveMapMaker(false, BusConst.BUS_START_MAP_MAKER, startStation.getLat()
, startStation.getLon(),R.raw.star_marker);
}
if (null != endStation) {
setOrRemoveMapMaker(false, BusConst.BUS_END_MAP_MAKER, endStation.getLat()
, endStation.getLon(),R.raw.end_marker);
}
//清除鹰眼右下角小地图轨迹
CallerLogger.INSTANCE.d(SceneConstant.M_BUS, "clearBusStationsMarkers --------->");
smallMapView.clearPolyline();
}
/**
* VR模式切换
*
* @param isVRMode
*/
public void onVRModeChanged(boolean isVRMode) {
if (mRootView != null) {
mRootView.setVisibility(isVRMode ? View.VISIBLE : View.GONE);
}
}
/**
* 绘制地图起点终点
*
* @param isAdd
* @param uuid
*/
private void setOrRemoveMapMaker(boolean isAdd, String uuid, double lat, double longi,int resourceId) {
if (isAdd) {
Runnable setMapMarkerRunnable = () -> {
CallerLogger.INSTANCE.d(M_BUS + "setMapMaker= "+Thread.currentThread().getName(),
uuid + "=latitude=" + lat + ",longitude=" + longi);
Point.Options.Builder builder = new Point.Options.Builder(BusConst.TYPE_MARKER_BUS_ORDER, Level.MAP_MARKER)
.setId(uuid)
.anchor(0.5f, 0.5f)
.set3DMode(true)
.isUseGps(true)
.controlAngle(true)
.icon3DRes(resourceId)
.latitude(lat)
.longitude(longi);
IMogoMapUIController mapUIController = CallerMapUIServiceManager.INSTANCE.getMapUIController();
if (mapUIController != null) {
CenterLine centerLine = mapUIController.getCenterLineInfo(
longi,lat,-1);
if (null != centerLine) { // 有可能鹰眼map为空没有角度。判空使用后可能造成maker角度跟道路角度不一致 地图未初始化会返回空
Double angle = centerLine.getAngle();
if (angle != null) {
builder.rotate(angle.floatValue());
}
}
}
IMoGoOverlayManager overlayManager = CallerMapUIServiceManager.INSTANCE.getOverlayManager();
if (overlayManager != null) {
overlayManager.showOrUpdatePoint(builder.build());
}
};
OCHThreadPoolManager.getsInstance().execute(setMapMarkerRunnable);
}else {
Runnable removeMapMarkerRunnable = () -> {
CallerLogger.INSTANCE.d(M_BUS + "RemoveMapMaker="+Thread.currentThread().getName(),
uuid+"=latitude="+lat+",longitude="+longi);
Objects.requireNonNull(CallerMapUIServiceManager.INSTANCE.getOverlayManager()).removePoint(uuid);
};
OCHThreadPoolManager.getsInstance().execute(removeMapMarkerRunnable);
}
}
@Override
public void debugAutoPilotStatus(int status) {
mPresenter.debugAutoPilotStatus(status);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.switch_line_btn) {//切换路线条件: 自动驾驶过程中点击则toast提示自动驾驶中不可切换路线
//本次行程未结束不支持切换路线。点击则toast提示当前行程未完成不可切换路线
if (CallerAutoPilotStatusListenerManager.INSTANCE.getState()
== IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING) {
ToastUtils.showLong(getResources().getString(R.string.bus_switch_line_btn_warning1));
return;
}
if ((int)mSwitchLine.getTag() == 0){//切换路线
Intent intent = new Intent(getContext(), BusSwitchLineActivity.class);
startActivity(intent);
}else {//结束任务
OCHCommitDialog.Builder builder = new OCHCommitDialog.Builder();
OCHCommitDialog closeLineConfirmDialog = builder
.title(getString(R.string.bus_dialog_title))
.tips(getString(R.string.bus_dialog_tips))
.confirmStr(getString(R.string.bus_dialog_confirm))
.cancelStr(getString(R.string.bus_dialog_cancel))
.build(getContext());
closeLineConfirmDialog.setClickListener(new OCHCommitDialog.ClickListener() {
@Override
public void confirm() {
mPresenter.abortTask();
}
@Override
public void cancel() {
closeLineConfirmDialog.dismiss();
}
});
closeLineConfirmDialog.show();
}
}
}
}

View File

@@ -0,0 +1,148 @@
package com.mogo.och.bus.model;
import android.content.Context;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.eagle.core.utilcode.util.NetworkUtils;
import com.mogo.eagle.core.utilcode.util.SharedPrefs;
import com.mogo.eagle.core.utilcode.util.ToastUtils;
import com.mogo.och.bus.R;
import com.mogo.och.bus.bean.BusQueryLineTaskResponse;
import com.mogo.och.bus.bean.BusQueryLinesResponse;
import com.mogo.och.bus.bean.BusRoutesResponse;
import com.mogo.och.bus.callback.IBusLinesCallback;
import com.mogo.och.bus.net.OrderServiceManager;
import com.mogo.och.bus.ui.BusSwitchLineActivity;
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback;
/**
* @author: wangmingjun
* @date: 2022/2/9
*/
public class BusLineModel {
private static volatile BusLineModel sInstance;
private Context mContext;
private IBusLinesCallback mBusLinesCallback;
public static BusLineModel getInstance() {
if ( sInstance == null ) {
synchronized ( BusLineModel.class ) {
if ( sInstance == null ) {
sInstance = new BusLineModel();
}
}
}
return sInstance;
}
private BusLineModel() {
}
public void init() {
mContext = AbsMogoApplication.getApp();
}
public void setBusLinesCallback(IBusLinesCallback callback){
mBusLinesCallback = callback;
}
public void queryBusLines(){
OrderServiceManager.queryBusLines(mContext, new OchCommonServiceCallback<BusQueryLinesResponse>() {
@Override
public void onSuccess(BusQueryLinesResponse data) {
if (null == data && mBusLinesCallback != null) {
mBusLinesCallback.onBusLinesChange(null);
return;
}
if (mBusLinesCallback != null){
mBusLinesCallback.onBusLinesChange(data);
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
@Override
public void onFail(int code, String failMsg) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
}else {
ToastUtils.showShort("查询所有绑定路线失败:"+failMsg);
}
}
});
}
/**
* 通过线路id 查询线路排班表
* @param lineId
* @param position 位置
*/
public void queryBusLineTasksById(int lineId, int position,boolean autoRefresh){
OrderServiceManager.queryBusTaskByLineId(mContext, String.valueOf(lineId),new OchCommonServiceCallback<BusQueryLineTaskResponse>() {
@Override
public void onSuccess(BusQueryLineTaskResponse data) {
if (null == data && mBusLinesCallback != null) {
mBusLinesCallback.onBusLineTasks(null,position,autoRefresh);
return;
}
if (mBusLinesCallback != null){
mBusLinesCallback.onBusLineTasks(data,position,autoRefresh);
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
@Override
public void onFail(int code, String failMsg) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
}else {
ToastUtils.showShort("查询所有绑定路线失败:"+failMsg);
}
}
});
}
public void commitSwitchLineId(int taskId,int lineId){
OrderServiceManager.switchLine(mContext,taskId, new OchCommonServiceCallback<BusRoutesResponse>() {
@Override
public void onSuccess(BusRoutesResponse o) {
SharedPrefs.getInstance(mContext).putInt(BusSwitchLineActivity.LASTCOMMITLINEID,lineId);
if (mBusLinesCallback != null){
mBusLinesCallback.onChangeLineIdSuccess();
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
@Override
public void onFail(int code, String failMsg) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
}else {
ToastUtils.showShort("选择任务失败:"+failMsg);
}
}
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
package com.mogo.och.bus.net;
import com.mogo.eagle.core.data.BaseData;
import com.mogo.och.bus.bean.BusCloseTaskRequest;
import com.mogo.och.bus.bean.BusOrdersResponse;
import com.mogo.och.bus.bean.BusQueryLineTaskResponse;
import com.mogo.och.bus.bean.BusQueryLinesResponse;
import com.mogo.och.bus.bean.BusQueryLineStationsRequest;
import com.mogo.och.bus.bean.BusRoutesResponse;
import com.mogo.och.bus.bean.QueryLeaveAwayPassengersRequest;
import com.mogo.och.bus.bean.QueryLeaveAwayPassengersResponse;
import com.mogo.och.bus.bean.BusResetDrivingLineRequest;
import com.mogo.och.bus.bean.BusUpdateSiteStatusRequest;
import io.reactivex.Observable;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Query;
/**
* 小巴车相关接口
*
* @author tongchenfei
* <p>
* wiki: http://wiki.zhidaohulian.com/pages/viewpage.action?pageId=48970072
*/
public interface IBascApiService {
/**
* 查询当前运行任务
*
* @param request 请求参数
* @return 接口返回数据
*/
@Headers( {"Content-Type:application/json;charset=UTF-8"} )
@POST( "/och-shuttle-cabin/api/business/v1/driver/lineDataWithDriver/query" )
Observable<BusRoutesResponse> queryBusRoutes(@Header ("appId") String appId, @Header("ticket") String ticket, @Body BusQueryLineStationsRequest request);
/**
* @param request 请求参数{"destLine":1,"sn":"F803EB2046PZD00229"} 这个接口是重置bus线路的 不是重置线路中站点的
* @return 返回值是重置后的车站列表
* 开始路线
*/
@Headers( {"Content-Type:application/json;charset=UTF-8"} )
@POST( "/och-shuttle-cabin/api/flow/v1/driver/startTask" )
Observable<BusRoutesResponse> switchLine(@Header ("appId") String appId, @Header("ticket") String ticket, @Body BusResetDrivingLineRequest request);
/**
* 离站,通知服务器
* @param request
* @return
*/
@Headers({"Content-Type:application/json;charset=UTF-8"})
@POST("/och-shuttle-cabin/api/flow/v1/driver/leave")
Observable<BaseData> leaveStation(@Header ("appId") String appId, @Header("ticket") String ticket, @Body BusUpdateSiteStatusRequest request);
/**
* 到站 更新到站信息
* @param request
* @return
*/
@Headers({"Content-type:application/json;charset=UTF-8"})
@POST("/och-shuttle-cabin/api/flow/v1/driver/arrive")
Observable<BaseData> arriveSiteStation(@Header ("appId") String appId,@Header("ticket") String ticket,@Body BusUpdateSiteStatusRequest request);
/**
* 到站查询下车乘客
* @param request
* @return
*/
@Headers({"Content-type:application/json;charset=UTF-8"})
@POST("/och-shuttle-cabin/api/business/v1/driver/siteArrivedOrders")
Observable< QueryLeaveAwayPassengersResponse > queryStationLeaveAwayPassengers(@Header ("appId") String appId,@Header("ticket") String ticket,@Body QueryLeaveAwayPassengersRequest request);
/**
*
* @param appId
* @param ticket
* @param sn
* @return todo 修改结构
*/
@Headers({"Content-type:application/json;charset=UTF-8"})
@GET("/och-shuttle-cabin/api/business/v1/driver/servicingOrders/query")
Observable<BusOrdersResponse> queryBusOrders(@Header ("appId") String appId, @Header("ticket") String ticket, @Query("sn") String sn);
/**
* 查询车辆配置的所有路线
* @param appId
* @param ticket
* @param sn
* @return
*/
@GET("/och-shuttle-cabin/api/business/v1/driver/bindLine/query")
Observable<BusQueryLinesResponse> queryBusLines(@Header ("appId") String appId, @Header("ticket") String ticket, @Query("sn") String sn);
/**
* 查询路线当天的任务
* @param appId
* @param ticket
* @param lineId 线路id
* @return
*/
@GET("/och-shuttle-cabin/api/business/v1/driver/task/query")
Observable<BusQueryLineTaskResponse> queryBusTaskByLineId(@Header ("appId") String appId, @Header("ticket") String ticket, @Query("lineId") String lineId);
/**
* 中止任务
* @param appId
* @param ticket
* @param data
* @return
*/
@POST("/och-shuttle-cabin/api/flow/v1/driver/abortTask")
Observable<BaseData> abortTask(@Header ("appId") String appId, @Header("ticket") String ticket, @Body BusCloseTaskRequest data);
/**
* 任务正常跑完结束
* @param appId
* @param ticket
* @param data
* @return
*/
@Headers( {"Content-type:application/json;charset=UTF-8"} )
@POST("/och-shuttle-cabin/api/flow/v1/driver/endTask")
Observable<BaseData> endTask(@Header ("appId") String appId, @Header("ticket") String ticket, @Body BusCloseTaskRequest data);
}

View File

@@ -0,0 +1,204 @@
package com.mogo.och.bus.net
import android.content.Context
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.data.BaseData
import com.mogo.eagle.core.network.MoGoRetrofitFactory
import com.mogo.och.bus.bean.*
import com.mogo.och.common.module.biz.constant.OchCommonConst
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
import com.mogo.och.common.module.biz.network.OchCommonSubscribeImpl
import com.mogo.och.common.module.biz.network.interceptor.transformTry
/**
* @author: wangmingjun
* @date: 2021/10/20
*/
object OrderServiceManager {
private val mService: IBascApiService = MoGoRetrofitFactory.getInstance(OchCommonConst.getShuttleUrl()).create(
IBascApiService::class.java
)
/**
* 查询小巴车当前任务
* @param context
* @param callback
*/
@JvmStatic
fun queryBusRoutes(context: Context, callback: OchCommonServiceCallback<BusRoutesResponse>?) {
//获取当前高德坐标
mService.queryBusRoutes(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
BusQueryLineStationsRequest()
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "queryBusRoutes"))
}
/**
* 重置线路站点
* @param context
* @param taskId
* @param callback
*/
@JvmStatic
fun switchLine(
context: Context,
taskId: Int,
callback: OchCommonServiceCallback<BusRoutesResponse>?
) {
mService.switchLine(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
BusResetDrivingLineRequest(taskId)
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "switchLine"))
}
/**
* 中断当前任务
* @param context
* @param taskId
* @param callback
*/
@JvmStatic
fun abortTask(context: Context, taskId: Int, callback: OchCommonServiceCallback<BaseData>?) {
mService.abortTask(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
BusCloseTaskRequest(taskId)
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "abortTask"))
}
/**
* 正常结束任务
* @param context
* @param taskId
* @param callback
*/
@JvmStatic
fun endTask(context: Context, taskId: Int, callback: OchCommonServiceCallback<BaseData>?) {
mService.endTask(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
BusCloseTaskRequest(taskId)
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "endTask"))
}
/**
* 离站上报
* @param context
* @param seq
* @param siteId
* @param callback
*/
@JvmStatic
fun leaveStation(
context: Context,
seq: Int,
siteId: Int,
taskId: Int,
writeVersion: Long,
callback: OchCommonServiceCallback<BaseData>?
) {
mService.leaveStation(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
BusUpdateSiteStatusRequest(taskId, siteId, seq,writeVersion)
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "leaveStation"))
}
/**
* 到站更新站点状态
* @param context
* @param seq
* @param siteId
* @param callback
*/
@JvmStatic
fun arriveSiteStation(
context: Context, seq: Int, siteId: Int, taskId: Int, writeVersion: Long,
callback: OchCommonServiceCallback<BaseData>?
) {
mService.arriveSiteStation(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
BusUpdateSiteStatusRequest(taskId, siteId, seq, writeVersion)
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "arriveSiteStation"))
}
/**
* 查询下车乘客
* @param context
* @param seq
* @param siteId
* @param callback
*/
@JvmStatic
fun queryStationLeaveAwayPassengers(
context: Context,
seq: Int,
siteId: Int,
callback: OchCommonServiceCallback<QueryLeaveAwayPassengersResponse>?
) {
mService.queryStationLeaveAwayPassengers(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
QueryLeaveAwayPassengersRequest(seq, siteId)
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "queryStationLeaveAwayPassengers"))
}
/**
* 查询小巴车订单
* @param context
* @param callback
*/
@JvmStatic
fun queryBusOrders(context: Context, callback: OchCommonServiceCallback<BusOrdersResponse>?) {
mService.queryBusOrders(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
MoGoAiCloudClientConfig.getInstance().sn
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "queryBusOrders"))
}
@JvmStatic
fun queryBusLines(context: Context, callback: OchCommonServiceCallback<BusQueryLinesResponse>?) {
mService.queryBusLines(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
MoGoAiCloudClientConfig.getInstance().sn
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "queryBusLines"))
}
@JvmStatic
fun queryBusTaskByLineId(
context: Context,
lineId: String?,
callback: OchCommonServiceCallback<BusQueryLineTaskResponse>?
) {
mService.queryBusTaskByLineId(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
lineId
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "queryBusLines"))
}
}

View File

@@ -0,0 +1,47 @@
package com.mogo.och.bus.net.login
import android.content.Context
import com.mogo.eagle.core.data.BaseData
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean
import com.mogo.och.common.module.biz.bean.TaxiLoginReqBean
import com.mogo.och.common.module.biz.bean.TaxiLoginRespBean
import com.mogo.och.common.module.biz.bean.TaxiLogoutReqBean
import com.mogo.och.common.module.biz.network.LoginDefaultManage
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
class LoginBusImpl: LoginDefaultManage {
override fun getPhoneCode(
context: Context, phone: String?,
callback: OchCommonServiceCallback<BaseData>?
) {
OchCommonServiceManager.getPhoneCode(context,phone,callback)
}
override fun gotoLoginBycode(
context: Context,
phone: String?,
code: String?,
location4Login: TaxiLoginReqBean.Location4Login?,
callback: OchCommonServiceCallback<TaxiLoginRespBean>?
) {
OchCommonServiceManager.gotoLoginBycode(context,phone,code,location4Login,callback)
}
override fun logout(
context: Context,
location4Login: TaxiLogoutReqBean.Location4Login?,
callback: OchCommonServiceCallback<BaseData>?
) {
OchCommonServiceManager.logout(context,location4Login,callback)
}
override fun queryDriverServiceStatus(
context: Context,
callback: OchCommonServiceCallback<DriverStatusQueryRespBean>?
) {
OchCommonServiceManager.queryDriverServiceStatus(context,callback)
}
}

View File

@@ -0,0 +1,107 @@
package com.mogo.och.bus.net.login
import android.content.Context
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.och.common.module.biz.bean.TaxiLoginSmsReqBean
import com.mogo.och.common.module.biz.bean.TaxiLoginReqBean
import com.mogo.och.common.module.biz.bean.TaxiLoginRespBean
import com.mogo.och.common.module.biz.bean.TaxiLogoutReqBean
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean
import com.mogo.och.common.module.biz.constant.OchCommonConst
import com.mogo.cloud.passport.MoGoAiCloudClient
import com.mogo.eagle.core.data.BaseData
import com.mogo.eagle.core.network.MoGoRetrofitFactory
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
import com.mogo.och.common.module.biz.network.OchCommonSubscribeImpl
import com.mogo.och.common.module.biz.network.interceptor.transformTry
/**
* Created by pangfan on 2021/8/19
*/
object OchCommonServiceManager {
private const val TAG = "OchCommonServiceManager"
private val mLoginServiceApi: OchLoginServiceApi =
MoGoRetrofitFactory.getInstance(OchCommonConst.getShuttleUrl()).create(
OchLoginServiceApi::class.java
)
/**
* 获取手机验证码
* @param context
* @param callback
*/
@JvmStatic
fun getPhoneCode(
context: Context, phone: String?,
callback: OchCommonServiceCallback<BaseData>?
) {
mLoginServiceApi.getPhoneCode(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
TaxiLoginSmsReqBean(phone)
).transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "getPhoneCode"))
}
/**
* 通过验证码登录
* @param context
* @param callback
*/
@JvmStatic
fun gotoLoginBycode(
context: Context, phone: String?, code: String?,
location4Login: TaxiLoginReqBean.Location4Login?,
callback: OchCommonServiceCallback<TaxiLoginRespBean>?
) {
val sn = MoGoAiCloudClientConfig.getInstance().sn
mLoginServiceApi.gotoLoginBycode4Bus(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
TaxiLoginReqBean(phone, code, sn, location4Login)
).transformTry().subscribe(OchCommonSubscribeImpl(context, callback, "gotoLoginBycode"))
}
/**
* 登出
*/
@JvmStatic
fun logout(
context: Context,
location4Login: TaxiLogoutReqBean.Location4Login?,
callback: OchCommonServiceCallback<BaseData>?
) {
mLoginServiceApi.logout4Bus(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
TaxiLogoutReqBean(MoGoAiCloudClientConfig.getInstance().sn, location4Login)
).transformTry().subscribe(OchCommonSubscribeImpl(context, callback, "logout"))
}
/**
* 接单状态和登录状态查询
*
* @param context
* @param callback
*/
@JvmStatic
fun queryDriverServiceStatus(
context: Context,
callback: OchCommonServiceCallback<DriverStatusQueryRespBean>?
) {
if (MoGoAiCloudClientConfig.getInstance().token.isEmpty()) {
callback?.onFail(OchCommonConst.WAIT_TAKEN, "等待令牌中请稍等")
MoGoAiCloudClient.getInstance().refreshToken()
return
}
mLoginServiceApi.queryDriverServiceStatusAndLoginStatus(
MoGoAiCloudClientConfig.getInstance().serviceAppId,
MoGoAiCloudClientConfig.getInstance().token,
MoGoAiCloudClientConfig.getInstance().sn
).transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "queryDriverServiceStatus"))
}
}

View File

@@ -0,0 +1,65 @@
package com.mogo.och.bus.net.login;
import com.mogo.eagle.core.data.BaseData;
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean;
import com.mogo.och.common.module.biz.bean.TaxiLoginReqBean;
import com.mogo.och.common.module.biz.bean.TaxiLoginRespBean;
import com.mogo.och.common.module.biz.bean.TaxiLoginSmsReqBean;
import com.mogo.och.common.module.biz.bean.TaxiLogoutReqBean;
import io.reactivex.Observable;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
/**
* Created by pangfan on 2021/8/19
* <p>
* 网约车-出租车接口定义
*/
interface OchLoginServiceApi {
/**
* 获取手机验证码
*
* @return
*/
@Headers( {"Content-type:application/json;charset=UTF-8"} )
@POST( "/och-shuttle-cabin/api/sms/v1/driver/sendSms" )
Observable<DriverStatusQueryRespBean> getPhoneCode(@Header("appId") String appId
, @Header("ticket") String ticket, @Body TaxiLoginSmsReqBean data);
/**
* 通过验证码登录
* @param appId
* @param ticket
* @param data
* @return
*/
@Headers( {"Content-type:application/json;charset=UTF-8"} )
@POST( "/och-shuttle-cabin/api/flow/v1/driver/startOperation" )
Observable<TaxiLoginRespBean> gotoLoginBycode4Bus(@Header("appId") String appId
, @Header("ticket") String ticket, @Body TaxiLoginReqBean data);
/**
* 登出接口
*/
@Headers({"Content-type:application/json;charset=UTF-8"})
@POST("/och-shuttle-cabin/api/flow/v1/driver/endOperation")
Observable<BaseData> logout4Bus(@Header("appId") String appId, @Header("ticket") String ticket,
@Body TaxiLogoutReqBean data);
/**
* 接单状态和登录状态查询 出租车司机端、小巴车司机端、小巴车乘客端
* @param sn
* @return
*/
@Headers( {"Content-type:application/json;charset=UTF-8"} )
@GET( "/och-shuttle-cabin/api/business/v1/driver/loginStatus")
Observable<DriverStatusQueryRespBean> queryDriverServiceStatusAndLoginStatus(@Header("appId") String appId
, @Header("ticket") String ticket, @Query("sn") String sn);
}

View File

@@ -0,0 +1,110 @@
package com.mogo.och.bus.presenter;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import com.mogo.commons.mvp.Presenter;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.bus.bean.BusQueryLineTaskResponse;
import com.mogo.och.bus.bean.BusQueryLinesResponse;
import com.mogo.och.bus.callback.IBusLinesCallback;
import com.mogo.och.bus.model.BusLineModel;
import com.mogo.och.bus.model.OrderModel;
import com.mogo.och.bus.ui.BusSwitchLineView;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
/**
* @author: wangmingjun
* @date: 2022/2/9
*/
public class BusLinePresenter extends Presenter<BusSwitchLineView> implements IBusLinesCallback {
private Disposable subscribe;
public BusLinePresenter(BusSwitchLineView view) {
super(view);
BusLineModel.getInstance().init();
OrderModel.getInstance().init();
}
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
super.onCreate(owner);
initListener();
}
private void initListener() {
BusLineModel.getInstance().setBusLinesCallback(this);
}
private void runOnUIThread( Runnable executor ) {
if ( executor == null ) {
return;
}
if ( Looper.myLooper() != Looper.getMainLooper() ) {
UiThreadHandler.post( executor );
} else {
executor.run();
}
}
@Override
public void onBusLinesChange(BusQueryLinesResponse lines) {
runOnUIThread(() -> mView.onBusLinesChange(lines));
}
@Override
public void onChangeLineIdSuccess() {
OrderModel.getInstance().clearBusStationDatas();
runOnUIThread(() -> mView.onChangeLineIdSuccess());
}
public void queryBusLines(){
BusLineModel.getInstance().queryBusLines();
}
public void queryBusLineTasks(int lineId, int position,boolean close){
if(subscribe!=null&&!subscribe.isDisposed()){
subscribe.dispose();
}
if(close){
return;
}
BusLineModel.getInstance().queryBusLineTasksById(lineId,position,false);
subscribe = Observable.interval(3, TimeUnit.MINUTES).subscribe(aLong -> {
BusLineModel.getInstance().queryBusLineTasksById(lineId,position,true);
});
}
public void commitSwitchLineId(int taskId,int lineId){
BusLineModel.getInstance().commitSwitchLineId(taskId,lineId);
}
public void removeListener(){
BusLineModel.getInstance().setBusLinesCallback(null);
}
public void queryBusRoutes(){
OrderModel.getInstance().queryBusRoutes();
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
super.onDestroy(owner);
if(subscribe!=null&&!subscribe.isDisposed()){
subscribe.dispose();
}
}
@Override
public void onBusLineTasks(BusQueryLineTaskResponse o, int position,boolean autoRefresh) {
mView.onBusLineTasks(o,position,autoRefresh);
}
}

View File

@@ -0,0 +1,77 @@
package com.mogo.och.bus.presenter;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.och.bus.constant.BusConst;
import com.mogo.och.bus.model.OrderModel;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Created on 2021/11/22
*
* 管理轮询逻辑(订单轮询、新单轮询、新单抢单结果轮询等等)
*/
public class BusModelLoopManager {
private static final String TAG = BusModelLoopManager.class.getSimpleName();
private static final class SingletonHolder {
private static final BusModelLoopManager INSTANCE = new BusModelLoopManager();
}
public static BusModelLoopManager getInstance() {
return SingletonHolder.INSTANCE;
}
private Disposable mHeartbeatDisposable; //心跳轮询
// private Disposable mQueryPassengerDisposable; //查询核销乘客
// public void startQueryPassengerWriteOffLoop() {
// if (mQueryPassengerDisposable != null && !mQueryPassengerDisposable.isDisposed()) {
// return;
// }
// CallerLogger.INSTANCE.i(M_BUS + TAG, "startQueryPassengerWriteOffLoop()");
// mQueryPassengerDisposable = Observable.interval(LOOP_DELAY_500,
// LOOP_PASSENGER_5S, TimeUnit.MILLISECONDS)
// .map((aLong -> aLong + 1))
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(aLong -> BusOrderModel.getInstance().queryPassengerWriteOff());
// }
// public void stopQueryPassengerWriteOffLoop() {
// if (mQueryPassengerDisposable != null) {
// CallerLogger.INSTANCE.i(M_BUS + TAG, "stopQueryPassengerWriteOffLoop()");
// mQueryPassengerDisposable.dispose();
// mQueryPassengerDisposable = null;
// }
// }
public void startHeartbeatLoop() {
if (mHeartbeatDisposable != null && !mHeartbeatDisposable.isDisposed()) {
return;
}
CallerLogger.INSTANCE.i(M_BUS + TAG, "startHeartbeatLoop()");
mHeartbeatDisposable = Observable.interval(BusConst.LOOP_DELAY,
BusConst.LOOP_PERIOD_60S, TimeUnit.MILLISECONDS)
.map((aLong -> aLong + 1))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> OrderModel.getInstance().runCarHeartbeat());
}
public void stopHeartbeatLoop() {
if (mHeartbeatDisposable != null) {
CallerLogger.INSTANCE.i(M_BUS + TAG, "stopHeartbeatLoop()");
mHeartbeatDisposable.dispose();
mHeartbeatDisposable = null;
}
}
}

View File

@@ -0,0 +1,313 @@
package com.mogo.och.bus.presenter;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.mvp.Presenter;
import com.mogo.commons.voice.AIAssist;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.data.bean.BusStationBean;
import com.mogo.och.bus.bean.WriteOffPassenger;
import com.mogo.och.bus.callback.IBusADASStatusCallback;
import com.mogo.och.bus.callback.IBusControllerStatusCallback;
import com.mogo.och.bus.callback.IPassengerCallback;
import com.mogo.och.bus.callback.IRefreshBusStationsCallback;
import com.mogo.och.bus.callback.ISlidePannelHideCallback;
import com.mogo.och.bus.fragment.BusFragment;
import com.mogo.och.bus.model.OrderModel;
import com.mogo.och.bus.util.BusTrajectoryManager;
import com.mogo.och.common.module.biz.bean.DriverStatusQueryRespBean;
import com.mogo.och.common.module.biz.callback.ILoginCallback;
import com.mogo.och.common.module.biz.constant.LoginStatusManager;
import com.mogo.och.common.module.manager.OCHAdasAbilityManager;
import com.mogo.och.common.module.voice.VoiceNotice;
import com.mogo.och.data.manager.cache.CacheDataManager;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import mogo.telematics.pad.MessagePad;
import mogo_msg.MogoReportMsg;
import system_master.SsmInfo;
import system_master.SystemStatusInfo;
/**
* 网约车小巴
*
* @author tongchenfei
*/
public class BusPresenter extends Presenter<BusFragment>
implements IRefreshBusStationsCallback, ISlidePannelHideCallback
, IMoGoAutopilotStatusListener, IBusControllerStatusCallback, ILoginCallback, IPassengerCallback, IBusADASStatusCallback {
private static final String TAG = "BusPresenter";
private List<BusStationBean> mStationList = new ArrayList<>();
private int mCurrentStation = 0;
public BusPresenter(BusFragment view) {
super(view);
//2021.11.1 鹰眼架构整合由IMoGoAutopilotStatusListener逐步替代IMogoAdasOCHCallback接口
CallerAutoPilotStatusListenerManager.INSTANCE.addListener(TAG, this);
OrderModel.getInstance().init();
OCHAdasAbilityManager.getInstance().init(AbsMogoApplication.getApp());
}
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
super.onCreate(owner);
initModelListener();
OrderModel.getInstance().queryBusCacheRoutes();
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
super.onDestroy(owner);
OrderModel.getInstance().release();
releaseListener();
}
public void initModelListener() {
OrderModel.getInstance().setRefreshBusStationsCallback(this);
OrderModel.getInstance().setSlidePanelHideCallback(this);
OrderModel.getInstance().setControllerStatusCallback(this);
OrderModel.getInstance().setPassengerCallback(this);
OrderModel.getInstance().setAdasStatusCallback(this);
}
public void releaseListener() {
OrderModel.getInstance().setRefreshBusStationsCallback(null);
OrderModel.getInstance().setSlidePanelHideCallback(null);
OrderModel.getInstance().setControllerStatusCallback(null);
OrderModel.getInstance().setPassengerCallback(null);
OrderModel.getInstance().setAdasStatusCallback(null);
OCHAdasAbilityManager.getInstance().release();
}
public void abortTask() {
OrderModel.getInstance().abortTask();
}
public void autoDriveToNextStation() {
OrderModel.getInstance().autoDriveToNextStation();
}
public void restartAutopilot() {
if (OrderModel.getInstance().isGoingToNextStation()){
OrderModel.getInstance().restartAutopilot();
}
}
// 登出
public void logout() {
OrderModel.getInstance().logout();
}
@Override
public void updateBusTaskStatus(String lineName,String lineTime,
List<BusStationBean> stationList,
int arrivingOrArrivedIndex,
boolean isArrived) {
mStationList.clear();
mStationList.addAll(stationList);
if (arrivingOrArrivedIndex == 0 || isArrived){
mCurrentStation = arrivingOrArrivedIndex;
}else {
mCurrentStation = arrivingOrArrivedIndex -1;
}
CallerLogger.INSTANCE.d(M_BUS + "BusOrderModel =", " mCurrentStation =" + mCurrentStation);
if (mView != null) {
runOnUIThread(() -> mView.updateBusTaskStatus(lineName,lineTime,
stationList, arrivingOrArrivedIndex, isArrived));
}
}
@Override
public void updateEmptyUi() {
runOnUIThread(() -> mView.updateLineEmptyUI());
}
@Override
public void clearBusStationsMarkers() {
runOnUIThread(() -> mView.clearBusStationsMarkers());
}
@Override
public void hideSlidePanel() {
if (mView != null) {
mView.hideSlidePanel();
mView.setArrivedClikable(true);
}
}
@Override
public void onAutopilotArriveAtStation(MessagePad.ArrivalNotification arrivalNotification) {
CallerLogger.INSTANCE.e( M_BUS + TAG, "行程日志-onAutopilotArriveAtStation arrive");
OrderModel.getInstance().onArriveAt(arrivalNotification);
}
@Override
public void onAutopilotSNRequest() {
}
@Override
public void onAutopilotStatusResponse(@NotNull AutopilotStatusInfo autopilotStatusInfo) {
}
@Override
public void onAutopilotStatusResponse(int state) {
switch (state) {
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE:
case IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING:
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE:
if (FunctionBuildConfig.isDemoMode
&& (
(mCurrentStation >= 0 && mCurrentStation <= mStationList.size() - 1)
&& OrderModel.getInstance().isGoingToNextStation()
)
) {
CallerLogger.INSTANCE.d(M_BUS + "BusOrderModel=", "有美化功能");
return;
}
// 改变UI自动驾驶状态
runOnUIThread(() -> mView.onAutopilotStatusChanged(state));
break;
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING:
OrderModel.getInstance().triggerStartServiceEvent(
OrderModel.getInstance().isRestartAutopilot(), true);
// 改变UI自动驾驶状态
runOnUIThread(() -> mView.onAutopilotStatusChanged(state));
break;
default:
runOnUIThread(() -> mView.onAutopilotEnableChange(false));
break;
}
}
@Override
public void onVRModeChanged(boolean isVRMode) {
runOnUIThread(() -> mView.onVRModeChanged(isVRMode));
}
@Override
public void onCarLocationChanged(MogoLocation location) {
if (null != location) {
runOnUIThread(() -> mView.updateSpeedView((float) location.getGnssSpeed()));
}
}
@Override
public void startOpenAutopilot() {
//非美化模式下启动动画
runOnUIThread(() -> mView.startAutopilotAnimation());
//中间站点再次开启自驾时, 自动驾驶状态是2未改变 此次鹰眼底层不再返给业务,需优化按钮动画显示
if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING
== CallerAutoPilotStatusListenerManager.INSTANCE.getState()){
mView.onAutopilotStatusChanged(CallerAutoPilotStatusListenerManager.INSTANCE.getState());
}
}
private void runOnUIThread(Runnable executor) {
if (executor == null) {
return;
}
if (Looper.myLooper() != Looper.getMainLooper()) {
UiThreadHandler.post(executor);
} else {
executor.run();
}
}
/**
* 测试使用
*/
public void debugAutoPilotStatus(int status) {
AutopilotStatusInfo info = new AutopilotStatusInfo();
info.setState(status);
onAutopilotStatusResponse(info);
}
@Override
public void onAutopilotRouteLineId(long lineId) {
}
@Override
public void onAutopilotGuardian(MogoReportMsg.MogoReportMessage guardianInfo) {
BusTrajectoryManager.getInstance().onAutopilotGuardian(guardianInfo);
}
@Override
public void onAutopilotIpcConnectStatusChanged(int status, @Nullable String reason) {
}
@Override
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {
}
@Override
public void onSystemStatus(@NonNull SsmInfo.SsmStatusInf statusInf) {
}
@Override
public void loginSuccess(DriverStatusQueryRespBean data) {
CallerLogger.INSTANCE.d(M_BUS + TAG, " loginStatus =" + LoginStatusManager.isLogin());
if(LoginStatusManager.isLogin()){
OrderModel.getInstance().startOrStopOrderLoop(true);
// OrderModel.getInstance().queryBusRoutes();
OrderModel.getInstance().queryBusCacheRoutes();
}else {
BusTrajectoryManager.getInstance().stopTrajReqLoop();
OrderModel.getInstance().startOrStopOrderLoop(false);
BusTrajectoryManager.getInstance().stopTrajReqLoop();
clearBusStationsMarkers();
mView.hideSlidePanel();
OrderModel.getInstance().closeBeautificationMode();
}
}
@Override
public void loginFail(boolean isLogin) {
}
@Override
public void playPassenger(WriteOffPassenger passenger) {
int passengerNum = passenger.passengerSize;
if (passengerNum > 1){ //多人播报 "x人" ---》x人核验通过
VoiceNotice.showNotice(passengerNum+"人核验通过", AIAssist.LEVEL3);
OrderModel.getInstance().sendWriteOffNumToClient(passengerNum+"人核验通过");
}else { //是 1 播放 "滴" 2022.11.09 改为: "核验通过"
// mView.playDI();
VoiceNotice.showNotice("核验通过", AIAssist.LEVEL3);
OrderModel.getInstance().sendWriteOffNumToClient("核验通过");
}
}
@Override
public void onStartAdasFailure() {
runOnUIThread(() -> mView.stopAnimAndUpdateBtnStatus());
}
@Override
public void onAutopilotDockerInfo(@NonNull String dockerVersion) {
}
}

View File

@@ -0,0 +1,42 @@
package com.mogo.och.bus.receiver;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.och.bus.constant.BusConst;
/**
* 测试小巴车的场景
*
* @author donghongyu
* @date 4/26/21 12:08 PM
*/
public class TestBusBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "TestBusBroadcastReceiver";
private Context mContext;
@Override
public void onReceive(Context context, Intent intent) {
try {
this.mContext = context;
int sceneType = intent.getIntExtra(BusConst.BROADCAST_TEST_BUS_CONTROL_TYPE_EXTRA_KEY, 0);
CallerLogger.INSTANCE.d(M_BUS + TAG, "sceneType:" + sceneType);
// 分发场景
dispatchSceneTest(sceneType);
} catch (Exception e) {
e.printStackTrace();
}
}
private void dispatchSceneTest(int sceneType) {
}
}

View File

@@ -0,0 +1,99 @@
package com.mogo.och.bus.ui
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import com.mogo.och.bus.R
import com.mogo.och.common.module.utils.BlinkAnimationUtil
import kotlinx.android.synthetic.main.bus_stations_common_item.view.*
/**
* @author: wangmingjun
* @date: 2022/9/15
*/
class BusStationCommonItem @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr){
init {
LayoutInflater.from(context).inflate(R.layout.bus_stations_common_item,this,true)
}
fun setStationName(name: String){
busStationNameTv.text = name
}
fun setStationNameColor(color: Int){
busStationNameTv.setTextColor(color)
}
fun setStationPointBg(type: Int) { // 0:灰色 过站 1绿色 到站或者即将到站 2蓝色未到站
BlinkAnimationUtil.clearAnimation(busCircleIv)
when (type) {
0 -> {
busCircleIvBg.visibility = GONE
busCircleIv.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_point_grey_bus
)
)
}
1 -> {
busCircleIvBg.visibility = VISIBLE
busCircleIv.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_point_green_bus
)
)
BlinkAnimationUtil.setAnimation(busCircleIv)
}
2 -> {
busCircleIvBg.visibility = GONE
busCircleIv.setImageDrawable(
ContextCompat.getDrawable(
context,
R.drawable.icon_point_blue_bus
)
)
}
}
}
fun getCircleImageView() : ImageView{
return busCircleIv
}
fun setStationArrowBg(type: Int){// 0:灰色 过站 1绿色 前往下一站 2蓝色 未到站
when(type){
0 -> busArrowBg.setImageResource(R.drawable.icon_arrow_grey_bus)
1 -> busArrowBg.setImageResource(R.drawable.icon_arrow_green_bus)
2 -> busArrowBg.setImageResource(R.drawable.icon_arrow_blue_bus)
}
}
fun setStationTag(tag: String){ // 0起 1
if (tag.isNullOrEmpty()){
busTagTxt.visibility = GONE
}else{
busTagTxt.text = tag
busTagTxt.visibility = VISIBLE
}
}
fun showOrHideStationArrowBg(isShow:Boolean){
if (isShow){
busArrowBg.visibility = VISIBLE
}else{
busArrowBg.visibility = GONE
}
}
}

View File

@@ -0,0 +1,222 @@
package com.mogo.och.bus.ui
import android.graphics.Point
import android.os.Bundle
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mogo.commons.mvp.MvpActivity
import com.mogo.eagle.core.utilcode.util.SharedPrefs
import com.mogo.och.bus.presenter.BusLinePresenter
import com.mogo.och.bus.ui.adapter.SwitchLineAdapter
import com.mogo.och.bus.bean.BusQueryLinesResponse
import com.mogo.och.bus.ui.adapter.OpenItemAnimator
import com.mogo.eagle.core.utilcode.mogo.view.SpacesItemDecoration
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.bus.R
import com.mogo.och.bus.bean.BusQueryLineTaskResponse
import java.util.ArrayList
/**
* @author: wangmingjun
* @date: 2022/2/8
*/
class BusSwitchLineActivity : MvpActivity<BusSwitchLineView?, BusLinePresenter?>(),
View.OnClickListener, BusSwitchLineView {
companion object{
const val LASTCOMMITLINEID = "lastcommitlineid"
}
private lateinit var mClose: ImageView
private lateinit var mNoDatasView: ConstraintLayout
private lateinit var mLinesListView: RecyclerView
private lateinit var mLineCommitBtn: TextView
private lateinit var mAdapter: SwitchLineAdapter
private lateinit var linearLayoutManager:LinearLayoutManager
private val mData: MutableList<BusQueryLinesResponse.Result> = ArrayList()
override fun getLayoutId(): Int {
return R.layout.activity_bus_switch_line
}
override fun createPresenter(): BusLinePresenter {
return BusLinePresenter(this)
}
override fun initViews() {
initWH()
initView()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initDatas()
}
/**
* 初始化view
*/
private fun initView() {
mClose = findViewById(R.id.switch_line_close)
mClose.setOnClickListener(this)
mNoDatasView = findViewById(R.id.no_order_data_view)
mLineCommitBtn = findViewById(R.id.switch_line_btn_commit)
mLineCommitBtn.setOnClickListener(this)
mLinesListView = findViewById(R.id.switch_line_rv)
linearLayoutManager = LinearLayoutManager(this)
mLinesListView.setLayoutManager(linearLayoutManager)
mLinesListView.setItemAnimator(OpenItemAnimator())
mAdapter = SwitchLineAdapter(applicationContext, mData)
mLinesListView.addItemDecoration(
SpacesItemDecoration(
4
)
)
mLinesListView.setAdapter(mAdapter)
//设置item 点击事件
mAdapter.setOnLineItemClickListener(object :SwitchLineAdapter.LineItemClickListener{
override fun onItemClick(position: Int, close: Boolean) {
mPresenter?.queryBusLineTasks(mData[position].lineId, position, close)
}
})
}
/**
* 设置布局宽高
*/
private fun initWH() {
val window = window
val params = window.attributes
val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val point = Point()
windowManager.defaultDisplay.getSize(point) //用于获取屏幕高度
params.width = (point.x * 0.375).toInt()
params.height = ViewGroup.LayoutParams.MATCH_PARENT
window.attributes = params
window.setGravity(Gravity.START or Gravity.BOTTOM)
}
/**
* 初始化数据
*/
private fun initDatas() {
mPresenter?.queryBusLines()
}
/**
* 查询返回绑定路线集合
* @param data
*/
override fun onBusLinesChange(data: BusQueryLinesResponse?) {
if (null == data) {
showNoData(true)
return
}
var lastCommitLineid = SharedPrefs.getInstance(this).getInt(LASTCOMMITLINEID, -1)
if (data.data != null && data.data.size > 0) {
showNoData(false)
mData.clear()
mData.addAll(data.data)
mAdapter.notifyDataSetChanged()
if(lastCommitLineid>0){
mData.forEachIndexed { index, line ->
if(line.lineId==lastCommitLineid){
line.open = true
mPresenter?.queryBusLineTasks(line.lineId,index,false)
return
}
}
}
} else {
showNoData(true)
}
}
override fun onChangeLineIdSuccess() {
ToastUtils.showLong(resources.getString(R.string.bus_change_line_commit_tip_s))
mPresenter?.queryBusRoutes()
mAdapter.setOnLineItemClickListener(null)
mPresenter?.removeListener()
finish()
}
override fun onBusLineTasks(lineTaskInfo: BusQueryLineTaskResponse?, position: Int,autoRefresh:Boolean) {
val result = mData[position]
if (result.taskList == null) {
result.taskList = ArrayList()
}
if (lineTaskInfo == null) {
result.haveTask = true
// 打开操作
mAdapter.notifyItemChanged(position)
} else {
if (lineTaskInfo.data == null || lineTaskInfo.data.isEmpty()) {
result.haveTask = true
lineTaskInfo.data = ArrayList()
}
if (lineTaskInfo.data.size != result.taskList.size) { // 不相等有变动 重新赋值
result.taskList.clear()
result.taskList.addAll(lineTaskInfo.data)
if(autoRefresh) {
mAdapter.checkTaskId = -1
mAdapter.checkLineId = -1
lineTaskInfo.data.forEach {
if (it.id == mAdapter.checkTaskId) {
mAdapter.checkTaskId = it.id
mAdapter.checkLineId = result.lineId
}
}
}
// 打开操作
mAdapter.notifyItemChanged(position)
linearLayoutManager.stackFromEnd = (position==mData.size-1||position==mData.size-2)&&mData.size>6
mLinesListView.smoothScrollToPosition(position)
}else if(lineTaskInfo.data.isEmpty()){
mAdapter.notifyItemChanged(position)
}
}
}
/**
* 有无数据UI显示
* @param b
*/
private fun showNoData(b: Boolean) {
if (b) {
mLinesListView.visibility = View.GONE
mLineCommitBtn.visibility = View.GONE
mNoDatasView.visibility = View.VISIBLE
} else {
mLinesListView.visibility = View.VISIBLE
mLineCommitBtn.visibility = View.VISIBLE
mNoDatasView.visibility = View.GONE
}
}
override fun onClick(v: View) {
//关闭dialog
if (v.id == R.id.switch_line_close) {
finish()
return
}
//切换路线提交
if (v.id == R.id.switch_line_btn_commit) {
if(mAdapter.checkLineId!=-1&&mAdapter.checkTaskId!=-1){
mPresenter?.commitSwitchLineId(mAdapter.checkTaskId,mAdapter.checkLineId)
}else{
ToastUtils.showLong("请选择任务")
}
}
}
override fun onDestroy() {
mPresenter!!.removeListener()
super.onDestroy()
}
}

View File

@@ -0,0 +1,18 @@
package com.mogo.och.bus.ui;
import com.mogo.commons.mvp.IView;
import com.mogo.och.bus.bean.BusQueryLineTaskResponse;
import com.mogo.och.bus.bean.BusQueryLinesResponse;
/**
* @author: wangmingjun
* @date: 2022/2/10
*/
public interface BusSwitchLineView extends IView {
void onBusLinesChange(BusQueryLinesResponse data);
void onChangeLineIdSuccess();
void onBusLineTasks(BusQueryLineTaskResponse o, int position,boolean autoRefresh);
}

View File

@@ -0,0 +1,655 @@
package com.mogo.och.bus.ui.adapter;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.view.View;
import android.view.ViewPropertyAnimator;
import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import java.util.ArrayList;
import java.util.List;
/**
* This implementation of {@link RecyclerView.ItemAnimator} provides basic
* animations on remove, add, and move events that happen to the items in
* a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.
*
* @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)
*/
public class OpenItemAnimator extends DefaultItemAnimator {
private static final boolean DEBUG = false;
private static TimeInterpolator sDefaultInterpolator;
private ArrayList<RecyclerView.ViewHolder> mPendingRemovals = new ArrayList<>();
private ArrayList<RecyclerView.ViewHolder> mPendingAdditions = new ArrayList<>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
ArrayList<ArrayList<RecyclerView.ViewHolder>> mAdditionsList = new ArrayList<>();
ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
ArrayList<RecyclerView.ViewHolder> mAddAnimations = new ArrayList<>();
ArrayList<RecyclerView.ViewHolder> mMoveAnimations = new ArrayList<>();
ArrayList<RecyclerView.ViewHolder> mRemoveAnimations = new ArrayList<>();
ArrayList<RecyclerView.ViewHolder> mChangeAnimations = new ArrayList<>();
private static class MoveInfo {
public RecyclerView.ViewHolder holder;
public int fromX, fromY, toX, toY;
MoveInfo(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
this.holder = holder;
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
}
}
private static class ChangeInfo {
public RecyclerView.ViewHolder oldHolder, newHolder;
public int fromX, fromY, toX, toY;
private ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder) {
this.oldHolder = oldHolder;
this.newHolder = newHolder;
}
ChangeInfo(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
int fromX, int fromY, int toX, int toY) {
this(oldHolder, newHolder);
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
}
@Override
public String toString() {
return "ChangeInfo{"
+ "oldHolder=" + oldHolder
+ ", newHolder=" + newHolder
+ ", fromX=" + fromX
+ ", fromY=" + fromY
+ ", toX=" + toX
+ ", toY=" + toY
+ '}';
}
}
@Override
public void runPendingAnimations() {
boolean removalsPending = !mPendingRemovals.isEmpty();
boolean movesPending = !mPendingMoves.isEmpty();
boolean changesPending = !mPendingChanges.isEmpty();
boolean additionsPending = !mPendingAdditions.isEmpty();
if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
// nothing to animate
return;
}
// First, remove stuff
for (RecyclerView.ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear();
// Next, move stuff
if (movesPending) {
final ArrayList<MoveInfo> moves = new ArrayList<>();
moves.addAll(mPendingMoves);
mMovesList.add(moves);
mPendingMoves.clear();
Runnable mover = new Runnable() {
@Override
public void run() {
for (MoveInfo moveInfo : moves) {
animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
moveInfo.toX, moveInfo.toY);
}
moves.clear();
mMovesList.remove(moves);
}
};
if (removalsPending) {
View view = moves.get(0).holder.itemView;
ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
} else {
mover.run();
}
}
// Next, change stuff, to run in parallel with move animations
if (changesPending) {
final ArrayList<ChangeInfo> changes = new ArrayList<>();
changes.addAll(mPendingChanges);
mChangesList.add(changes);
mPendingChanges.clear();
Runnable changer = new Runnable() {
@Override
public void run() {
for (ChangeInfo change : changes) {
animateChangeImpl(change);
}
changes.clear();
mChangesList.remove(changes);
}
};
if (removalsPending) {
RecyclerView.ViewHolder holder = changes.get(0).oldHolder;
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
} else {
changer.run();
}
}
// Next, add stuff
if (additionsPending) {
final ArrayList<RecyclerView.ViewHolder> additions = new ArrayList<>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear();
Runnable adder = new Runnable() {
@Override
public void run() {
for (RecyclerView.ViewHolder holder : additions) {
animateAddImpl(holder);
}
additions.clear();
mAdditionsList.remove(additions);
}
};
if (removalsPending || movesPending || changesPending) {
long removeDuration = removalsPending ? getRemoveDuration() : 0;
long moveDuration = movesPending ? getMoveDuration() : 0;
long changeDuration = changesPending ? getChangeDuration() : 0;
long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
View view = additions.get(0).itemView;
ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
} else {
adder.run();
}
}
}
@Override
public boolean animateRemove(final RecyclerView.ViewHolder holder) {
resetAnimation(holder);
mPendingRemovals.add(holder);
return true;
}
private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mRemoveAnimations.add(holder);
animation.setDuration(getRemoveDuration()).alpha(0).setListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchRemoveStarting(holder);
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
view.setAlpha(1);
dispatchRemoveFinished(holder);
mRemoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
@Override
public boolean animateAdd(final RecyclerView.ViewHolder holder) {
resetAnimation(holder);
holder.itemView.setAlpha(0);
mPendingAdditions.add(holder);
return true;
}
void animateAddImpl(final RecyclerView.ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mAddAnimations.add(holder);
animation.alpha(1).setDuration(getAddDuration())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchAddStarting(holder);
}
@Override
public void onAnimationCancel(Animator animator) {
view.setAlpha(1);
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
dispatchAddFinished(holder);
mAddAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
@Override
public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
int toX, int toY) {
final View view = holder.itemView;
fromX += (int) holder.itemView.getTranslationX();
fromY += (int) holder.itemView.getTranslationY();
resetAnimation(holder);
int deltaX = toX - fromX;
int deltaY = toY - fromY;
if (deltaX == 0 && deltaY == 0) {
dispatchMoveFinished(holder);
return false;
}
if (deltaX != 0) {
view.setTranslationX(-deltaX);
}
if (deltaY != 0) {
view.setTranslationY(-deltaY);
}
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
return true;
}
void animateMoveImpl(final RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
final View view = holder.itemView;
final int deltaX = toX - fromX;
final int deltaY = toY - fromY;
if (deltaX != 0) {
view.animate().translationX(0);
}
if (deltaY != 0) {
view.animate().translationY(0);
}
// TODO: make EndActions end listeners instead, since end actions aren't called when
// vpas are canceled (and can't end them. why?)
// need listener functionality in VPACompat for this. Ick.
final ViewPropertyAnimator animation = view.animate();
mMoveAnimations.add(holder);
animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchMoveStarting(holder);
}
@Override
public void onAnimationCancel(Animator animator) {
if (deltaX != 0) {
view.setTranslationX(0);
}
if (deltaY != 0) {
view.setTranslationY(0);
}
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
dispatchMoveFinished(holder);
mMoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,
int fromX, int fromY, int toX, int toY) {
if (oldHolder == newHolder) {
// Don't know how to run change animations when the same view holder is re-used.
// run a move animation to handle position changes.
return animateMove(oldHolder, fromX, fromY, toX, toY);
}
final float prevTranslationX = oldHolder.itemView.getTranslationX();
final float prevTranslationY = oldHolder.itemView.getTranslationY();
final float prevAlpha = oldHolder.itemView.getAlpha();
resetAnimation(oldHolder);
int deltaX = (int) (toX - fromX - prevTranslationX);
int deltaY = (int) (toY - fromY - prevTranslationY);
// recover prev translation state after ending animation
oldHolder.itemView.setTranslationX(prevTranslationX);
oldHolder.itemView.setTranslationY(prevTranslationY);
oldHolder.itemView.setAlpha(prevAlpha);
if (newHolder != null) {
// carry over translation values
resetAnimation(newHolder);
newHolder.itemView.setTranslationX(-deltaX);
newHolder.itemView.setTranslationY(-deltaY);
newHolder.itemView.setAlpha(0);
}
mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
return true;
}
void animateChangeImpl(final ChangeInfo changeInfo) {
final RecyclerView.ViewHolder holder = changeInfo.oldHolder;
final View view = holder == null ? null : holder.itemView;
final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;
final View newView = newHolder != null ? newHolder.itemView : null;
if (view != null) {
final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
getChangeDuration());
mChangeAnimations.add(changeInfo.oldHolder);
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
oldViewAnim.alpha(0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchChangeStarting(changeInfo.oldHolder, true);
}
@Override
public void onAnimationEnd(Animator animator) {
oldViewAnim.setListener(null);
view.setAlpha(1);
view.setTranslationX(0);
view.setTranslationY(0);
dispatchChangeFinished(changeInfo.oldHolder, true);
mChangeAnimations.remove(changeInfo.oldHolder);
dispatchFinishedWhenDone();
}
}).start();
}
if (newView != null) {
final ViewPropertyAnimator newViewAnimation = newView.animate();
mChangeAnimations.add(changeInfo.newHolder);
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
.alpha(1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchChangeStarting(changeInfo.newHolder, false);
}
@Override
public void onAnimationEnd(Animator animator) {
newViewAnimation.setListener(null);
newView.setAlpha(1);
newView.setTranslationX(0);
newView.setTranslationY(0);
dispatchChangeFinished(changeInfo.newHolder, false);
mChangeAnimations.remove(changeInfo.newHolder);
dispatchFinishedWhenDone();
}
}).start();
}
}
private void endChangeAnimation(List<ChangeInfo> infoList, RecyclerView.ViewHolder item) {
for (int i = infoList.size() - 1; i >= 0; i--) {
ChangeInfo changeInfo = infoList.get(i);
if (endChangeAnimationIfNecessary(changeInfo, item)) {
if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
infoList.remove(changeInfo);
}
}
}
}
private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
if (changeInfo.oldHolder != null) {
endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
}
if (changeInfo.newHolder != null) {
endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
}
}
private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerView.ViewHolder item) {
boolean oldItem = false;
if (changeInfo.newHolder == item) {
changeInfo.newHolder = null;
} else if (changeInfo.oldHolder == item) {
changeInfo.oldHolder = null;
oldItem = true;
} else {
return false;
}
item.itemView.setAlpha(1);
item.itemView.setTranslationX(0);
item.itemView.setTranslationY(0);
dispatchChangeFinished(item, oldItem);
return true;
}
@Override
public void endAnimation(RecyclerView.ViewHolder item) {
final View view = item.itemView;
// this will trigger end callback which should set properties to their target values.
view.animate().cancel();
// TODO if some other animations are chained to end, how do we cancel them as well?
for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
MoveInfo moveInfo = mPendingMoves.get(i);
if (moveInfo.holder == item) {
view.setTranslationY(0);
view.setTranslationX(0);
dispatchMoveFinished(item);
mPendingMoves.remove(i);
}
}
endChangeAnimation(mPendingChanges, item);
if (mPendingRemovals.remove(item)) {
view.setAlpha(1);
dispatchRemoveFinished(item);
}
if (mPendingAdditions.remove(item)) {
view.setAlpha(1);
dispatchAddFinished(item);
}
for (int i = mChangesList.size() - 1; i >= 0; i--) {
ArrayList<ChangeInfo> changes = mChangesList.get(i);
endChangeAnimation(changes, item);
if (changes.isEmpty()) {
mChangesList.remove(i);
}
}
for (int i = mMovesList.size() - 1; i >= 0; i--) {
ArrayList<MoveInfo> moves = mMovesList.get(i);
for (int j = moves.size() - 1; j >= 0; j--) {
MoveInfo moveInfo = moves.get(j);
if (moveInfo.holder == item) {
view.setTranslationY(0);
view.setTranslationX(0);
dispatchMoveFinished(item);
moves.remove(j);
if (moves.isEmpty()) {
mMovesList.remove(i);
}
break;
}
}
}
for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
if (additions.remove(item)) {
view.setAlpha(1);
dispatchAddFinished(item);
if (additions.isEmpty()) {
mAdditionsList.remove(i);
}
}
}
// animations should be ended by the cancel above.
//noinspection PointlessBooleanExpression,ConstantConditions
if (mRemoveAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mRemoveAnimations list");
}
//noinspection PointlessBooleanExpression,ConstantConditions
if (mAddAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mAddAnimations list");
}
//noinspection PointlessBooleanExpression,ConstantConditions
if (mChangeAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mChangeAnimations list");
}
//noinspection PointlessBooleanExpression,ConstantConditions
if (mMoveAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mMoveAnimations list");
}
dispatchFinishedWhenDone();
}
private void resetAnimation(RecyclerView.ViewHolder holder) {
if (sDefaultInterpolator == null) {
sDefaultInterpolator = new ValueAnimator().getInterpolator();
}
holder.itemView.animate().setInterpolator(sDefaultInterpolator);
endAnimation(holder);
}
@Override
public boolean isRunning() {
return (!mPendingAdditions.isEmpty()
|| !mPendingChanges.isEmpty()
|| !mPendingMoves.isEmpty()
|| !mPendingRemovals.isEmpty()
|| !mMoveAnimations.isEmpty()
|| !mRemoveAnimations.isEmpty()
|| !mAddAnimations.isEmpty()
|| !mChangeAnimations.isEmpty()
|| !mMovesList.isEmpty()
|| !mAdditionsList.isEmpty()
|| !mChangesList.isEmpty());
}
/**
* Check the state of currently pending and running animations. If there are none
* pending/running, call {@link #dispatchAnimationsFinished()} to notify any
* listeners.
*/
void dispatchFinishedWhenDone() {
if (!isRunning()) {
dispatchAnimationsFinished();
}
}
@Override
public void endAnimations() {
int count = mPendingMoves.size();
for (int i = count - 1; i >= 0; i--) {
MoveInfo item = mPendingMoves.get(i);
View view = item.holder.itemView;
view.setTranslationY(0);
view.setTranslationX(0);
dispatchMoveFinished(item.holder);
mPendingMoves.remove(i);
}
count = mPendingRemovals.size();
for (int i = count - 1; i >= 0; i--) {
RecyclerView.ViewHolder item = mPendingRemovals.get(i);
dispatchRemoveFinished(item);
mPendingRemovals.remove(i);
}
count = mPendingAdditions.size();
for (int i = count - 1; i >= 0; i--) {
RecyclerView.ViewHolder item = mPendingAdditions.get(i);
item.itemView.setAlpha(1);
dispatchAddFinished(item);
mPendingAdditions.remove(i);
}
count = mPendingChanges.size();
for (int i = count - 1; i >= 0; i--) {
endChangeAnimationIfNecessary(mPendingChanges.get(i));
}
mPendingChanges.clear();
if (!isRunning()) {
return;
}
int listCount = mMovesList.size();
for (int i = listCount - 1; i >= 0; i--) {
ArrayList<MoveInfo> moves = mMovesList.get(i);
count = moves.size();
for (int j = count - 1; j >= 0; j--) {
MoveInfo moveInfo = moves.get(j);
RecyclerView.ViewHolder item = moveInfo.holder;
View view = item.itemView;
view.setTranslationY(0);
view.setTranslationX(0);
dispatchMoveFinished(moveInfo.holder);
moves.remove(j);
if (moves.isEmpty()) {
mMovesList.remove(moves);
}
}
}
listCount = mAdditionsList.size();
for (int i = listCount - 1; i >= 0; i--) {
ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
count = additions.size();
for (int j = count - 1; j >= 0; j--) {
RecyclerView.ViewHolder item = additions.get(j);
View view = item.itemView;
view.setAlpha(1);
dispatchAddFinished(item);
additions.remove(j);
if (additions.isEmpty()) {
mAdditionsList.remove(additions);
}
}
}
listCount = mChangesList.size();
for (int i = listCount - 1; i >= 0; i--) {
ArrayList<ChangeInfo> changes = mChangesList.get(i);
count = changes.size();
for (int j = count - 1; j >= 0; j--) {
endChangeAnimationIfNecessary(changes.get(j));
if (changes.isEmpty()) {
mChangesList.remove(changes);
}
}
}
cancelAll(mRemoveAnimations);
cancelAll(mMoveAnimations);
cancelAll(mAddAnimations);
cancelAll(mChangeAnimations);
dispatchAnimationsFinished();
}
void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {
for (int i = viewHolders.size() - 1; i >= 0; i--) {
viewHolders.get(i).itemView.animate().cancel();
}
}
/**
* {@inheritDoc}
* <p>
* If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
* When this is the case:
* <ul>
* <li>If you override {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)}, both
* ViewHolder arguments will be the same instance.
* </li>
* <li>
* If you are not overriding {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)},
* then DefaultItemAnimator will call {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int)} and
* run a move animation instead.
* </li>
* </ul>
*/
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull List<Object> payloads) {
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
}
}

View File

@@ -0,0 +1,175 @@
package com.mogo.och.bus.ui.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mogo.och.bus.R
import com.mogo.och.bus.bean.BusQueryLinesResponse
import com.mogo.och.bus.ui.adapter.SwitchLineAdapter.SwitchLineViewHolder
import kotlin.text.StringBuilder
/**
* 路线列表adapter
*/
class SwitchLineAdapter(
private val mContext: Context,
private val mData: List<BusQueryLinesResponse.Result>
) : RecyclerView.Adapter<SwitchLineViewHolder>() {
companion object{
const val TAG = "SwitchLineAdapter"
}
// RecyclerView设置点击事件
private var mItemClickListener: LineItemClickListener? = null
var checkLineId:Int = -1
var checkTaskId:Int = -1
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): SwitchLineViewHolder {
val view = LayoutInflater.from(mContext).inflate(
R.layout.bus_switch_line_list_item, parent, false
)
return SwitchLineViewHolder(view)
}
override fun onBindViewHolder(holder: SwitchLineViewHolder, position: Int) {
val currentPosition = holder.bindingAdapterPosition
val line = mData[currentPosition]
if(line.name!=null&&line.name.length>10){
line.name = line.name.substring(0,10)+""
}
holder.lineName.text = line.name
val sb = StringBuilder()
if (line.endSiteName!=null&&line.endSiteName.length>10) {
sb.append(line.endSiteName.substring(0,5))
sb.append("")
sb.append(line.endSiteName.substring(line.endSiteName.length-5,line.endSiteName.length))
}else{
sb.append(line.endSiteName)
}
val string = mContext.getString(R.string.bus_line_goto_end, sb.toString())
holder.lineEndName.text = string
holder.rvLineTask.layoutManager = GridLayoutManager(mContext, 3)
val switchLineTaskAdapter = SwitchLineTaskAdapter(
mContext,
checkTaskId,
line.taskList,
object : SwitchLineTaskAdapter.TaskItemClickListener {
override fun onItemClick(position: Int,isCheck:Boolean) {
if(isCheck) {
checkLineId = line.lineId
checkTaskId = line.taskList[position].id
}else{
checkLineId=-1
checkTaskId=-1
}
}
})
holder.rvLineTask.adapter = switchLineTaskAdapter
holder.rvLineTask.isFocusableInTouchMode = false
holder.actvShowMore.visibility = View.VISIBLE
if(line.open){
if(line.taskList==null||line.taskList.isEmpty()){
holder.actvShowMore.text = mContext.getString(R.string.bus_switch_line_no_task)
holder.actvShowMore.setTextColor(ContextCompat.getColor(mContext,R.color.bus_color_4dffffff))
holder.vLineTask.visibility = View.GONE
holder.rvLineTask.visibility = View.GONE
holder.selectIv.visibility = View.INVISIBLE
holder.itemView.setBackgroundResource(R.drawable.bus_shape_select_line_item_bg_normal)
}else {
holder.actvShowMore.text = mContext.getString(R.string.bus_switch_line_select_task)
holder.actvShowMore.setTextColor(ContextCompat.getColor(mContext,android.R.color.white))
holder.vLineTask.visibility = View.VISIBLE
holder.rvLineTask.visibility = View.VISIBLE
holder.selectIv.visibility = View.VISIBLE
holder.itemView.setBackgroundResource(R.drawable.bus_shape_select_line_item_bg_selected)
holder.selectIv.apply {
pivotX = 9.5f
pivotY = 17f
rotation = 90f
}
}
}else{
if(line.haveTask){
holder.actvShowMore.text = mContext.getString(R.string.bus_switch_line_no_task)
holder.actvShowMore.setTextColor(ContextCompat.getColor(mContext,R.color.bus_color_4dffffff))
holder.vLineTask.visibility = View.GONE
holder.rvLineTask.visibility = View.GONE
holder.selectIv.visibility = View.INVISIBLE
holder.itemView.setBackgroundResource(R.drawable.bus_shape_select_line_item_bg_normal)
}else {
holder.actvShowMore.text = mContext.getString(R.string.bus_switch_line_select_task)
holder.actvShowMore.setTextColor(ContextCompat.getColor(mContext,android.R.color.white))
holder.vLineTask.visibility = View.GONE
holder.rvLineTask.visibility = View.GONE
holder.selectIv.visibility = View.VISIBLE
holder.itemView.setBackgroundResource(R.drawable.bus_shape_select_line_item_bg_normal)
holder.selectIv.apply {
pivotX = 9.5f
pivotY = 17f
rotation = 0f
}
}
}
//设置item点击事件
holder.itemView.setOnClickListener {
if(holder.actvShowMore.text==mContext.getString(R.string.bus_switch_line_no_task)){
return@setOnClickListener
}
mData.forEachIndexed { index, result ->
if(result.open){
result.open = false;
notifyItemChanged(index)
if(result.taskList!=null) {
result.taskList.clear()
}
if(index==currentPosition){// 点击当前已经打开的item 去关闭定时网络请求
mItemClickListener?.onItemClick(currentPosition,true)
return@setOnClickListener
}
}
}
mItemClickListener?.onItemClick(currentPosition,false)
line.open = holder.rvLineTask.visibility == View.GONE
}
}
override fun getItemCount(): Int {
return mData.size
}
fun setOnLineItemClickListener(itemClickListener: LineItemClickListener?) {
mItemClickListener = itemClickListener
}
class SwitchLineViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val selectIv: ImageView
val lineName: AppCompatTextView//线路名称
val lineEndName: AppCompatTextView //终点
val actvShowMore: AppCompatTextView //选择时间
val rvLineTask: RecyclerView// 排班时间
val vLineTask: View// 白色分割线
init {
selectIv = itemView.findViewById(R.id.switch_line_item_select_iv)
lineName = itemView.findViewById(R.id.switch_line_name)
lineEndName = itemView.findViewById(R.id.switch_line_end_station)
rvLineTask = itemView.findViewById(R.id.rv_line_task_list)
vLineTask = itemView.findViewById(R.id.v_line_task)
actvShowMore = itemView.findViewById(R.id.actv_show_more)
}
}
interface LineItemClickListener {
fun onItemClick(position: Int,close:Boolean)
}
}

View File

@@ -0,0 +1,76 @@
package com.mogo.och.bus.ui.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.mogo.eagle.core.utilcode.util.TimeUtils
import com.mogo.och.bus.R
import com.mogo.och.bus.bean.BusQueryLineTaskResponse
import com.mogo.och.bus.ui.adapter.SwitchLineTaskAdapter.SwitchLineTaskViewHolder
/**
* 路线列表adapter
*/
class SwitchLineTaskAdapter(
private val mContext: Context,
private var checkTaskId:Int,
private val mData: List<BusQueryLineTaskResponse.Result>?,
private val mTaskItemClickListener: TaskItemClickListener?
) : RecyclerView.Adapter<SwitchLineTaskViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SwitchLineTaskViewHolder {
val view = LayoutInflater.from(mContext).inflate(
R.layout.bus_switch_line_list_task_item, parent, false
)
return SwitchLineTaskViewHolder(view)
}
override fun onBindViewHolder(holder: SwitchLineTaskViewHolder, position: Int) {
val currentPosition = holder.bindingAdapterPosition
val task = mData!![currentPosition]
val taskStartTime = TimeUtils.millis2String(task.taskStartTime, "HH:mm")
holder.lineTask.text = taskStartTime
if(checkTaskId==task.id){
holder.lineTask.setBackgroundResource(R.drawable.bus_shape_select_line_item_time_bg_selected)
}else{
holder.lineTask.setBackgroundResource(R.drawable.bus_shape_select_line_item_time_bg)
}
holder.lineTask.setOnClickListener {
if(checkTaskId==task.id){
checkTaskId = -1
mTaskItemClickListener?.onItemClick(currentPosition,false)
}else {
resetOther()
checkTaskId = task.id
mTaskItemClickListener?.onItemClick(currentPosition,true)
}
notifyItemChanged(currentPosition)
}
}
private fun resetOther() {
mData?.forEachIndexed { index, result ->
if(result.id==checkTaskId){
checkTaskId = -1
notifyItemChanged(index)
}
}
}
override fun getItemCount(): Int {
return mData?.size ?: 0
}
class SwitchLineTaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val lineTask: TextView // 时间
init {
lineTask = itemView.findViewById(R.id.actv_line_task)
}
}
interface TaskItemClickListener {
fun onItemClick(position: Int,isCheck:Boolean)
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,146 @@
package com.mogo.och.bus.util;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS;
import android.text.TextUtils;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.eagle.core.data.app.AppConfigInfo;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.commons.utils.MogoAnalyticUtils;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.DateTimeUtils;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.bus.constant.BusConst;
import java.util.HashMap;
/**
* OCH Bus埋点工具
*
* Created on 2022/3/24
*/
public class BusAnalyticsManager {
private static final class SingletonHolder {
private static final BusAnalyticsManager INSTANCE = new BusAnalyticsManager();
}
public static BusAnalyticsManager getInstance() {
return BusAnalyticsManager.SingletonHolder.INSTANCE;
}
private String mStartAutopilotKey;
private HashMap<String, Object> mStartAutopilotParams = new HashMap<>();
private Runnable startAutopilotRunnable = () -> {
// 15s内未开启上报失败埋点
triggerStartAutopilotFailureEvent("", "15s后app等待超时");
};
public void triggerStartAutopilotFailureEventByAdas(String failCode, String failMsg){
removeWaitingCallback();
triggerStartAutopilotFailureEvent(failCode, failMsg);
}
private void triggerStartAutopilotFailureEvent(String failCode, String failMsg){
if (mStartAutopilotParams.isEmpty()) return;
CallerLogger.INSTANCE.e( M_BUS + "triggerStartAutopilotFailureEvent", failMsg );
if (CallerAutoPilotStatusListenerManager.INSTANCE.getState() !=
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING){
mStartAutopilotParams.put(BusConst.EVENT_PARAM_START_FAILURE_CODE, failCode);
mStartAutopilotParams.put(BusConst.EVENT_PARAM_START_FAILURE_MSG, failMsg);
}
mStartAutopilotParams.put(BusConst.EVENT_PARAM_START_RESULT
, CallerAutoPilotStatusListenerManager.INSTANCE.getState() ==
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING);
MogoAnalyticUtils.INSTANCE.track(mStartAutopilotKey, mStartAutopilotParams);
clearStartAutopilotParams();//清空参数数据,防止误传
}
private void removeWaitingCallback() {
if (startAutopilotRunnable != null) {
UiThreadHandler.removeCallbacks(startAutopilotRunnable);
}
}
public void clearStartAutopilotFailureMSG(){
mStartAutopilotParams.put(BusConst.EVENT_PARAM_START_FAILURE_CODE, "");
mStartAutopilotParams.put(BusConst.EVENT_PARAM_START_FAILURE_MSG, "");
}
private void clearStartAutopilotParams(){
mStartAutopilotParams.clear();
}
/**
* 触发'开启自动驾驶'埋点流程
* 开启自动驾驶15s内成功则发送成功埋点否则发送失败埋点
* @param restart false点击'滑动出发'启动)/true接管后点击'自动驾驶'按钮启动)
* @param send 是否直接发送埋点15s内开启成功则直接发送成功埋点
*/
public void triggerStartAutopilotEvent(
boolean restart, boolean send, String startName, String endName, int lineId) {
mStartAutopilotKey = restart ?
BusConst.EVENT_KEY_RESTART_AUTOPILOT : BusConst.EVENT_KEY_START_SERVICE;
String sn = MoGoAiCloudClientConfig.getInstance().getSn();
String plateNum = AppConfigInfo.INSTANCE.getPlateNumber();
String dateTime = DateTimeUtils.getTimeText(
System.currentTimeMillis(), DateTimeUtils.yyyy_MM_dd_HH_mm_ss);
mStartAutopilotParams.put(BusConst.EVENT_PARAM_SN, sn);
mStartAutopilotParams.put(BusConst.EVENT_PARAM_PLATE_NUM, TextUtils.isEmpty(plateNum) ? "" : plateNum);
mStartAutopilotParams.put(BusConst.EVENT_PARAM_ENV_ONLINE,
DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE ? true : false);
mStartAutopilotParams.put(BusConst.EVENT_PARAM_TIME, dateTime);
mStartAutopilotParams.put(BusConst.EVENT_PARAM_START_NAME, startName);
mStartAutopilotParams.put(BusConst.EVENT_PARAM_END_NAME, endName);
mStartAutopilotParams.put(BusConst.EVENT_PARAM_LINE_ID, lineId);
if (send) {
if (mStartAutopilotParams.isEmpty()) return;
// 开启成功,上报埋点
clearStartAutopilotFailureMSG();
removeWaitingCallback();
mStartAutopilotParams.put(BusConst.EVENT_PARAM_START_RESULT, true);
MogoAnalyticUtils.INSTANCE.track(mStartAutopilotKey, mStartAutopilotParams);
clearStartAutopilotParams();//清空参数数据,防止误传
} else {
UiThreadHandler.postDelayed(startAutopilotRunnable, BusConst.LOOP_PERIOD_15S);
}
}
/**
* 触发"无法开启自驾已知异常"埋点
* @param startName
* @param endName
* @param lineId
*/
public void triggerUnableStartAPReasonEvent(String startName, String endName, int lineId,
String reason) {
String sn = MoGoAiCloudClientConfig.getInstance().getSn();
String plateNum = AppConfigInfo.INSTANCE.getPlateNumber();
String dateTime = DateTimeUtils.getTimeText(
System.currentTimeMillis(), DateTimeUtils.yyyy_MM_dd_HH_mm_ss);
HashMap<String, Object> params = new HashMap<>();
params.put(BusConst.EVENT_PARAM_SN, sn);
params.put(BusConst.EVENT_PARAM_PLATE_NUM, TextUtils.isEmpty(plateNum) ? "" : plateNum);
params.put(BusConst.EVENT_PARAM_ENV_ONLINE,
DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE ? true : false);
params.put(BusConst.EVENT_PARAM_TIME, dateTime);
params.put(BusConst.EVENT_PARAM_START_NAME, startName);
params.put(BusConst.EVENT_PARAM_END_NAME, endName);
params.put(BusConst.EVENT_PARAM_LINE_ID, lineId);
params.put(BusConst.EVENT_PARAM_UNABLE_START_REASON, reason);
MogoAnalyticUtils.INSTANCE.track(BusConst.EVENT_KEY_AP_UNABLE_START_REASON, params);
}
}

View File

@@ -0,0 +1,36 @@
package com.mogo.och.bus.util
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
/**
* @author: wangmingjun
* @date: 2022/10/24
*/
object BusSendTripInfoManager{
const val START_TRIP = 1
const val END_TRIP = 2
const val LEAVE_STATION = 3
const val ARRIVE_STATION = 4
/**
* 行程信息
* @param type 事件类型, 1:行程开始, 2:行程结束, 3:出站, 4:进站, 5:城市占道施工预警
* @param lineName 路线名, for type 1, 2
* @param departureStopName 出站站点名, for type 3, 4
* @param arrivalStopName 下一站到达站点名, for type 3, 4
* @param isLastStop 是否终点站(下一站或者要到达站)
* @return
*/
fun sendBusTripInfo(type: Int, lineName: String,
departureStopName: String,
arrivalStopName: String,
isLastStop: Boolean) {
d(SceneConstant.M_BUS + "BusSendTripInfoManager", "type: "+ type
+", lineName: "+ lineName +", departureStopName: "+ departureStopName
+ ", arrivalStopName: "+arrivalStopName+", isLastStop: "+isLastStop)
CallerAutoPilotControlManager.sendTripInfo(type,lineName,departureStopName, arrivalStopName, isLastStop)
}
}

View File

@@ -0,0 +1,193 @@
package com.mogo.och.bus.util;
import androidx.annotation.Nullable;
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.GsonUtils;
import com.mogo.och.data.bean.BusRoutesResult;
import com.mogo.och.bus.constant.BusConst;
import com.mogo.och.bus.model.OrderModel;
import com.mogo.och.common.module.biz.constant.LoginStatusManager;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import mogo_msg.MogoReportMsg;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS;
/**
* Bus轨迹管理给MEC下发用于轨迹下载的信息
* Created on 2022/6/23
*/
public class BusTrajectoryManager {
private static final String TAG = BusTrajectoryManager.class.getSimpleName();
private static final class SingletonHolder {
private static final BusTrajectoryManager INSTANCE = new BusTrajectoryManager();
}
public static BusTrajectoryManager getInstance() {
return BusTrajectoryManager.SingletonHolder.INSTANCE;
}
private AutopilotControlParameters.AutoPilotLine mAutoPilotLine = null;
private Disposable mSendReqDisposable = null;
public BusTrajectoryManager() {
mAutoPilotLine = new AutopilotControlParameters.AutoPilotLine(-1, "",
"", "", "", "", 0, "",
"", "", "", "", 0);
}
/**
* 同步Bus路线信息
*/
public void syncTrajectoryInfo() {
BusRoutesResult routesResult = OrderModel.getInstance().getBusRoutesResult();
if (LoginStatusManager.isLogin() && routesResult != null
&& OrderModel.getInstance().getCurrentStationIndex() == 0
&& !OrderModel.getInstance().isGoingToNextStation()) {
CallerLogger.INSTANCE.d(M_BUS + TAG, "syncTrajectoryInfo() start.");
startTrajReqLoop();
} else {
// 无路线信息or当前未在始发站
CallerLogger.INSTANCE.d(M_BUS + TAG, "syncTrajectoryInfo() stop.");
stopTrajReqLoop();
}
}
/**
* 接口MEC反馈的常规信息MAP v2.5.0新增轨迹相关信息)
* @param guardianInfo
*/
public void onAutopilotGuardian(@Nullable MogoReportMsg.MogoReportMessage guardianInfo) {
if (guardianInfo == null || !guardianInfo.hasCode()) return;
if ("ISYS_INIT_TRAJECTORY_START".equals(guardianInfo.getCode())) {
// 1. 轨迹管理_轨迹开始下载本地已有对应轨迹也触发
CallerLogger.INSTANCE.d(M_BUS + TAG, "onAutopilotGuardian() 轨迹开始下载");
// ToastUtils.showShort("轨迹开始下载");
stopTrajReqLoop();
} else if ("ISYS_INIT_TRAJECTORY_SUCCESS".equals(guardianInfo.getCode())) {
// 2. 轨迹管理_轨迹下载成功本地已有对应轨迹也触发
CallerLogger.INSTANCE.d(M_BUS + TAG, "onAutopilotGuardian() 轨迹下载成功");
// ToastUtils.showShort("轨迹下载成功");
stopTrajReqLoop();
} else if ("ISYS_INIT_TRAJECTORY_FAILURE".equals(guardianInfo.getCode())) {
// 3. 轨迹管理_轨迹下载失败本地无对应轨迹
CallerLogger.INSTANCE.d(M_BUS + TAG, "onAutopilotGuardian() " +
"轨迹下载失败,本地无对应轨迹");
// ToastUtils.showShort("轨迹下载失败,本地无对应轨迹");
} else if ("ISYS_INIT_TRAJECTORY_WARNING".equals(guardianInfo.getCode())) {
// 4. 轨迹管理_轨迹下载失败本地有对应轨迹认为成功
CallerLogger.INSTANCE.d(M_BUS + TAG, "onAutopilotGuardian() " +
"轨迹下载失败,本地有对应轨迹,认为成功");
// ToastUtils.showShort("轨迹下载失败,本地有对应轨迹,认为成功");
} else if ("ISYS_INIT_TRAJECTORY_TIMEOUT".equals(guardianInfo.getCode())) {
// 5. 轨迹管理_轨迹下载超时
CallerLogger.INSTANCE.d(M_BUS + TAG, "onAutopilotGuardian() 轨迹下载超时");
// ToastUtils.showShort("轨迹下载超时");
} else if ("ISSM_FUNC_AUTO_PILOT_READY".equals(guardianInfo.getCode())) {
// 收到ssm的自动驾驶变为ready再次下发轨迹下载.解决域控重启或者102域控启动太早107节点初始化未完成导致的轨迹未进行下载。
CallerLogger.INSTANCE.d(M_BUS + TAG, "onAutopilotGuardian() ssm ready再次发起下载");
syncTrajectoryInfo();
}
}
private void setupAutoPilotLine() {
BusRoutesResult routesResult = OrderModel.getInstance().getBusRoutesResult();
if (routesResult == null) {
CallerLogger.INSTANCE.e(M_BUS + TAG,
"setupAutoPilotLine(): routesResult is null.");
return;
} else {
if (mAutoPilotLine == null) {
mAutoPilotLine = new AutopilotControlParameters.AutoPilotLine(
routesResult.getLineId(), routesResult.getName(),
routesResult.csvFileUrl, routesResult.csvFileMd5,
routesResult.txtFileUrl, routesResult.txtFileMd5,
routesResult.contrailSaveTime, routesResult.carModel,
routesResult.csvFileUrlDPQP, routesResult.csvFileMd5DPQP,
routesResult.txtFileUrlDPQP, routesResult.txtFileMd5DPQP,
routesResult.contrailSaveTimeDPQP);
} else {
mAutoPilotLine.setLineId(routesResult.getLineId());
mAutoPilotLine.setLineName(routesResult.getName());
mAutoPilotLine.setTrajUrl(routesResult.csvFileUrl);
mAutoPilotLine.setTrajMd5(routesResult.csvFileMd5);
mAutoPilotLine.setStopUrl(routesResult.txtFileUrl);
mAutoPilotLine.setStopMd5(routesResult.txtFileMd5);
mAutoPilotLine.setTimestamp(routesResult.contrailSaveTime);
mAutoPilotLine.setVehicleModel(routesResult.carModel);
mAutoPilotLine.setTrajUrl_dpqp(routesResult.csvFileUrlDPQP);
mAutoPilotLine.setTrajMd5_dpqp(routesResult.csvFileMd5DPQP);
mAutoPilotLine.setStopUrl_dpqp(routesResult.txtFileUrlDPQP);
mAutoPilotLine.setStopMd5_dpqp(routesResult.txtFileMd5DPQP);
mAutoPilotLine.setTimestamp_dpqp(routesResult.contrailSaveTimeDPQP);
}
}
}
private void clearAutoPilotLine() {
if (mAutoPilotLine == null) return;
mAutoPilotLine.setLineId(-1);
mAutoPilotLine.setLineName("");
mAutoPilotLine.setTrajUrl("");
mAutoPilotLine.setTrajMd5("");
mAutoPilotLine.setStopUrl("");
mAutoPilotLine.setStopMd5("");
mAutoPilotLine.setTimestamp(0);
mAutoPilotLine.setVehicleModel("");
mAutoPilotLine.setTrajUrl_dpqp("");
mAutoPilotLine.setTrajMd5_dpqp("");
mAutoPilotLine.setStopUrl_dpqp("");
mAutoPilotLine.setStopMd5_dpqp("");
mAutoPilotLine.setTimestamp_dpqp(0);
}
private void startTrajReqLoop() {
if (mSendReqDisposable != null && !mSendReqDisposable.isDisposed()) {
return;
}
CallerLogger.INSTANCE.d(M_BUS + TAG, "startTrajReqLoop()");
setupAutoPilotLine();
mSendReqDisposable = Observable.interval(BusConst.LOOP_DELAY,
BusConst.LOOP_PERIOD_10S, TimeUnit.MILLISECONDS)
.map((aLong -> aLong + 1))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> {
if (aLong > BusConst.LOOP_SEND_TRAJ_TIMES) {
stopTrajReqLoop();
return;
}
CallerLogger.INSTANCE.d(M_BUS + TAG, "loop sendTrajectoryReq: " + aLong);
sendTrajectoryReq();
});
}
public void stopTrajReqLoop() {
if (mSendReqDisposable != null) {
CallerLogger.INSTANCE.d(M_BUS + TAG, "stopTrajReqLoop()");
mSendReqDisposable.dispose();
mSendReqDisposable = null;
clearAutoPilotLine();
}
}
private void sendTrajectoryReq() {
if (mAutoPilotLine == null) {
CallerLogger.INSTANCE.e(M_BUS + TAG, "sendTrajectoryReq(): mAutoPilotLine is null!!!");
return;
}
CallerAutoPilotControlManager.INSTANCE.sendTrajectoryDownloadReq(mAutoPilotLine);
CallerLogger.INSTANCE.d(M_BUS + TAG, "sendTrajectoryReq(): "
+ GsonUtils.toJson(mAutoPilotLine));
}
}

View File

@@ -0,0 +1,190 @@
package com.mogo.och.bus.view;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import androidx.annotation.Nullable;
import com.mogo.och.bus.R;
/**
* created by wujifei on 2021/3/24 16:20
* describe:
*/
public class BusArcView extends View {
//中心的文字描述
private String mDes = "KM/H";
//根据数据显示的圆弧Paint
private Paint mArcPaint;
//圆弧颜色
private int mArcColor;
//圆弧的画笔的宽度
private float mStrokeWith = getResources().getDimension(R.dimen.bus_ext_arcView_stroke_with);
//文字描述的paint
private Paint mTextPaint;
//当前进度夹角大小
private float mIncludedAngle = 0;
//当前数据
private int currentValue;
//最大数据
private int maxValue = 240;
//圆弧背景的开始和结束间的夹角大小
private float mAngle = 270;
//上次绘制圆弧夹角
private float lastAngle = 0;
public BusArcView(Context context) {
this(context, null);
}
public BusArcView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BusArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//初始化paint
initPaint();
//绘制弧度
drawArc(canvas);
//绘制文本
drawText(canvas);
}
private void drawText(Canvas canvas) {
Rect mRect = new Rect();
String mValue = String.valueOf(currentValue);
mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
//绘制中心的数值
mTextPaint.getTextBounds(mValue, 0, mValue.length(), mRect);
canvas.drawText(mValue, getWidth() / 2, getHeight() / 2 + mRect.height() / 2 - 10, mTextPaint);
mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
//绘制中心文字描述
mTextPaint.setTextSize(getResources().getDimension(R.dimen.bus_ext_arcView_des_text_size));
mTextPaint.getTextBounds(mDes, 0, mDes.length(), mRect);
canvas.drawText(mDes, getWidth() / 2, getHeight() * 17 / 20 + mRect.height() / 2, mTextPaint);
}
private void drawArc(Canvas canvas) {
//绘制圆弧背景
RectF mRectF = new RectF(mStrokeWith, mStrokeWith, getWidth() - mStrokeWith, getHeight() - mStrokeWith);
canvas.drawArc(mRectF, 135, mAngle, false, mArcPaint);
//绘制当前数值对应的圆弧
mArcPaint.setColor(mArcColor);
//根据当前数据绘制对应的圆弧
canvas.drawArc(mRectF, 135, mIncludedAngle, false, mArcPaint);
}
private void initPaint() {
//圆弧的paint
mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//抗锯齿
mArcPaint.setAntiAlias(true);
mArcPaint.setColor(Color.parseColor("#151D4C"));
//设置透明度数值为0-255
mArcPaint.setAlpha(100);
//设置画笔的画出的形状
mArcPaint.setStrokeJoin(Paint.Join.ROUND);
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
//设置画笔类型
mArcPaint.setStyle(Paint.Style.STROKE);
//画笔宽度
mArcPaint.setStrokeWidth(mStrokeWith);
//中心文字的paint
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.parseColor("#FFFFFF"));
//设置文本的对齐方式
mTextPaint.setTextAlign(Paint.Align.CENTER);
//mTextPaint.setTextSize(getResources().getDimensionPixelSize(R.dimen.dp_12));
mTextPaint.setTextSize(getResources().getDimension(R.dimen.bus_ext_arcView_center_text_size));
}
/**
* 为绘制弧度及数据设置动画
*
* @param startAngle 开始的弧度
* @param currentAngle 需要绘制的弧度
* @param time 动画执行的时长
*/
private void setAnimation(float startAngle, float currentAngle, int time) {
//绘制当前数据对应的圆弧的动画效果
ValueAnimator progressAnimator = ValueAnimator.ofFloat(startAngle, currentAngle);
progressAnimator.setDuration(time);
progressAnimator.setTarget(mIncludedAngle);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mIncludedAngle = (float) animation.getAnimatedValue();
//重新绘制,不然不会出现效果
postInvalidate();
}
});
//开始执行动画
progressAnimator.start();
}
/**
* 设置弧形颜色
*
* @param value 颜色值
*/
public void setArcColor(int value) {
mArcColor = value;
}
/**
* 设置数据
*
* @param value 当前绘制的值
*/
public void setValues(int value) {
//完全覆盖
if (value > maxValue) {
value = maxValue;
}
if (value < 0) {
value = 0;
}
currentValue = value;
//计算弧度比重
float scale = (float) currentValue / maxValue;
//计算弧度
float currentAngle = scale * mAngle;
//开始执行动画
setAnimation(lastAngle, currentAngle, 1000);
lastAngle = currentAngle;
//重新绘制
postInvalidate();
}
private float dp2px(float dp) {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
return dp * metrics.density;
}
}

View File

@@ -0,0 +1,37 @@
package com.mogo.och.bus.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.mogo.commons.debug.DebugConfig;
/**
* 强制拦截所有touch时间的约束布局
*
* @author tongchenfei
*/
public class NoTouchConstraintLayout extends ConstraintLayout {
public NoTouchConstraintLayout(Context context) {
super(context);
}
public NoTouchConstraintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoTouchConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(DebugConfig.isDebug()){
return super.onInterceptTouchEvent(ev);
}else {
return true;
}
}
}

View File

@@ -0,0 +1,278 @@
package com.mogo.och.bus.view;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import androidx.annotation.Nullable;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.eagle.core.utilcode.util.BitmapHelper;
import com.mogo.och.bus.R;
import me.jessyan.autosize.AutoSizeConfig;
import me.jessyan.autosize.utils.AutoSizeUtils;
/**
* 滑块滑动面板
*
* @author tongchenfei
*/
public class SlidePanelView extends View {
private static final String TAG = "SlidePanelView";
public SlidePanelView(Context context) {
this(context, null);
}
public SlidePanelView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidePanelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.SlidePanelView);
textSize = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_textSize, textSize);
BLOCK_START_X = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_BLOCK_START_X, BLOCK_START_X);
BLOCK_START_Y = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_BLOCK_START_Y, BLOCK_START_Y);
NORMAL_TEXT_MARGIN_LEFT = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_NORMAL_TEXT_MARGIN_LEFT, NORMAL_TEXT_MARGIN_LEFT);
NORMAL_TEXT_MARGIN_RIGHT = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_NORMAL_TEXT_MARGIN_RIGHT, NORMAL_TEXT_MARGIN_RIGHT);
SHORT_TEXT_MARGIN_LEFT = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_SHORT_TEXT_MARGIN_LEFT, SHORT_TEXT_MARGIN_LEFT);
SHORT_TEXT_MARGIN_RIGHT = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_SHORT_TEXT_MARGIN_RIGHT, SHORT_TEXT_MARGIN_RIGHT);
init();
}
private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint blockPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static int textSize = 40;
private static int BLOCK_START_X = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),15);
private static int BLOCK_START_Y = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),15);
private static int NORMAL_TEXT_MARGIN_LEFT = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),40);
private static int NORMAL_TEXT_MARGIN_RIGHT = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),60);
private static int SHORT_TEXT_MARGIN_LEFT = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),60);;
private static int SHORT_TEXT_MARGIN_RIGHT = AutoSizeUtils.dp2px(AbsMogoApplication.getApp(),70);;
private int textMarginLeft = NORMAL_TEXT_MARGIN_LEFT;
private int textMarginRight = NORMAL_TEXT_MARGIN_RIGHT;
private OnSlidePanelMoveToEndListener moveToEndListener;
private int blockWidth = 0;
private int blockOffset = 0;
private float lastX;
private boolean isToEnd = false;
private static final String STRING_SLIDE_TO_RIGHT = "向右滑动";
private RectF bgRectF;
private Bitmap bmBlock;
private final Matrix gradientMatrix = new Matrix();
private float matrixTranslate;
private final Rect textRect = new Rect();
private LinearGradient textGradient;
private ObjectAnimator matrixAnim;
private String blockText = STRING_SLIDE_TO_RIGHT;
private Paint.FontMetrics blockTextMetrics = new Paint.FontMetrics();
private static final int GRADIENT_OFFSET = 200;
public void setOnSlidePanelMoveToEndListener(OnSlidePanelMoveToEndListener moveToEndListener) {
this.moveToEndListener = moveToEndListener;
}
private void setBlockOffset(int blockOffset) {
this.blockOffset = blockOffset;
invalidate();
}
private void setMatrixTranslate(float matrixTranslate) {
this.matrixTranslate = matrixTranslate;
invalidate();
}
public void setText(String text) {
this.blockText = text;
requestLayout();
invalidate();
}
private void init() {
bgRectF = new RectF(0, 0, 0, 0);
bgPaint.setColor(Color.parseColor("#CC0F1325"));
bgPaint.setStyle(Paint.Style.FILL);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextSize(textSize);
textPaint.setTextAlign(Paint.Align.LEFT);
textGradient = new LinearGradient(-GRADIENT_OFFSET, 0, 0, 0, new int[]{0x33ffffff, 0xffffffff, 0x33ffffff}, null, Shader.TileMode.CLAMP);
textGradient.setLocalMatrix(gradientMatrix);
textPaint.setShader(textGradient);
textPaint.getFontMetrics(blockTextMetrics);
int size = AutoSizeUtils.dp2px(getContext(), 120);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inDensity = (int) AutoSizeConfig.getInstance().getInitDensity();
bmBlock = BitmapFactory.decodeResource(getResources(), R.drawable.bus_base_slide_block,opts);
bmBlock = Bitmap.createScaledBitmap(bmBlock, size, size, true);
blockWidth = bmBlock.getWidth();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize;
int heightSize;
if (blockText.length() < 5) {
textMarginLeft = SHORT_TEXT_MARGIN_LEFT;
textMarginRight = SHORT_TEXT_MARGIN_RIGHT;
} else {
textMarginLeft = NORMAL_TEXT_MARGIN_LEFT;
textMarginRight = NORMAL_TEXT_MARGIN_RIGHT;
}
if (widthMode == MeasureSpec.AT_MOST) {
// 宽度根据图片大小,字符串长度,各种间隔确定
// 高度根据图片大小和上下间隔确定
textPaint.getTextBounds(blockText, 0, blockText.length(), textRect);
widthSize = BLOCK_START_X * 2 + bmBlock.getWidth() + textMarginLeft + textMarginRight + textRect.width();
heightSize = BLOCK_START_Y * 2 + bmBlock.getHeight();
widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private float textOffset = 0;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (bgRectF != null){
bgRectF.left = 0;
bgRectF.top = 0;
bgRectF.right = w;
bgRectF.bottom = h;
}
if (matrixAnim != null) {
matrixAnim.cancel();
}
textOffset = (getHeight() - blockTextMetrics.ascent - blockTextMetrics.descent) / 2;
matrixAnim = ObjectAnimator.ofFloat(this, "matrixTranslate", 0, w + GRADIENT_OFFSET).setDuration(2000);
matrixAnim.setRepeatCount(ValueAnimator.INFINITE);
matrixAnim.start();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (matrixAnim != null) {
matrixAnim.start();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (matrixAnim != null) {
matrixAnim.cancel();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x > BLOCK_START_X + blockOffset && x < blockWidth + BLOCK_START_X + blockOffset && y > BLOCK_START_Y && y < getHeight() - BLOCK_START_Y) {
isToEnd = false;
lastX = x;
}
break;
case MotionEvent.ACTION_MOVE:
if (lastX != 0) {
blockOffset = (int) (x - lastX);
if (blockOffset < 0) {
blockOffset = 0;
}
if (blockOffset + BLOCK_START_X + blockWidth > getWidth()) {
// 超出右边界
blockOffset = getWidth() - BLOCK_START_X - blockWidth;
if (!isToEnd) {
isToEnd = true;
if (moveToEndListener != null) {
moveToEndListener.moveToEnd();
}
startBlockBackAnim();
}
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
// 执行滑块回归动画
if (!isToEnd) {
startBlockBackAnim();
}
break;
default:
break;
}
return true;
}
private void startBlockBackAnim() {
ObjectAnimator blockBackanimator = ObjectAnimator.ofInt(this, "blockOffset", blockOffset, 0);
blockBackanimator.setInterpolator(new DecelerateInterpolator());
blockBackanimator.setDuration(1000 * blockOffset / getWidth());
blockBackanimator.start();
lastX = 0;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 画背景
canvas.drawRoundRect(bgRectF, (float) getHeight() / 2, (float) getHeight() / 2, bgPaint);
// 画文字
gradientMatrix.setTranslate(matrixTranslate, 0);
textGradient.setLocalMatrix(gradientMatrix);
canvas.save();
canvas.drawText(blockText, blockWidth + BLOCK_START_X + textMarginLeft, textOffset, textPaint);
canvas.restore();
// 画滑块
canvas.drawBitmap(bmBlock, BLOCK_START_X + blockOffset, BLOCK_START_Y, blockPaint);
}
public interface OnSlidePanelMoveToEndListener {
/**
* 滑块滑到了末尾
*/
void moveToEnd();
}
}

View File

@@ -0,0 +1,64 @@
package com.mogo.och.bus.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
/**
* 垂直虚线
*
* @author tongchenfei
*/
public class VerticalDashLineView extends View {
public VerticalDashLineView(Context context) {
this(context,null);
}
public VerticalDashLineView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public VerticalDashLineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private final Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Path dashPath = new Path();
private void init(){
linePaint.setColor(Color.GREEN);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeWidth(2);
linePaint.setPathEffect(new DashPathEffect(new float[]{5, 5}, 0));
}
public void setGradient(int startColor, int endColor) {
LinearGradient linearGradient = new LinearGradient(0, 0, getWidth(), getHeight(), startColor, endColor, Shader.TileMode.CLAMP);
linePaint.setShader(linearGradient);
invalidate();
}
public void setColor(int color) {
linePaint.setShader(null);
linePaint.setColor(color);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
dashPath.reset();
dashPath.moveTo((float) getWidth()/2, 0);
dashPath.lineTo((float) getWidth()/2,getHeight());
canvas.drawPath(dashPath,linePaint);
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/bus_autopilot_text_color_normal"/>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Some files were not shown because too many files have changed in this diff Show More