Merge branch 'dev_robotaxi-d-app-module_290_220715_2.9.0' into dev_robotaxi-d-app-module_290_220715_2.9.0_interface_subscribe

This commit is contained in:
xinfengkun
2022-08-03 16:30:34 +08:00
17 changed files with 443 additions and 552 deletions

View File

@@ -37,11 +37,12 @@ public class BusPassengerModelLoopManager {
}
private Disposable mHeartbeatDisposable; //心跳轮询
private CompositeDisposable mRouteWipeDisposable = new CompositeDisposable();
private CompositeDisposable mCalculateRouteDisposable = new CompositeDisposable(); //每隔2s计算一次剩余里程和时间
private CompositeDisposable mRouteWipeDisposable;
private CompositeDisposable mCalculateRouteDisposable; //每隔2s计算一次剩余里程和时间
public void startOrStopRouteAndWipe() {
CallerLogger.INSTANCE.i(M_BUS_P + TAG, "startOrStopRouteWipe()");
if (mRouteWipeDisposable != null) return;
if (mRouteWipeDisposable == null){
mRouteWipeDisposable = new CompositeDisposable();
}
@@ -70,7 +71,10 @@ public class BusPassengerModelLoopManager {
}
public void stopOrStopRouteAndWipe() {
if (mRouteWipeDisposable != null) mRouteWipeDisposable.dispose();
if (mRouteWipeDisposable != null) {
mRouteWipeDisposable.dispose();
mRouteWipeDisposable = null;
}
}
public void startQueryDriverLineLoop() {
@@ -96,6 +100,7 @@ public class BusPassengerModelLoopManager {
public void startCalculateRouteInfoLoop() {
CallerLogger.INSTANCE.i(M_BUS_P + TAG, "startCalculateRouteInfoLoop()");
if (mCalculateRouteDisposable != null) return;
if (mCalculateRouteDisposable == null){
mCalculateRouteDisposable = new CompositeDisposable();
}
@@ -127,6 +132,7 @@ public class BusPassengerModelLoopManager {
if (mCalculateRouteDisposable != null) {
CallerLogger.INSTANCE.i(M_BUS_P + TAG, "stopCalculateRouteInfLoop()");
mCalculateRouteDisposable.dispose();
mCalculateRouteDisposable = null;
}
}

View File

@@ -107,13 +107,16 @@ public abstract class BusPassengerBaseFragment<V extends IView, P extends Presen
if (currentIndex == 0){
mCurrentArriveStationTitle.setText(getResources().getString(R.string.bus_p_cur_station_title_init));
mCurrentArriveTip.setText(getResources().getString(R.string.bus_p_cur_station_arrived_tip_init));
mProgressBar.setVisibility(View.GONE);
return;
}
if (isArrived){
mCurrentArriveStationTitle.setText(getResources().getString(R.string.bus_p_cur_station_title));
mCurrentArriveTip.setText(getResources().getString(R.string.bus_p_cur_station_arrived_tip));
mProgressBar.setVisibility(View.GONE);
}else {
mCurrentArriveStationTitle.setText(getResources().getString(R.string.bus_p_cur_next_station_title));
mProgressBar.setVisibility(View.VISIBLE);
}
}

View File

@@ -299,6 +299,7 @@ public class BusPassengerMapDirectionView
public void clearCoordinatesLatLng(){
mCoordinatesLatLng.clear();
mLinePointsLatLng.clear();
}
public void onCreateView(Bundle savedInstanceState) {

View File

@@ -35,7 +35,7 @@ public class TaxiPassengerModelLoopManager {
private Disposable mInAndWaitServiceDisposable; //进行中、待服务订单列表轮询
private Disposable mQueryOrderRemainingDisposable; //心跳轮询
private CompositeDisposable mRouteWipeDisposable = new CompositeDisposable(); //轨迹擦除
private CompositeDisposable mRouteWipeDisposable; //轨迹擦除
private Disposable mReadyToAutopilotDisposable; //轮询是否可开启自动驾驶
public void startReadyToAutopilot() {
@@ -60,10 +60,11 @@ public class TaxiPassengerModelLoopManager {
}
public void startRouteAndWipe() {
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "startRouteAndWipe()");
if (mRouteWipeDisposable != null) return;
if (mRouteWipeDisposable == null){
mRouteWipeDisposable = new CompositeDisposable();
}
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "startRouteAndWipe()");
Disposable disposable = startLoopRouteAndWipe()
.doOnSubscribe(new Consumer<Disposable>() {
@Override
@@ -102,6 +103,7 @@ public class TaxiPassengerModelLoopManager {
if (mRouteWipeDisposable != null) {
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "stopRouteAndWipe()");
mRouteWipeDisposable.dispose();
mRouteWipeDisposable = null;
}
}

View File

@@ -1208,6 +1208,8 @@ public class TaxiModel {
@Override
public void onAutopilotRotting(MessagePad.GlobalPathResp routeList) {
CallerLogger.INSTANCE.d(M_TAXI + TAG, "onAutopilotRotting = "
+ GsonUtil.jsonFromObject(routeList));
if (null != routeList && routeList.getWayPointsList().size() > 0) {
updateOrderRoute(routeList.getWayPointsList());
updateOrderRouteInfo(routeList.getWayPointsList());
@@ -1276,7 +1278,7 @@ public class TaxiModel {
if (mCurrentOCHOrder != null && mRoutePoints.size() == 0) {//根据orderNo去查询
queryOrderRouteList(mCurrentOCHOrder.orderNo);
}
Logger.d(M_TAXI + TAG, "--------mRoutePoints.size---------- " + mRoutePoints.size());
//开启实时计算剩余距离,剩余时间,预计时间
startOrStopCalculateRouteInfo(true);
}
@@ -1300,7 +1302,8 @@ public class TaxiModel {
double lastTime = lastSumLength / TaxiConst.TAXI_AVERAGE_SPEED * 3.6; //秒
Logger.d(M_TAXI + "dynamicCalculateRouteInfo"
, "---lastSumLength: " + lastSumLength + "----lastTime : " + lastTime);
, "---lastSumLength: " + lastSumLength + "----lastTime : " + lastTime
+ " thread = "+ Thread.currentThread().getName());
mCurrentOCHOrder.decreaseTravelDistance(lastSumLength);
if (mOrderStatusCallback != null) {

View File

@@ -38,7 +38,7 @@ public class TaxiModelLoopManager {
private Disposable mNewBookingOrderDisposable; //新到待抢预约单轮询
private Disposable mGrabResultDisposable; //抢单结果轮询
private Disposable mHeartbeatDisposable; //心跳轮询
private CompositeDisposable mCalculateRouteDisposable = new CompositeDisposable(); //每隔2s计算一次剩余里程和时间
private CompositeDisposable mCalculateRouteDisposable; //每隔2s计算一次剩余里程和时间
public void startInAndWaitOrdersLoop() {
if (mInAndWaitServiceDisposable != null && !mInAndWaitServiceDisposable.isDisposed()) {
@@ -126,8 +126,11 @@ public class TaxiModelLoopManager {
public void startCalculateRouteInfoLoop() {
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "startCalculateRouteInfoLoop()");
CallerLogger.INSTANCE.i(M_TAXI + TAG, "startCalculateRouteInfoLoop()");
if (mCalculateRouteDisposable != null) return;
if (mCalculateRouteDisposable == null){
mCalculateRouteDisposable = new CompositeDisposable();
}
Disposable disposable = loopDynamicCalculateRouteInfo()
.doOnSubscribe(new Consumer<Disposable>() {
@Override
@@ -165,8 +168,9 @@ public class TaxiModelLoopManager {
public void stopCalculateRouteInfLoop() {
if (mCalculateRouteDisposable != null) {
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "stopCalculateRouteInfLoop()");
CallerLogger.INSTANCE.i(M_TAXI + TAG, "stopCalculateRouteInfLoop()");
mCalculateRouteDisposable.dispose();
mCalculateRouteDisposable = null;
}
}
}

View File

@@ -161,7 +161,7 @@ class MoGoAdasListenerImpl : OnAdasListener {
//油门
CallerAutopilotVehicleStateListenerManager.invokeAutopilotThrottle(vehicleState.throttle)
//刹车
CallerAutopilotVehicleStateListenerManager.invokeAutopilotThrottle(vehicleState.brake)
CallerAutopilotVehicleStateListenerManager.invokeAutopilotBrake(vehicleState.brake)
} else {
CallerAutopilotVehicleStateListenerManager.invokeAutopilotDataException(header.timestamp.toLong())

View File

@@ -1034,7 +1034,7 @@ class DebugSettingView @JvmOverloads constructor(
accelerationIsShow = isChecked
}
tbRouteDynamicEffect.isChecked = AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)
tbRouteDynamicEffect.isChecked = AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode) && !AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode)
tbRouteDynamicEffect.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {

View File

@@ -1,8 +1,10 @@
package com.mogo.eagle.core.function.hmi.ui.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.TextView;
@@ -11,11 +13,10 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotVehicleStateListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotCarStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotVehicleStateListenerManager;
import com.mogo.eagle.core.function.hmi.R;
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
@@ -39,6 +40,22 @@ public class TrafficDataView extends ConstraintLayout {
//圆弧颜色
private int mArcColor;
private static final int MSG_SEND_UPDATE = 1;
private volatile double acceleration;
@SuppressLint("HandlerLeak")
private final Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == MSG_SEND_UPDATE) {
java.text.DecimalFormat mFormat = new java.text.DecimalFormat("0.00");
String accStr = mFormat.format(acceleration);
accTextView.setText("a: " + accStr);
}
}
};
public TrafficDataView(@NonNull Context context) {
super(context);
}
@@ -46,7 +63,6 @@ public class TrafficDataView extends ConstraintLayout {
public TrafficDataView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context);
CallerAutopilotVehicleStateListenerManager.INSTANCE.addListener(TAG, mIMoGoAutopilotVehicleStateListener);
}
public TrafficDataView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
@@ -57,11 +73,19 @@ public class TrafficDataView extends ConstraintLayout {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
CallerAutopilotVehicleStateListenerManager.INSTANCE.addListener(TAG, mIMoGoAutopilotVehicleStateListener);
CallerAutopilotCarStatusListenerManager.INSTANCE.addListener(TAG, gnssInfo -> {
if (gnssInfo != null) {
acceleration = gnssInfo.getAcceleration();
}
handler.sendEmptyMessageDelayed(MSG_SEND_UPDATE, 1000L);
});
}
private void initView(@NonNull Context context) {
if (AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode)) {
// LayoutInflater.from(context).inflate(R.layout.hmi_traffic_data, this);
} else {
}
LayoutInflater.from(context).inflate(R.layout.hmi_traffic_data, this);
tapPositionView = findViewById(R.id.traffic_position);
speedImage = findViewById(R.id.speedImage);
@@ -70,6 +94,13 @@ public class TrafficDataView extends ConstraintLayout {
brakeStatus = findViewById(R.id.brakeStatus);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
CallerAutopilotVehicleStateListenerManager.INSTANCE.removeListener(TAG);
CallerAutopilotCarStatusListenerManager.INSTANCE.removeListener(TAG);
}
private final IMoGoAutopilotVehicleStateListener mIMoGoAutopilotVehicleStateListener = new IMoGoAutopilotVehicleStateListener() {
/**
* 车辆转向灯
@@ -77,7 +108,7 @@ public class TrafficDataView extends ConstraintLayout {
*/
@Override
public void onAutopilotLightSwitchData(@org.jetbrains.annotations.Nullable Chassis.LightSwitch lightSwitch) {
if(lightSwitch != null){
if (lightSwitch != null) {
CallerLogger.INSTANCE.d(TAG, "车辆转向灯:" + lightSwitch.toString());
}
}
@@ -125,12 +156,7 @@ public class TrafficDataView extends ConstraintLayout {
@Override
public void onAutopilotAcc(float carAcc) {
CallerLogger.INSTANCE.d(TAG, "司机屏加速度:" + carAcc);
ThreadUtils.runOnUiThread(() -> {
java.text.DecimalFormat mFormat = new java.text.DecimalFormat("0.00");
String accStr = mFormat.format(carAcc);
accTextView.setText("a: " + accStr);
});
}
@Override

View File

@@ -54,8 +54,12 @@ dependencies {
kapt rootProject.ext.dependencies.aroutercompiler
//implementation rootProject.ext.dependencies.adasHigh
implementation rootProject.ext.dependencies.mogocustommapoperational
implementation (rootProject.ext.dependencies.mogocustommapoperational) {
exclude group: 'com.zhidaoauto.machine', module: 'map'
}
implementation rootProject.ext.dependencies.mogocustommap
implementation rootProject.ext.dependencies.amapnavi3dmap
implementation rootProject.ext.dependencies.amaplocation

View File

@@ -308,7 +308,7 @@ public class AMapCustomView
Log.d(TAG, "onAutopilotRotting");
List list = globalPathResp.getWayPointsList();
int minCount = 2;
if (list.size() >= minCount && sList.size() == 0 && eList.size() == 0 && mWayPointList.size() == 0) {
if (list.size() >= minCount) {
calculate = true;
MessagePad.Location sLocation = (MessagePad.Location) list.get(0);
MessagePad.Location eLocation = (MessagePad.Location) list.get(list.size() - 1);

View File

@@ -28,6 +28,8 @@ public class MogoLatLng implements Parcelable {
public double distance;
public double acc;
public MogoLatLng( double lat, double lon ) {
this.lat = lat;
this.lon = lon;

View File

@@ -33,6 +33,12 @@ public class MogoPolylineOptions {
private boolean mGps = false;
private List<Integer> mColorValues;
private float brightSpeed = Float.MIN_VALUE;
private int brightColor = -1;
private boolean isBrightOn = false;
public MogoPolylineOptions() {
this.mPoints = new ArrayList<>();
}
@@ -161,6 +167,21 @@ public class MogoPolylineOptions {
return this;
}
public MogoPolylineOptions brightSpeed(float speed) {
this.brightSpeed = speed;
return this;
}
public MogoPolylineOptions openBright(boolean on) {
this.isBrightOn = on;
return this;
}
public MogoPolylineOptions brightColor(int color) {
this.brightColor = color;
return this;
}
/**
* @param isAboveMaskLayer
* @return
@@ -231,6 +252,18 @@ public class MogoPolylineOptions {
return mIsPointsUpdated;
}
public float getBrightSpeed() {
return brightSpeed;
}
public boolean isBrightOn() {
return isBrightOn;
}
public int getBrightColor() {
return brightColor;
}
public List<Integer> getColorValues() {
return mColorValues;
}

View File

@@ -482,6 +482,11 @@ public class ObjectUtils {
if (options.getColorValues() != null) {
target.colorValues(options.getColorValues());
}
if (options.isBrightOn()) {
target.isBright = true;
target.brightColor = options.getBrightColor();
target.brightSpeed = options.getBrightSpeed();
}
return target;
}

View File

@@ -4,6 +4,8 @@ import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.map.MogoLocation;
@@ -13,10 +15,13 @@ import com.mogo.eagle.core.function.api.map.listener.IMoGoMapLocationListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotPlanningListenerManager;
import com.mogo.eagle.core.function.call.map.CallerMapLocationListenerManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -25,17 +30,14 @@ import mogo_msg.MogoReportMsg;
import system_master.SystemStatusInfo;
public class MogoRouteOverlayManager implements
IMoGoAutopilotPlanningListener, IMoGoAutopilotStatusListener,
IMoGoMapLocationListener {
IMoGoAutopilotPlanningListener, IMoGoAutopilotStatusListener, IMoGoMapLocationListener {
private static volatile MogoRouteOverlayManager sInstance;
private static final String TAG = "Route";
private int STATUS_AUTOPILOT = 0;//0 非自动驾驶 ; 1 自动驾驶
private MogoLocation mLocation;
private final AtomicBoolean isArriveAtStation = new AtomicBoolean(false);
private final AtomicInteger autopilotMode = new AtomicInteger(0);
private List<MessagePad.TrajectoryPoint> mTrajectoryList = null;
private final Lock mLock = new ReentrantLock();
private final LinkedList<List<MessagePad.TrajectoryPoint>> queue = new LinkedList<>();
private MogoRouteOverlayManager() {}
@@ -59,22 +61,28 @@ public class MogoRouteOverlayManager implements
@Override
public void onAutopilotTrajectory(@NonNull List<MessagePad.TrajectoryPoint> items) {
if (isArriveAtStation.get()) {
synchronized (queue) {
queue.clear();
queue.offer(items);
}
}
@Override
public void onLocationChanged(@Nullable MogoLocation location, int from) {
if (from != 1 || location == null) {
return;
}
if (items.isEmpty()) {
if (isArriveAtStation.get() || autopilotMode.get() != 1) {
RouteOverlayDrawer.getInstance().clearMogoRouteOverlay();
return;
}
MogoLocation location = mLocation;
if (location == null) {
return;
}
try {
mLock.lock();
mTrajectoryList = items;
} finally {
mLock.unlock();
synchronized (queue) {
List<MessagePad.TrajectoryPoint> items = queue.getLast();
if (items != null && !items.isEmpty()) {
RouteOverlayDrawer.getInstance().drawTrajectoryList(items, location.getBearing());
}
}
}
@Override
@@ -87,13 +95,11 @@ public class MogoRouteOverlayManager implements
isArriveAtStation.set(false);
return;
}
this.STATUS_AUTOPILOT = autoPilotStatusInfo.getPilotmode();
if (this.STATUS_AUTOPILOT == 1) {
int mode = autoPilotStatusInfo.getPilotmode();
if (mode == 1) {
isArriveAtStation.set(false);
}
if (STATUS_AUTOPILOT != 1) {
RouteOverlayDrawer.getInstance().clearMogoRouteOverlay();
}
this.autopilotMode.set(mode);
}
@Override
@@ -104,7 +110,6 @@ public class MogoRouteOverlayManager implements
Log.d(TAG, "onArriveAt data : " + arrivalNotification);
if (!isArriveAtStation.get()) {
isArriveAtStation.set(true);
RouteOverlayDrawer.getInstance().clearMogoRouteOverlay();
}
}
@@ -114,28 +119,6 @@ public class MogoRouteOverlayManager implements
}
@Override
public void onLocationChanged(@Nullable MogoLocation location, int from) {
if (from != 1) {
return;
}
boolean isExcept = false;
try {
if (location == null) {
isExcept = true;
return;
}
if (FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData || STATUS_AUTOPILOT == 1) {
RouteOverlayDrawer.getInstance().drawTrajectoryList(mLock, location, mTrajectoryList);
}
} finally {
mLocation = location;
if (isExcept) {
RouteOverlayDrawer.getInstance().hide();
}
}
}
@Override
public void onAutopilotGuardian(MogoReportMsg.MogoReportMessage guardianInfo) {
@@ -146,7 +129,5 @@ public class MogoRouteOverlayManager implements
}
@Override
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {
}
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {}
}

View File

@@ -1,23 +1,27 @@
package com.mogo.module.service.routeoverlay;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_OLD_ROUTE;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.util.Log;
import androidx.core.util.Pools;
import com.mogo.eagle.core.data.map.MogoLatLng;
import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.map.MogoOverlayManager;
import com.mogo.map.overlay.IMogoOverlayManager;
import com.mogo.map.overlay.IMogoPolyline;
import com.mogo.map.overlay.MogoPolylineOptions;
import com.mogo.module.common.utils.DrivingDirectionUtils;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import mogo.telematics.pad.MessagePad;
public class RouteOverlayDrawer {
@@ -27,13 +31,31 @@ public class RouteOverlayDrawer {
// 连接线参数
private Handler mRenderHandler;
// private Handler mLogHandler;
IMogoOverlayManager mogoOverlayManager;
private static volatile RouteOverlayDrawer sInstance;
private final MogoPolylineOptions mPolylineOptions;
private volatile IMogoPolyline mMoGoPolyline;
// private FileWriter writer;
private RouteOverlayDrawer() {
mPolylineOptions = new MogoPolylineOptions();
mPolylineOptions.zIndex(75000f);
mPolylineOptions.setGps(true);
mPolylineOptions.width(20).useGradient(true);
// try {
// File log = new File(Environment.getExternalStorageDirectory(), "log.txt");
// if (log.exists()) {
// log.delete();
// }
// if (log.getParentFile() != null && !log.getParentFile().exists()) {
// log.getParentFile().mkdirs();
// }
//
// log.createNewFile();
// writer = new FileWriter(log, false);
// } catch (Throwable t) {
//
// }
// 渐变色
mogoOverlayManager = MogoOverlayManager.getInstance();
// 线条粗细,渐变,渐变色值
@@ -45,15 +67,6 @@ public class RouteOverlayDrawer {
}
};
renderTask.start();
// HandlerThread logHandler = new HandlerThread("log_handler") {
// @Override
// protected void onLooperPrepared() {
// super.onLooperPrepared();
// mLogHandler = new Handler(getLooper());
// }
// };
// logHandler.start();
}
public static RouteOverlayDrawer getInstance() {
@@ -68,281 +81,102 @@ public class RouteOverlayDrawer {
}
public void clearMogoRouteOverlay() {
if (mRenderTask != null && mRenderHandler != null) {
mRenderTask.remove();
if (mMoGoPolyline != null) {
mMoGoPolyline.remove();
mMoGoPolyline = null;
}
if (mRenderTask != null) {
mRenderHandler.removeCallbacks(mRenderTask);
}
}
public void remove() {
if (mRenderTask != null && mRenderHandler != null) {
mRenderTask.remove();
mRenderHandler.removeCallbacks(mRenderTask);
}
}
public void hide() {
if (mRenderTask != null && mRenderHandler != null) {
mRenderTask.hide();
mRenderHandler.removeCallbacks(mRenderTask);
}
}
private static class RenderOptions {
private IMogoPolyline mPolyLine;
private final MogoPolylineOptions mOptions;
private static int taskSequence = 0;
//private Handler mHandler;
private final IMogoOverlayManager mOverlayManager;
//private ChildRenderTask mTask;
private class ChildRenderTask implements Runnable {
private List<Integer> colors;
private List<MogoLatLng> points;
public void setData(List<Integer> colors, List<MogoLatLng> points) {
this.colors = colors;
this.points = points;
}
@Override
public void run() {
if (this.colors == null || this.colors.isEmpty()) {
return;
}
if (this.points == null || this.points.isEmpty()) {
return;
}
mOptions.colorValues(colors);
mOptions.points(points);
if (mPolyLine == null || mPolyLine.isDestroyed()) {
mPolyLine = mOverlayManager.addPolyline(mOptions);
} else {
mPolyLine.setOption(mOptions);
}
if (mPolyLine != null && !mPolyLine.isVisible()) {
mPolyLine.setVisible(true);
}
}
}
public RenderOptions(IMogoOverlayManager overlay) {
mOverlayManager = overlay;
mOptions = new MogoPolylineOptions().width(20).useGradient(true).zIndex(75000f).setGps(true);
// new HandlerThread("child-render-" +(taskSequence++)) {
// @Override
// protected void onLooperPrepared() {
// super.onLooperPrepared();
// mHandler = new Handler(getLooper());
// }
// }.start();
}
public void show(List<Integer> colors, List<MogoLatLng> points) {
// if (mTask == null) {
// mTask = new ChildRenderTask();
// }
// mTask.setData(colors, points);
// if (mHandler != null) {
// mHandler.removeCallbacks(mTask);
// mHandler.post(mTask);
// }
mOptions.colorValues(colors);
mOptions.points(points);
if (mPolyLine == null || mPolyLine.isDestroyed()) {
mPolyLine = mOverlayManager.addPolyline(mOptions);
} else {
mPolyLine.setOption(mOptions);
}
if (mPolyLine != null && !mPolyLine.isVisible()) {
mPolyLine.setVisible(true);
}
}
public void hide() {
// if (mTask != null && mHandler != null) {
// mHandler.removeCallbacks(mTask);
// }
if (mPolyLine != null && mPolyLine.isVisible()) {
mPolyLine.setVisible(false);
}
}
public void remove() {
// if (mTask != null && mHandler != null) {
// mHandler.removeCallbacks(mTask);
// }
if (mPolyLine != null && !mPolyLine.isDestroyed()) {
mPolyLine.remove();
mPolyLine = null;
}
}
}
private class RenderTask implements Runnable {
private List<MessagePad.TrajectoryPoint> routeList;
private MogoLocation location;
private volatile List<MessagePad.TrajectoryPoint> routeList;
private final Pools.Pool<MogoLatLng> pools;
private final LinkedList<MogoLatLng> points;
private final Pools.Pool<RenderOptions> options;
private Lock mLock;
private double bearing;
public RenderTask() {
this.pools = new Pools.SimplePool<>(500);
this.points = new LinkedList<>();
this.options = new Pools.SimplePool<>(5);
}
public void setData(Lock lock, MogoLocation location, List<MessagePad.TrajectoryPoint> routeList) {
this.location = location;
public void setData(List<MessagePad.TrajectoryPoint> routeList, double bearing) {
this.routeList = routeList;
this.mLock = lock;
this.bearing = bearing;
}
@SuppressLint("LongLogTag")
@Override
public void run() {
Lock lock = this.mLock;
if (lock == null) {
return;
}
LinkedList<MogoLatLng> pps = this.points;
boolean isExcept = false;
// StringBuilder sb;
int total;
try {
pps.clear();
long drawStart = SystemClock.elapsedRealtime();
try {
lock.lock();
List<MessagePad.TrajectoryPoint> routes = this.routeList;
if (routes == null || (total = routes.size()) < 2) {
isExcept = true;
return;
}
RouteStrategy.INSTANCE.start();
// sb = new StringBuilder();
// sb.append(System.currentTimeMillis()).append(" ");
for (int i = 0; i < total; i++) {
MessagePad.TrajectoryPoint route = null;
try {
route = routes.get(i);
if (route == null) {
continue;
}
} catch (Throwable t) {
Log.d("Route", "render-error:" + t.getMessage());
}
if (route == null) {
//数组越界了,结束循环
break;
}
// sb.append(route.getVelocity()).append(" ");
// sb.append(route.getAcceleration()).append(" ");
// sb.append(route.getAccumulatedDis()).append(" ");
MogoLatLng acquire = pools.acquire();
double latitude = route.getLatitude();
double longitude = route.getLongitude();
if (acquire == null) {
acquire = new MogoLatLng(latitude, longitude);
} else {
acquire.lon = longitude;
acquire.lat = latitude;
}
pps.add(acquire);
RouteStrategy.INSTANCE.check(route.getVelocity(), route.getAcceleration());
}
RouteStrategy.INSTANCE.end();
} finally {
lock.unlock();
List<MessagePad.TrajectoryPoint> routes = this.routeList;
if (routes == null || (total = routes.size()) < 2) {
isExcept = true;
return;
}
Strategy strategy = RouteStrategy.INSTANCE.getStrategy();
/// Log.d("ROUTE_XX", "strategy:" + strategy + "->route: " +sb);
// StringBuilder newSb = new StringBuilder("strategy:" + strategy + "->route: ");
// newSb.append(sb);
// if (mLogHandler != null) {
// mLogHandler.sendMessage(Message.obtain(mLogHandler, () -> {
// BufferedWriter bw = null;
// try {
// File file = new File(Environment.getExternalStorageDirectory(), "log.txt");
// if (!file.exists()) {
// file.createNewFile();
// }
// bw = new BufferedWriter(new FileWriter(file, true));
// bw.write(newSb.toString());
// bw.newLine();
// bw.flush();
// } catch (Throwable t) {
// try {
// if (bw != null) {
// bw.close();
// }
// } catch (Throwable t2) {
//
// }
// }
// }));
// }
Integer[] indexes = strategy.getIndexes();
Integer[] colors = strategy.getColors();
LinkedList<LinkedList<MogoLatLng>> parts = new LinkedList<>();
if (indexes.length > 0) {
for (int i = 0; i < indexes.length; i += 2) {
int startIndex = indexes[i];
int endIndex = indexes[i + 1];
parts.add(new LinkedList<>(pps.subList(startIndex, Math.min(endIndex + 1, pps.size()))));
RouteStrategy.INSTANCE.start();
for (int i = 0; i < total; i++) {
MessagePad.TrajectoryPoint route = null;
try {
route = routes.get(i);
if (route == null) {
continue;
}
} catch (Throwable t) {
Log.d("Route", "render-error:" + t.getMessage());
}
if (route == null) {
//数组越界了,结束循环
break;
}
MogoLatLng acquire = pools.acquire();
double latitude = route.getLatitude();
double longitude = route.getLongitude();
if (acquire == null) {
acquire = new MogoLatLng(latitude, longitude);
} else {
acquire.lon = longitude;
acquire.lat = latitude;
}
acquire.acc = route.getAcceleration();
acquire.speed = route.getVelocity();
pps.add(acquire);
RouteStrategy.INSTANCE.check(route.getVelocity(), route.getAcceleration(), routeList.size());
}
double lon = CallerAutoPilotStatusListenerManager.INSTANCE.getCurWgs84Lon();
double lat = CallerAutoPilotStatusListenerManager.INSTANCE.getCurWgs84Lat();
int removeCount = 0;
int colorsIndex = 0;
if (!parts.isEmpty()) {
L:while (!parts.isEmpty()) {
LinkedList<MogoLatLng> part = parts.peek();
if (part == null) {
if (points.size() > 0) {
MogoLatLng top = null;
while (points.size() != 0) {
MogoLatLng first = points.peek();
if (first == null) {
continue;
}
MogoLatLng top = null;
while (!part.isEmpty()) {
MogoLatLng first = part.peek();
if (first == null) {
part.poll();
continue;
}
if (first == top) {
break L;
}
lon = CallerAutoPilotStatusListenerManager.INSTANCE.getCurWgs84Lon();
lat = CallerAutoPilotStatusListenerManager.INSTANCE.getCurWgs84Lat();
long angle = isPointOnCarFront(lon, lat, location.getBearing(), first.lon, first.lat);
if (angle >= 90) {
removeCount++;
pps.remove(first);
try {
pools.release(first);
} catch (Throwable ignore) {
} finally {
part.poll();
}
}
top = first;
if (first == top) {
break;
}
parts.poll();
colorsIndex += 2;
lon = CallerAutoPilotStatusListenerManager.INSTANCE.getCurWgs84Lon();
lat = CallerAutoPilotStatusListenerManager.INSTANCE.getCurWgs84Lat();
long angle = isPointOnCarFront(lon, lat, bearing, first.lon, first.lat);
if (angle >= 90) {
removeCount++;
RouteStrategy.INSTANCE.remove(first.acc);
pools.release(first);
points.poll();
}
top = first;
}
if (parts.isEmpty()) {
if (points.size() == 0) {
isExcept = true;
return;
}
@@ -353,75 +187,92 @@ public class RouteOverlayDrawer {
self.lat = lat;
self.lon = lon;
}
pps.addFirst(self);
parts.get(0).addFirst(self);
List<RenderOptions> ll = new ArrayList<>();
for (int i = 0; i < parts.size(); i++) {
LinkedList<MogoLatLng> ps = parts.get(i);
RenderOptions options = this.options.acquire();
if (options == null) {
options = new RenderOptions(mogoOverlayManager);
points.addFirst(self);
RouteStrategy.INSTANCE.end();
Strategy strategy = RouteStrategy.INSTANCE.getStrategy();
List<Integer> colors = strategy.getColors();
// StringBuilder sb = new StringBuilder();
// int colorIndex = 0;
// sb.append("=========================================\n");
// for (MogoLatLng p : points) {
// sb.append(p.lat);
// sb.append(",");
// sb.append(p.lon);
// sb.append(",");
// sb.append(p.acc);
// sb.append(",");
// sb.append(p.speed);
// if (colorIndex < colors.size()) {
// int color = colors.get(colorIndex);
// sb.append(",");
// sb.append(color);
// sb.append(",");
// sb.append(Color.red(color));
// sb.append(",");
// sb.append(Color.green(color));
// sb.append(",");
// sb.append(Color.blue(color));
// sb.append(",");
// sb.append(Color.alpha(color));
// colorIndex ++;
// }
// sb.append("\n");
// }
// try {
// if (writer != null) {
// writer.write(sb.toString());
// writer.flush();
// }
// } catch (Throwable t) {
//
// }
boolean isLightOn = strategy instanceof ColorfulStrategy && ((ColorfulStrategy) strategy).isLightOn();
if (mMoGoPolyline == null || mMoGoPolyline.isDestroyed()) {
mPolylineOptions.points(points);
mPolylineOptions.colorValues(colors);
if (isLightOn) {
mPolylineOptions.openBright(true);
mPolylineOptions.brightColor(Color.parseColor("#D2F2F8"));
mPolylineOptions.brightSpeed(0.5f);
} else {
mPolylineOptions.openBright(false);
}
ll.add(options);
List<Integer> cls = new ArrayList<>();
cls.add(colors[colorsIndex++]);
cls.add(colors[colorsIndex++]);
options.show(cls, ps);
mMoGoPolyline = mogoOverlayManager.addPolyline(mPolylineOptions);
} else {
mPolylineOptions.points(points);
mPolylineOptions.colorValues(colors);
if (isLightOn) {
mPolylineOptions.openBright(true);
mPolylineOptions.brightColor(Color.parseColor("#D2F2F8"));
mPolylineOptions.brightSpeed(0.5f);
} else {
mPolylineOptions.openBright(false);
}
mMoGoPolyline.setOption(mPolylineOptions);
}
hide();
for (int i = ll.size() - 1; i >= 0 ; i--) {
this.options.release(ll.get(i));
if (mMoGoPolyline != null && !mMoGoPolyline.isDestroyed() && !mMoGoPolyline.isVisible()) {
mMoGoPolyline.setVisible(true);
}
} else {
isExcept = true;
}
long drawEnd = SystemClock.elapsedRealtime();
// Log.d("Route", "draw cost:" + (drawEnd - drawStart) + "ms and isExcept:" + isExcept + "::removeCount:" + removeCount + "::total:" + total);
Logger.d(M_OLD_ROUTE + TAG, "drawTrajectoryList cost : " + (drawEnd - drawStart) + "ms and isExcept:" + isExcept + "::removeCount:" + removeCount + "::total:" + total);
} catch (Throwable t) {
// Log.d("Route","error:" + Log.getStackTraceString(t));
Logger.d(M_OLD_ROUTE + TAG, "drawTrajectoryList error (isExcept: "+isExcept+") : " + t);
} finally {
try {
if (isExcept) {
hide();
}
if (!pps.isEmpty()) {
for (int i = 0; i < pps.size(); i++) {
MogoLatLng latLng = pps.get(i);
if (latLng == null) {
continue;
}
pools.release(latLng);
}
}
} catch (Throwable t) {
// Log.d("Route", "error2:" + Log.getStackTraceString(t));
if (isExcept) {
setVisible(false);
}
if (points.size() > 0) {
for (int i = 0; i < points.size(); i++) {
MogoLatLng latLng = points.get(i);
if (latLng == null) {
continue;
}
pools.release(latLng);
}
}
}
}
private void hide() {
List<RenderOptions> list = new ArrayList<>();
RenderOptions acquire = this.options.acquire();
while (acquire != null) {
acquire.hide();
list.add(acquire);
acquire = this.options.acquire();
}
for (int i = 0; i < list.size(); i++) {
this.options.release(list.get(i));
}
}
public void remove() {
List<RenderOptions> list = new ArrayList<>();
RenderOptions acquire = this.options.acquire();
while (acquire != null) {
acquire.remove();
list.add(acquire);
acquire = this.options.acquire();
}
for (int i = 0; i < list.size(); i++) {
this.options.release(list.get(i));
}
}
@@ -431,24 +282,30 @@ public class RouteOverlayDrawer {
// 计算车辆与点之间的夹角
long diffAngle = DrivingDirectionUtils.getDegreeOfCar2Poi2(
car_lon, car_lat, lon, lat, car_head);
// Log.d("Route", "isPointOnCarFront: angle->" + diffAngle);
CallerLogger.INSTANCE.d(M_OLD_ROUTE + TAG, "isPointOnCarFront: angle->" + diffAngle);
return diffAngle;
} finally {
// Log.d("Route", "isPointOnCarFront cost:" + (SystemClock.elapsedRealtime() - start) + "ms");
CallerLogger.INSTANCE.d(M_OLD_ROUTE + TAG, "isPointOnCarFront cost:" + (SystemClock.elapsedRealtime() - start) + "ms");
}
}
}
private RenderTask mRenderTask;
private volatile RenderTask mRenderTask;
public void drawTrajectoryList(Lock lock, MogoLocation carLoc, List<MessagePad.TrajectoryPoint> routeList) {
public void drawTrajectoryList(List<MessagePad.TrajectoryPoint> routeList, double bearing) {
if (mRenderTask == null) {
mRenderTask = new RenderTask();
}
mRenderTask.setData(lock, carLoc, routeList);
mRenderTask.setData(routeList, bearing);
if (mRenderHandler != null) {
mRenderHandler.removeCallbacks(mRenderTask);
mRenderHandler.post(mRenderTask);
}
}
public void setVisible(boolean isVisible) {
if (mMoGoPolyline != null && !mMoGoPolyline.isDestroyed()) {
mMoGoPolyline.setVisible(isVisible);
}
}
}

View File

@@ -1,202 +1,166 @@
package com.mogo.module.service.routeoverlay
import android.animation.*
import android.graphics.*
import android.view.animation.*
import com.mogo.eagle.core.data.config.*
import com.mogo.eagle.core.utilcode.mogo.*
import java.lang.Double.NaN
import com.mogo.module.service.routeoverlay.Colors.Companion.COLOR_BLUE
import com.mogo.module.service.routeoverlay.Colors.Companion.COLOR_BLUE_DARK
import com.mogo.module.service.routeoverlay.Colors.Companion.COLOR_RED_DARK
import com.mogo.module.service.routeoverlay.Colors.Companion.COLOR_TRANSPARENT
import java.util.*
import kotlin.collections.ArrayList
interface IStrategy {
fun getColors(): Array<Int>
fun getIndexes(): Array<Int>
fun getColors(): List<Int>
}
class Colors {
companion object {
val COLOR_BLUE_DARK = Color.parseColor("#2ABAD9")
val COLOR_BLUE_LIGHT = Color.parseColor("#332ABAD9")
val COLOR_RED_DARK = Color.parseColor("#CA4C15")
val COLOR_BLUE = Color.parseColor("#FF2ABAD9")
val COLOR_BLUE_DARK = Color.parseColor("#FF074EFF")
val COLOR_RED_DARK = Color.parseColor("#FFFF5F00")
val COLOR_TRANSPARENT = Color.parseColor("#002ABAD9")
}
}
sealed class Strategy: IStrategy
class Uniform(private val count: Int, private val colors: Array<Int>? = null): Strategy() {
class DefaultStrategy(private val colors: List<Int>? = null): Strategy() {
override fun getColors(): Array<Int> = colors ?: arrayOf(Colors.COLOR_BLUE_DARK, Colors.COLOR_TRANSPARENT)
override fun getIndexes(): Array<Int> = arrayOf(0, count)
override fun getColors(): List<Int> = colors ?: listOf(COLOR_BLUE, COLOR_TRANSPARENT)
}
class SlowDown(private val colors: Array<Int>, private val indexes: Array<Int>): Strategy() {
override fun getColors(): Array<Int> = colors
override fun getIndexes(): Array<Int> = indexes
override fun toString(): String {
return indexes.joinToString(",")
}
class ColorfulStrategy(private val colors: List<Int> = emptyList(), var isLightOn: Boolean): Strategy() {
override fun getColors(): List<Int> = colors
}
class SpeedUp(private val count: Int): Strategy() {
override fun getColors(): Array<Int> = arrayOf(Colors.COLOR_BLUE_DARK, Colors.COLOR_TRANSPARENT)
override fun getIndexes(): Array<Int> = arrayOf(0, count)
}
object RouteStrategy {
@Volatile
private var isEnable = AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode) && !AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode)
private var strategy: Strategy? = null
private var index: Int = 0
private var oldV: Double = NaN
private var oldA: Double = NaN
private var mode: Int = -1 // 0: 匀速, 1:减速, 2: 加速
private val colors4SlowDown: LinkedList<Int> = LinkedList()
private val colors4Uniform: LinkedList<Int> = LinkedList()
private val indexes4SlowDown: LinkedList<Int> = LinkedList()
private var isLightBlueAll = false
private var isRedBlue = false
private var ignore = false
private var firstV: Double = NaN
private val colors: ArrayList<Int> = ArrayList()
private var isEnable = AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)
private var index = 0
private val sorted: NavigableMap<Double, Int> by lazy { TreeMap() }
private var endEvaluator: ArgbEvaluator? = null
private var startColor = Int.MAX_VALUE
private var hasLessThan0 = false
fun enable(enable: Boolean) {
isEnable = enable
if (!enable) {
strategy = null
colors.clear()
}
}
fun start() {
if (sorted.isEmpty()) {
fill()
}
strategy = null
index = 0
mode = -1
ignore = false
colors4Uniform.clear()
colors4SlowDown.clear()
indexes4SlowDown.clear()
isLightBlueAll = false
isRedBlue = false
oldA = NaN
oldV = NaN
firstV = NaN
startColor = Int.MAX_VALUE
colors.clear()
endEvaluator = null
hasLessThan0 = false
}
fun end() {
strategy = acquireStrategy()
}
private fun acquireStrategy(): Strategy? {
if (mode == 0) {
return if (colors4Uniform.isNotEmpty()) Uniform(index, colors = Array(colors4Uniform.size) {
colors4Uniform[it]
}) else null
}
if (mode == 2) {
return SpeedUp(index)
}
if (indexes4SlowDown.isEmpty() || !isLightBlueAll) {
return null
}
val count = indexes4SlowDown.size
if (count == 3) {
val colors = when {
isRedBlue -> {
arrayOf(Colors.COLOR_RED_DARK, Colors.COLOR_BLUE_DARK, Colors.COLOR_BLUE_LIGHT, Colors.COLOR_TRANSPARENT)
}
else -> {
return SpeedUp(index)
}
}
val indexes = ArrayList<Int>()
val i = indexes4SlowDown[2]
if (index - i < 5) {
return SpeedUp(index)
}
indexes += 0
indexes += i
indexes += i
indexes += index
return SlowDown(colors, indexes = indexes.toTypedArray())
}
return null
}
fun check(speed: Double, acc: Double) {
try {
if (!isEnable){
if (isEnable) {
if (colors.isEmpty()) {
return
}
if (ignore) {
return
}
if (oldV.isNaN()) {
return
}
if (oldA.isNaN()) {
return
}
if (index < 3) {
//丢掉前3个点
return
}
if (firstV.isNaN()) {
firstV = speed;
return
}
if (speed == oldV) {
if (speed <= 0.01) {
colors4Uniform.clear()
colors4Uniform.add(Colors.COLOR_BLUE_LIGHT)
colors4Uniform.add(Colors.COLOR_TRANSPARENT)
}
ignore = true
mode = 0
return
}
if (speed > oldV) {
mode = 2
ignore = true
return
}
if (speed < oldV) {
if (mode != 1) {
indexes4SlowDown.add(0)
mode = 1
}
}
if (mode == 1) {
if (!isLightBlueAll && speed <= 0.01) {
isLightBlueAll = true
indexes4SlowDown.add(index)
ignore = true
return
}
if (!isRedBlue && firstV > 0 && speed > 0 && speed <= firstV / 2.0) {
isRedBlue = true
indexes4SlowDown.add(index)
}
}
} finally {
index++
oldV = speed
oldA = acc
val first = colors[0]
colors.add(0, first)
strategy = ColorfulStrategy(colors, true)
}
}
fun getStrategy(): Strategy = if (isEnable) strategy ?: Uniform(index) else SpeedUp(index)
fun check(speed: Double, acc: Double, total: Int) {
if (!isEnable){
return
}
if (sorted.isEmpty()) {
return
}
if (acc < 0) {
hasLessThan0 = true
}
val delta = (total * 0.35).toInt()
val last = total - delta
val entry = sorted.floorEntry(acc)
if (entry != null) {
if (index >= last - 1) {
if (startColor == Int.MAX_VALUE) {
startColor = entry.value
if (endEvaluator == null) {
endEvaluator = ArgbEvaluator()
}
colors += entry.value
} else {
if (endEvaluator != null) {
val fraction = (index - last) * 1.0f / delta
colors += endEvaluator!!.evaluate(fraction, startColor, COLOR_TRANSPARENT) as Int
}
}
} else {
colors += entry.value
}
}
index++
}
fun remove(acc: Double): List<Int> {
if (!isEnable) {
return emptyList()
}
if (sorted.isEmpty()) {
throw AssertionError("sorted map must not be null.")
}
val entry = sorted.floorEntry(acc)
if (entry != null) {
colors.remove(entry.value)
}
return ArrayList(colors)
}
private fun fill() {
var startValue = -4.0
var endValue = 0.0
val step = 0.01
var current = startValue
val evaluator = ArgbEvaluator()
val interceptor = AccelerateInterpolator()
var total = endValue - startValue
while (current <= endValue) {
val fraction = interceptor.getInterpolation(((current - startValue) / total).toFloat())
val colorValue = evaluator.evaluate(fraction, COLOR_RED_DARK, COLOR_BLUE) as Int
sorted[current] = colorValue
current += step
}
startValue = 0.01
endValue = 3.0
current = startValue
total = endValue - startValue
while (current <= endValue) {
val fraction = (current - startValue) / total
val colorValue = evaluator.evaluate(fraction.toFloat(), COLOR_BLUE, COLOR_BLUE_DARK) as Int
sorted[current] = colorValue
current += step
}
}
fun getStrategy(): Strategy = if (isEnable) { (strategy ?: DefaultStrategy()) } else DefaultStrategy()
}