Merge branch 'dev_robotaxi-d-app-module_280_220608_2.8.0' into dev_robotaxi-d-app-module_280_taxi_passenger

# Conflicts:
#	OCH/mogo-och-taxi-passenger/build.gradle
#	OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/model/TaxiPassengerModel.java
#	OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/network/TaxiPassengerServiceApi.java
This commit is contained in:
yangyakun
2022-06-23 21:30:39 +08:00
224 changed files with 3600 additions and 1262 deletions

View File

@@ -47,8 +47,8 @@ dependencies {
implementation rootProject.ext.dependencies.kotlinstdlibjdk7
implementation rootProject.ext.dependencies.androidxappcompat
implementation rootProject.ext.dependencies.arouter
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.2.1'
implementation rootProject.ext.dependencies.androidxrecyclerview
implementation rootProject.ext.dependencies.material
annotationProcessor rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.rxandroid
implementation rootProject.ext.dependencies.androidxconstraintlayout

View File

@@ -49,10 +49,11 @@ public class BusPassengerTrafficLightView extends IViewTrafficLight {
* 展示红绿灯预警
*
* @param checkLightId 0-都是默认1-红2-黄3-绿
* @param lightSource 1:云端下发2:自车感知
*/
@Override
public void showWarningTrafficLight(int checkLightId) {
super.showWarningTrafficLight(checkLightId);
public void showWarningTrafficLight(int checkLightId,int lightSource) {
super.showWarningTrafficLight(checkLightId,lightSource);
mCurrentLightId = checkLightId;
updateTrafficLightIcon(checkLightId);
}

View File

@@ -16,12 +16,12 @@ public class BusRoutesResult {
private int status;
//线路轨迹相关字段
public String csvFileUrl; //轨迹文件下载的cos url默认“”
public String csvFileMd5; //轨迹文件md5默认“”
public String txtFileUrl; //打点文件下载的cos url默认“”
public String txtFileMd5; //轨迹文件md5默认“”
public String csvFileUrl = ""; //轨迹文件下载的cos url默认“”
public String csvFileMd5 = ""; //轨迹文件md5默认“”
public String txtFileUrl = ""; //打点文件下载的cos url默认“”
public String txtFileMd5 = ""; //轨迹文件md5默认“”
public long contrailSaveTime; //上传轨迹完成时间戳ms用于MEC本地手动导入轨迹验证时不会被云端轨迹覆盖
public String carModel; //[optional] 车型号如红旗H9默认“”暂不加入校验逻辑、用于人工排查问题
public String carModel = ""; //[optional] 车型号如红旗H9默认“”暂不加入校验逻辑、用于人工排查问题
public int getLineId() {
return lineId;
@@ -48,6 +48,12 @@ public class BusRoutesResult {
", lineType=" + lineType +
", description='" + description + '\'' +
", status=" + status +
", csvFileUrl='" + csvFileUrl + '\'' +
", csvFileMd5='" + csvFileMd5 + '\'' +
", txtFileUrl='" + txtFileUrl + '\'' +
", txtFileMd5='" + txtFileMd5 + '\'' +
", contrailSaveTime=" + contrailSaveTime +
", carModel='" + carModel + '\'' +
'}';
}
}

View File

@@ -15,6 +15,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.amap.api.maps.model.LatLng;
import com.elegant.network.utils.GsonUtil;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.commons.voice.AIAssist;
@@ -508,20 +509,27 @@ public class BusOrderModel {
// == CallerAutoPilotStatusListenerManager.INSTANCE.getAutoPilotStatusInfo().getState()) {
// ToastUtils.showShort("自动驾驶状态为不可用!");
// }
AutopilotControlParameters currentAutopilot = new AutopilotControlParameters();
currentAutopilot.isSpeakVoice = !isRestart;
currentAutopilot.routeID = busRoutesResult.getLineId();
currentAutopilot.routeName = busRoutesResult.getName();
currentAutopilot.startName = PinYinUtil.getPinYinHeadChar(currentStation.getName());
currentAutopilot.endName = PinYinUtil.getPinYinHeadChar(nextStation.getName());
currentAutopilot.startLatLon = new AutopilotControlParameters
AutopilotControlParameters parameters = new AutopilotControlParameters();
parameters.isSpeakVoice = !isRestart;
parameters.routeID = busRoutesResult.getLineId();
parameters.routeName = busRoutesResult.getName();
parameters.startName = PinYinUtil.getPinYinHeadChar(currentStation.getName());
parameters.endName = PinYinUtil.getPinYinHeadChar(nextStation.getName());
parameters.startLatLon = new AutopilotControlParameters
.AutoPilotLonLat( currentStation.getLat(), currentStation.getLon() );
currentAutopilot.endLatLon = new AutopilotControlParameters
parameters.endLatLon = new AutopilotControlParameters
.AutoPilotLonLat( nextStation.getLat(), nextStation.getLon() );
currentAutopilot.vehicleType = VEHICLE_TYPE;
CallerLogger.INSTANCE.d( M_BUS + TAG, "开启自动驾驶====" + currentAutopilot.toString()
parameters.vehicleType = VEHICLE_TYPE;
if (parameters.autoPilotLine == null) {
parameters.autoPilotLine = new AutopilotControlParameters.AutoPilotLine(
busRoutesResult.getLineId(),
busRoutesResult.csvFileUrl, busRoutesResult.csvFileMd5,
busRoutesResult.txtFileUrl, busRoutesResult.txtFileMd5,
busRoutesResult.contrailSaveTime, busRoutesResult.carModel);
}
CallerLogger.INSTANCE.d( M_BUS + TAG, "开启自动驾驶====" + GsonUtil.jsonFromObject(parameters)
+" startLatLon="+currentStation.getName()+"endLatLon="+nextStation.getName());
CallerAutoPilotManager.INSTANCE.startAutoPilot(currentAutopilot);
CallerAutoPilotManager.INSTANCE.startAutoPilot(parameters);
triggerStartServiceEvent(isRestart, false);

View File

@@ -148,8 +148,7 @@ public interface IBusApiService {
Observable<BusQueryLinesResponse> queryBusLines(@Header ("appId") String appId, @Header("ticket") String ticket, @Query("sn") String sn);
@Headers( {"Content-type:application/json;charset=UTF-8"} )
// @POST( "/autopilot-car-hailing/location/v2/driver/heartbeat" )
@POST( "/autopilot-car-hailing/location/v2/driver/bus/heartbeat" )
@POST( "/autopilot-car-hailing/location/v2/driver/bus/saveLineCoordinate" )
Observable<BaseData> updateOrderRoute(@Header ("appId") String appId, @Header("ticket") String ticket, @Body BusRoutePlanningUpdateReqBean data);
}

View File

@@ -46,10 +46,11 @@ public class BusTrafficLightView extends IViewTrafficLight {
* 展示红绿灯预警
*
* @param checkLightId 0-都是默认1-红2-黄3-绿
* @param lightSource 1:云端下发2:自车感知
*/
@Override
public void showWarningTrafficLight(int checkLightId) {
super.showWarningTrafficLight(checkLightId);
public void showWarningTrafficLight(int checkLightId,int lightSource) {
super.showWarningTrafficLight(checkLightId,lightSource);
mCurrentLightId = checkLightId;
updateTrafficLightIcon(checkLightId);
}

View File

@@ -27,9 +27,9 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.2.1'
implementation rootProject.ext.dependencies.androidxccorektx
implementation rootProject.ext.dependencies.androidxappcompat
implementation rootProject.ext.dependencies.material
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

View File

@@ -0,0 +1,174 @@
package com.mogo.och.common.module.wigets.sfv;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.elegant.utils.UiThreadHandler;
public abstract class BaseSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
public static final int DEFAULT_FRAME_DURATION_MILLISECOND = 50;
private HandlerThread handlerThread;
private SurfaceViewHandler handler;
protected int frameDuration = DEFAULT_FRAME_DURATION_MILLISECOND;
private Canvas canvas;
private boolean isAlive;
public BaseSurfaceView(Context context) {
super(context);
init();
}
public BaseSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BaseSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
protected int getFrameDuration() {
return frameDuration;
}
protected void setFrameDuration(int frameDuration) {
this.frameDuration = frameDuration;
}
protected void init() {
getHolder().addCallback(this);
setBackgroundTransparent();
}
private void setBackgroundTransparent() {
getHolder().setFormat(PixelFormat.TRANSLUCENT);
setZOrderOnTop(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isAlive = true;
startDrawThread();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stopDrawThread();
isAlive = false;
}
private void stopDrawThread() {
isAlive = false;
handlerThread.quit();
handler = null;
}
private void startDrawThread() {
handlerThread = new HandlerThread("SurfaceViewThread");
handlerThread.start();
handler = new SurfaceViewHandler(handlerThread.getLooper());
handler.post(new DrawRunnable());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int originWidth = getMeasuredWidth();
int originHeight = getMeasuredHeight();
int width = widthMode == MeasureSpec.AT_MOST ? getDefaultWidth() : originWidth;
int height = heightMode == MeasureSpec.AT_MOST ? getDefaultHeight() : originHeight;
setMeasuredDimension(width, height);
Log.v("ttaylor", "BaseSurfaceView.onMeasure()" + " default Width=" + getDefaultWidth() + " default height=" + getDefaultHeight());
}
/**
* the width is used when wrap_content is set to layout_width
* the child knows how big it should be
*
* @return
*/
protected abstract int getDefaultWidth();
/**
* the height is used when wrap_content is set to layout_height
* the child knows how big it should be
*
* @return
*/
protected abstract int getDefaultHeight();
private class SurfaceViewHandler extends Handler {
public SurfaceViewHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
private class DrawRunnable implements Runnable {
@Override
public void run() {
if (!isAlive) {
return;
}
try {
canvas = getHolder().lockCanvas();
onFrameDraw(canvas);
} catch (Exception e) {
e.printStackTrace();
} finally {
getHolder().unlockCanvasAndPost(canvas);
onFrameDrawFinish();
}
handler.postDelayed(this, frameDuration);
}
}
/**
* it is will be invoked after one frame is drawn
*/
protected abstract void onFrameDrawFinish();
/**
* draw one frame to the surface by canvas
*
* @param canvas
*/
protected abstract void onFrameDraw(Canvas canvas);
protected void runOnUIThread( Runnable executor ) {
if ( executor == null ) {
return;
}
if ( Looper.myLooper() != Looper.getMainLooper() ) {
UiThreadHandler.post( executor );
} else {
executor.run();
}
}
}

View File

@@ -0,0 +1,5 @@
package com.mogo.och.common.module.wigets.sfv;
public interface FrameFinishCallback {
void onFinishCallback();
}

View File

@@ -0,0 +1,404 @@
package com.mogo.och.common.module.wigets.sfv;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.AttributeSet;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* a SurfaceView which draws bitmaps one after another like frame animation
*/
public class FrameSurfaceView extends BaseSurfaceView {
public static final int INVALID_INDEX = Integer.MAX_VALUE;
private int bufferSize = 3;
public static final String DECODE_THREAD_NAME = "DecodingThread";
public static final int INFINITE = -1;
//-1 means repeat infinitely
private int repeatTimes;
private int repeatedCount;
/**
* the resources of frame animation
*/
private List<Integer> bitmapIds = new ArrayList<>();
/**
* the index of bitmap resource which is decoding
*/
private int bitmapIdIndex;
/**
* the index of frame which is drawing
*/
private AtomicInteger frameIndex;
/**
* decoded bitmaps stores in this queue
* consumer is drawing thread, producer is decoding thread.
*/
private LinkedBlockingQueue decodedBitmaps = new LinkedBlockingQueue(bufferSize);
/**
* bitmaps already drawn by canvas stores in this queue
* consumer is decoding thread, producer is drawing thread.
*/
private LinkedBlockingQueue drawnBitmaps = new LinkedBlockingQueue(bufferSize);
/**
* the thread for decoding bitmaps
*/
private HandlerThread decodeThread;
/**
* the Runnable describes how to decode one bitmap
*/
private DecodeRunnable decodeRunnable;
/**
* this handler helps to decode bitmap one after another
*/
private Handler handler;
private BitmapFactory.Options options;
private Paint paint = new Paint();
private Rect srcRect;
private Rect dstRect = new Rect();
private int defaultWidth;
private int defaultHeight;
private FrameFinishCallback frameFinishCallback;
public FrameSurfaceView(Context context) {
super(context);
}
public FrameSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FrameSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setRepeatTimes(int repeatTimes) {
this.repeatTimes = repeatTimes;
}
@Override
protected void init() {
super.init();
options = new BitmapFactory.Options();
options.inMutable = true;
decodeThread = new HandlerThread(DECODE_THREAD_NAME);
frameIndex = new AtomicInteger();
frameIndex.set(INVALID_INDEX);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
dstRect.set(0, 0, getWidth(), getHeight());
}
@Override
protected int getDefaultWidth() {
return defaultWidth;
}
@Override
protected int getDefaultHeight() {
return defaultHeight;
}
@Override
protected void onFrameDrawFinish() {
}
/**
* set the duration of frame animation
*
* @param duration time in milliseconds
*/
public void setDuration(int duration) {
int frameDuration = duration / bitmapIds.size();
setFrameDuration(frameDuration);
}
/**
* set the materials of frame animation which is an array of bitmap resource id
*
* @param bitmapIds an array of bitmap resource id
*/
public void setBitmapIds(List<Integer> bitmapIds) {
if (bitmapIds == null || bitmapIds.size() == 0) {
return;
}
this.bitmapIds = bitmapIds;
//by default, take the first bitmap's dimension into consideration
getBitmapDimension(bitmapIds.get(bitmapIdIndex));
preloadFrames();
decodeRunnable = new DecodeRunnable(bitmapIdIndex, bitmapIds, options);
}
private void getBitmapDimension(int bitmapId) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(this.getResources(), bitmapId, options);
defaultWidth = options.outWidth;
defaultHeight = options.outHeight;
srcRect = new Rect(0, 0, defaultWidth, defaultHeight);
//we have to re-measure to make defaultWidth in use in onMeasure()
requestLayout();
}
/**
* load the first several frames of animation before it is started
*/
private void preloadFrames() {
decodeAndPutBitmap(bitmapIds.get(bitmapIdIndex++), options, new LinkedBitmap());
decodeAndPutBitmap(bitmapIds.get(bitmapIdIndex++), options, new LinkedBitmap());
}
/**
* recycle the bitmap used by frame animation.
* Usually it should be invoked when the ui of frame animation is no longer visible
*/
public void destroy() {
if (drawnBitmaps != null) {
drawnBitmaps.clear();
}
if (decodeThread != null) {
decodeThread.quit();
decodeThread = null;
}
if (handler != null) {
handler = null;
}
}
@Override
protected void onFrameDraw(Canvas canvas) {
clearCanvas(canvas);
if (!isStart()) {
return;
}
if (!isFinish()) {
drawOneFrame(canvas);
} else {
onFrameAnimationEnd();
if (repeatTimes != 0 && repeatTimes == INFINITE) {
start();
} else if (repeatedCount < repeatTimes) {
start();
repeatedCount++;
} else {
repeatedCount = 0;
}
}
}
/**
* draw a single frame which is a bitmap
*
* @param canvas
*/
private void drawOneFrame(Canvas canvas) {
LinkedBitmap linkedBitmap = getDecodedBitmap();
if (linkedBitmap != null) {
canvas.drawBitmap(linkedBitmap.bitmap, srcRect, dstRect, paint);
}
putDrawnBitmap(linkedBitmap);
frameIndex.incrementAndGet();
if(isFinish()&&frameFinishCallback!=null){
runOnUIThread(new Runnable() {
@Override
public void run() {
frameFinishCallback.onFinishCallback();
}
});
}
}
/**
* invoked when frame animation is done
*/
private void onFrameAnimationEnd() {
reset();
}
/**
* reset the index of frame, preparing for the next frame animation
*/
public void reset() {
frameIndex.set(INVALID_INDEX);
}
/**
* whether frame animation is finished
*
* @return true: animation is finished, false: animation is doing
*/
private boolean isFinish() {
return frameIndex.get() >= bitmapIds.size() - 1;
}
/**
* whether frame animation is started
*
* @return true: animation is started, false: animation is not started
*/
private boolean isStart() {
return frameIndex.get() != INVALID_INDEX;
}
/**
* start frame animation from the first frame
*/
public void start() {
frameIndex.compareAndSet(INVALID_INDEX, 0);
if (decodeThread == null) {
decodeThread = new HandlerThread(DECODE_THREAD_NAME);
}
if (!decodeThread.isAlive()) {
decodeThread.start();
}
if (handler == null) {
handler = new Handler(decodeThread.getLooper());
}
if (decodeRunnable != null) {
decodeRunnable.setIndex(0);
}
handler.post(decodeRunnable);
}
/**
* clear out the drawing on canvas,preparing for the next frame
* * @param canvas
*/
private void clearCanvas(Canvas canvas) {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
}
/**
* decode bitmap by BitmapFactory.decodeStream(), it is about twice faster than BitmapFactory.decodeResource()
*
* @param resId the bitmap resource
* @param options
* @return
*/
private Bitmap decodeBitmap(int resId, BitmapFactory.Options options) {
options.inScaled = false;
InputStream inputStream = getResources().openRawResource(resId);
return BitmapFactory.decodeStream(inputStream, null, options);
}
/**
* reuse bitmap in drawnBitmaps to decode new bitmap
*
* @param resId
* @param options
*/
private void decodedBitmapByReuse(int resId, BitmapFactory.Options options) {
LinkedBitmap linkedBitmap = getDrawnBitmap();
if (linkedBitmap == null) {
linkedBitmap = new LinkedBitmap();
}
options.inBitmap = linkedBitmap.bitmap;
decodeAndPutBitmap(resId, options, linkedBitmap);
}
/**
* decode bitmap and put it into decodedBitmaps
*
* @param resId
* @param options
* @param linkedBitmap
*/
private void decodeAndPutBitmap(int resId, BitmapFactory.Options options, LinkedBitmap linkedBitmap) {
Bitmap bitmap = decodeBitmap(resId, options);
linkedBitmap.bitmap = bitmap;
try {
decodedBitmaps.put(linkedBitmap);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void putDrawnBitmap(LinkedBitmap bitmap) {
drawnBitmaps.offer(bitmap);
}
/**
* get bitmap which already drawn by canvas
*
* @return
*/
private LinkedBitmap getDrawnBitmap() {
LinkedBitmap bitmap = null;
try {
bitmap = drawnBitmaps.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return bitmap;
}
/**
* get decoded bitmap in the decoded bitmap queue
* it might block due to new bitmap is not ready
*
* @return
*/
private LinkedBitmap getDecodedBitmap() {
LinkedBitmap bitmap = null;
try {
bitmap = decodedBitmaps.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return bitmap;
}
public FrameFinishCallback getFrameFinishCallback() {
return frameFinishCallback;
}
public void setFrameFinishCallback(FrameFinishCallback frameFinishCallback) {
this.frameFinishCallback = frameFinishCallback;
}
private class DecodeRunnable implements Runnable {
private int index;
private List<Integer> bitmapIds;
private BitmapFactory.Options options;
public DecodeRunnable(int index, List<Integer> bitmapIds, BitmapFactory.Options options) {
this.index = index;
this.bitmapIds = bitmapIds;
this.options = options;
}
public void setIndex(int index) {
this.index = index;
}
@Override
public void run() {
decodedBitmapByReuse(bitmapIds.get(index), options);
index++;
if (index < bitmapIds.size()) {
handler.post(this);
} else {
index = 0;
}
}
}
}

View File

@@ -0,0 +1,12 @@
package com.mogo.och.common.module.wigets.sfv;
import android.graphics.Bitmap;
/**
* a structure used by LinkedBlockingQueue to keep bitmap
*/
public class LinkedBitmap {
public Bitmap bitmap;
public LinkedBitmap next;
}

View File

@@ -0,0 +1,205 @@
package com.mogo.och.common.module.wigets.sfv;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class LinkedBlockingQueue {
/**
* Current number of elements
*/
private final AtomicInteger count = new AtomicInteger();
/**
* Lock held by take, poll, etc
*/
private final ReentrantLock takeLock = new ReentrantLock();
/**
* Wait queue for waiting takes
*/
private final Condition notEmpty = takeLock.newCondition();
/**
* Lock held by put, offer, etc
*/
private final ReentrantLock putLock = new ReentrantLock();
/**
* Wait queue for waiting puts
*/
private final Condition notFull = putLock.newCondition();
/**
* The capacity bound, or Integer.MAX_VALUE if none
*/
private final int capacity;
/**
* the first element in the queue
*/
private LinkedBitmap head;
/**
* the last element int the queue
*/
private LinkedBitmap tail;
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
public void put(LinkedBitmap bitmap) throws InterruptedException {
if (bitmap == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
* out by lock), and we (or some other waiting put) are
* signalled if it ever changes from capacity. Similarly
* for all other uses of count in other wait guards.
*/
while (count.get() == capacity) {
notFull.await();
}
enqueue(bitmap);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
public boolean offer(LinkedBitmap bitmap) {
if (bitmap == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
int c = -1;
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(bitmap);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
public LinkedBitmap take() throws InterruptedException {
LinkedBitmap x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
/**
* insert element into the end of queue
*
* @param bitmap
*/
private void enqueue(LinkedBitmap bitmap) {
if (head == null) {
head = bitmap;
tail = bitmap;
bitmap.next = null;
} else {
tail.next = bitmap;
bitmap.next = null;
}
}
/**
* get and remove the first element of the queue
*
* @return
*/
private LinkedBitmap dequeue() {
LinkedBitmap p = head;
if (p == null) {
return null;
} else {
head = head.next;
}
return p;
}
/**
* Signals a waiting take. Called only from put/offer (which do not
* otherwise ordinarily lock takeLock.)
*/
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
/**
* Signals a waiting put. Called only from take/poll.
*/
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
/**
* recycle the bitmaps one by one
*/
public void clear() {
LinkedBitmap p = head;
if (p == null) {
return;
}
while (p != null) {
if (p.bitmap != null) {
p.bitmap.recycle();
}
p.bitmap = null;
p = p.next;
}
}
public Integer getCount() {
return count.get();
}
}

View File

@@ -48,7 +48,7 @@ dependencies {
implementation rootProject.ext.dependencies.androidxappcompat
implementation rootProject.ext.dependencies.arouter
implementation rootProject.ext.dependencies.androidxrecyclerview
implementation 'com.google.android.material:material:1.2.1'
implementation rootProject.ext.dependencies.material
implementation rootProject.ext.dependencies.flexbox
annotationProcessor rootProject.ext.dependencies.aroutercompiler
implementation rootProject.ext.dependencies.rxandroid

View File

@@ -0,0 +1,23 @@
package com.mogo.och.taxi.passenger.bean;
/**
* Created by pangfan on 2021/8/19
* 司机端准备好或者乘客已验证上车请求参数
*/
public class TaxiPassengerStartReqBean {
public String orderNo;
public String sn;
public TaxiPassengerStartReqBean.Result loc;
public static class Result {
public Double lat;
public Double lon;
}
public TaxiPassengerStartReqBean(String sn, String orderNo, TaxiPassengerStartReqBean.Result point) {
this.sn = sn;
this.orderNo = orderNo;
this.loc = point;
}
}

View File

@@ -22,4 +22,6 @@ public interface IOCHTaxiPassengerOrderStatusCallback {
//当前路名字
void onCurrentRoadName(String currentRoadName);
// 司机已确认开启自动驾驶环境
void onDriverHasCheckedPilotCondition(boolean isBoarded);
}

View File

@@ -42,5 +42,21 @@ class TaxiPassengerConst {
//实时查询订单剩余时间 和 剩余里程 轮询间隔2s
const val LOOP_CALCULATEROUTE_2S = 2 * 1000L
// 开始服务启动自动驾驶等待时间(埋点上传)
const val LOOP_PERIOD_15S = 15 * 1000L
// 埋点key接管后点击'自动驾驶'按钮启动
const val EVENT_KEY_RESTART_AUTOPILOT = "event_key_och_taxi_restart_autopilot"
// 埋点key开始服务开启自动驾驶成功/失败)
const val EVENT_KEY_START_SERVICE = "event_key_och_taxi_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_ORDER_NUMBER = "order_num"
const val EVENT_PARAM_START_RESULT = "start_autopilot" // true/false
const val EVENT_PARAM_PLATE_NUM = "plate_number" // 车牌号
const val EVENT_PARAM_ENV_ONLINE = "env_online" // 是否线上环境true/false
}
}

View File

@@ -14,12 +14,13 @@ import com.elegant.network.utils.GsonUtil;
import com.mogo.aicloud.services.socket.MogoAiCloudSocketManager;
import com.mogo.cloud.commons.utils.CoordinateUtils;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.map.MogoLatLng;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotPlanningListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.api.v2x.LimitingVelocityListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
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.v2x.CallLimitingVelocityListenerManager;
@@ -32,11 +33,13 @@ import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.map.navi.IMogoCarLocationChangedListener2;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.och.common.module.utils.CoordinateCalculateRouteUtil;
import com.mogo.och.common.module.utils.PinYinUtil;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerBaseRespBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRemainingResp;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrdersInServiceQueryRespBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerQueryOrderRouteResp;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerStartReqBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerScoreUpdateOrderReqBean;
import com.mogo.och.taxi.passenger.callback.IOCHTaxiPassengerADASStatusCallback;
import com.mogo.och.taxi.passenger.callback.IOCHTaxiPassengerAutopilotPlanningCallback;
@@ -51,6 +54,7 @@ import com.mogo.och.taxi.passenger.constant.TaxiPassengerOrderStatusEnum;
import com.mogo.och.taxi.passenger.network.TaxiPassengerServiceCallback;
import com.mogo.och.taxi.passenger.network.TaxiPassengerServiceManager;
import com.mogo.aicloud.services.socket.IMogoLifecycleListener;
import com.mogo.och.taxi.passenger.utils.TaxiPassengerAnalyticsManager;
import com.mogo.service.intent.IMogoIntentListener;
import com.mogo.service.statusmanager.IMogoStatusChangedListener;
import com.mogo.service.statusmanager.StatusDescriptor;
@@ -70,7 +74,6 @@ import io.reactivex.plugins.RxJavaPlugins;
import mogo.telematics.pad.MessagePad;
import mogo_msg.MogoReportMsg;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI_P;
/**
@@ -158,31 +161,31 @@ public class TaxiPassengerModel implements IOCHTaxiPassengerNaviChangedCallback
public void accept(Throwable e) {
if (e instanceof UndeliverableException) {
e = e.getCause();
CallerLogger.INSTANCE.d(M_TAXI + TAG, "UndeliverableException");
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "UndeliverableException");
}
if ((e instanceof IOException)) {//
// fine, irrelevant network problem or API that throws on cancellation
CallerLogger.INSTANCE.d(M_TAXI + TAG, "IOException");
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "IOException");
return;
}
if (e instanceof InterruptedException) {
// fine, some blocking code was interrupted by a dispose call
CallerLogger.INSTANCE.d(M_TAXI + TAG, "InterruptedException");
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "InterruptedException");
return;
}
if ((e instanceof NullPointerException) || (e instanceof IllegalArgumentException)) {
// that's likely a bug in the application
CallerLogger.INSTANCE.d(M_TAXI + TAG, "NullPointerException or IllegalArgumentException");
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "NullPointerException or IllegalArgumentException");
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
return;
}
if (e instanceof IllegalStateException) {
// that's a bug in RxJava or in a custom operator
CallerLogger.INSTANCE.d(M_TAXI + TAG, "IllegalStateException");
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "IllegalStateException");
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
return;
}
CallerLogger.INSTANCE.d(M_TAXI + TAG,"Undeliverable exception");
CallerLogger.INSTANCE.d(M_TAXI_P + TAG,"Undeliverable exception");
}
});
@@ -505,11 +508,11 @@ public class TaxiPassengerModel implements IOCHTaxiPassengerNaviChangedCallback
if (state == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING) {
if (mADASStatusCallback != null) mADASStatusCallback.onAutopilotRunning();
if (mCurrentOCHOrder != null
&& getCurOrderStatus() == TaxiPassengerOrderStatusEnum.ArriveAtStart
&& getCurOrderStatus() == TaxiPassengerOrderStatusEnum.UserArriveAtStart
&& state != mPrevAPStatus) {
// 当高频返回autopilot 2时不重复调用订单状态变更
mPrevAPStatus = state; // 每个状态单独赋值解决无订单时已经是2的状态导致的新订单来时无法进入此逻辑更新状态
// updateOCHOrderStatus(OrderStatusEnum.OnTheWayToEndStation);
startServicePilotDone();
}
} else if (state == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE) {
mPrevAPStatus = state;
@@ -597,9 +600,9 @@ public class TaxiPassengerModel implements IOCHTaxiPassengerNaviChangedCallback
*/
public void startOrStopRouteAndWipe(boolean isStart){
if (isStart){
TaxiPassengerModelLoopManager.getInstance().startOrStopRouteAndWipe();
TaxiPassengerModelLoopManager.getInstance().startRouteAndWipe();
}else {
TaxiPassengerModelLoopManager.getInstance().stopOrStopRouteAndWipe();
TaxiPassengerModelLoopManager.getInstance().stopRouteAndWipe();
}
}
@@ -781,6 +784,102 @@ public class TaxiPassengerModel implements IOCHTaxiPassengerNaviChangedCallback
});
}
public void loopQueryPilotStatus(){
if (mCurrentOCHOrder == null) return;
TaxiPassengerServiceManager.getInstance().queryPilotStatus(mContext, mCurrentOCHOrder.orderNo,
new TaxiPassengerServiceCallback<TaxiPassengerBaseRespBean>() {
@Override
public void onSuccess(TaxiPassengerBaseRespBean data) {
if (data != null && data.code == 0 && data.data.equals(true)) {
updateAutopilotStatus(true);
startOrStopReadyToAutopilotoop(false);
}
}
@Override
public void onFail(int code, String msg) {
updateAutopilotStatus(false);
}
});
}
public void updateAutopilotStatus(boolean isBoarded){
if (mOrderStatusCallbackMap.size() > 0) {
for (IOCHTaxiPassengerOrderStatusCallback callback :mOrderStatusCallbackMap.values()){
callback.onDriverHasCheckedPilotCondition(isBoarded);
}
}
}
public void startDriverReadyToAutopilotLoop(){
if (NetworkUtils.isConnected(mContext)) {
startOrStopReadyToAutopilotoop(true);
}
}
public void startOrStopReadyToAutopilotoop(boolean isStart) {
if (isStart){
TaxiPassengerModelLoopManager.getInstance().startReadyToAutopilot();
}else {
TaxiPassengerModelLoopManager.getInstance().stopReadyToAutopilot();
}
}
public void startServicePilotDone(){
if (mCurrentOCHOrder == null) return;
TaxiPassengerStartReqBean.Result result = new TaxiPassengerStartReqBean.Result();
result.lat = mLatitude;
result.lon = mLongitude;
TaxiPassengerServiceManager.getInstance().startServicePilotDone(mContext,
mCurrentOCHOrder.orderNo, result,
new TaxiPassengerServiceCallback<TaxiPassengerBaseRespBean>(){
@Override
public void onSuccess(TaxiPassengerBaseRespBean data) {
}
@Override
public void onFail(int code, String msg) {
ToastUtils.showShort(msg);
}
});
}
public void startAutopilot() {
if (!checkCurrentOCHOrder()) {
CallerLogger.INSTANCE.e(M_TAXI_P + TAG, "no order or order is empty.");
ToastUtils.showShort("当前订单不存在或异常!");
return;
}
if (mCurrentOCHOrder.orderStatus != TaxiPassengerOrderStatusEnum.UserArriveAtStart.getCode()) {
ToastUtils.showShort("当前订单状态异常!");
return;
}
double startWgsLon = mCurrentOCHOrder.startSitePoint.get(0);
double startWgsLat = mCurrentOCHOrder.startSitePoint.get(1);
double endWgsLon = mCurrentOCHOrder.endSitePoint.get(0);
double endWgsLat = mCurrentOCHOrder.endSitePoint.get(1);
AutopilotControlParameters parameters = new AutopilotControlParameters();
parameters.vehicleType = mCurrentOCHOrder.businessType;
parameters.startName = PinYinUtil.getPinYinHeadChar(mCurrentOCHOrder.startSiteAddr); // 起点名称拼音首字母大写科学城B区2号门KXCBQ2HM
parameters.endName = PinYinUtil.getPinYinHeadChar(mCurrentOCHOrder.endSiteAddr); // 终点名称拼音首字母大写科学城C区三号门KXCCQSHM
parameters.startLatLon = new AutopilotControlParameters.AutoPilotLonLat(startWgsLat, startWgsLon);
parameters.endLatLon = new AutopilotControlParameters.AutoPilotLonLat(endWgsLat, endWgsLon);
CallerAutoPilotManager.INSTANCE.startAutoPilot(parameters);
CallerLogger.INSTANCE.d(M_TAXI_P + TAG, "start autopilot with parameter: %s"
, GsonUtil.jsonFromObject(parameters)
+ " ,startSiteName=" + mCurrentOCHOrder.startSiteAddr
+ " ,endSiteName=" + mCurrentOCHOrder.endSiteAddr);
TaxiPassengerAnalyticsManager.getInstance().triggerStartAutopilotEvent(false, false,
mCurrentOCHOrder.startSiteAddr, mCurrentOCHOrder.endSiteAddr, mCurrentOCHOrder.orderNo);
}
private void runOnUIThread(Runnable executor) {
if (executor == null) {
return;

View File

@@ -32,12 +32,34 @@ public class TaxiPassengerModelLoopManager {
private Disposable mInAndWaitServiceDisposable; //进行中、待服务订单列表轮询
private Disposable mQueryOrderRemainingDisposable; //心跳轮询
private Disposable mRouteWipeDisposable; //轨迹擦除
private Disposable mReadyToAutopilotDisposable; //轨迹擦除
public void startOrStopRouteAndWipe() {
public void startReadyToAutopilot() {
if (mReadyToAutopilotDisposable != null && !mReadyToAutopilotDisposable.isDisposed()) {
return;
}
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "startReadyToAutopilot()");
mReadyToAutopilotDisposable = Observable.interval(TaxiPassengerConst.LOOP_DELAY,
TaxiPassengerConst.LOOP_PERIOD_1S, TimeUnit.MILLISECONDS)
.map((aLong -> aLong + 1))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> TaxiPassengerModel.getInstance().loopQueryPilotStatus());
}
public void stopReadyToAutopilot() {
if (mReadyToAutopilotDisposable != null) {
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "stopReadyToAutopilot()");
mReadyToAutopilotDisposable.dispose();
mReadyToAutopilotDisposable = null;
}
}
public void startRouteAndWipe() {
if (mRouteWipeDisposable != null && !mRouteWipeDisposable.isDisposed()) {
return;
}
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "startOrStopRouteWipe()");
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "startRouteAndWipe()");
mRouteWipeDisposable = Observable.interval(TaxiPassengerConst.LOOP_DELAY,
TaxiPassengerConst.LOOP_PERIOD_1S, TimeUnit.MILLISECONDS)
.map((aLong -> aLong + 1))
@@ -46,9 +68,9 @@ public class TaxiPassengerModelLoopManager {
.subscribe(aLong -> TaxiPassengerModel.getInstance().loopRouteAndWipe());
}
public void stopOrStopRouteAndWipe() {
public void stopRouteAndWipe() {
if (mRouteWipeDisposable != null) {
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "stopOrStopRouteWipe()");
CallerLogger.INSTANCE.i(M_TAXI_P + TAG, "stopRouteAndWipe()");
mRouteWipeDisposable.dispose();
mRouteWipeDisposable = null;
}

View File

@@ -8,6 +8,7 @@ import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrdersInServiceQueryRespBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerQueryOrderRouteResp;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerScoreUpdateOrderReqBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerStartReqBean;
import io.reactivex.Observable;
import retrofit2.http.Body;
@@ -101,4 +102,27 @@ interface TaxiPassengerServiceApi {
@GET( "/autopilot-car-hailing/evaluation/label/driver/taxi/listByStar" )
Observable<TaxiPassengerAllStarWorld> getWorldByStar(@Header ("appId") String appId, @Header("ticket") String ticket,@Query("star") String star);
/**
* 查询司机是否已确认可开启自动驾驶
* @param appId
* @param ticket
* @param orderNo
* @return
*/
@Headers( {"Content-type:application/json;charset=UTF-8"} )
@GET( "/autopilot-car-hailing/cab/flow/v1/driver/taxi/pilot/status" )
Observable<TaxiPassengerBaseRespBean> queryPilotStatus(@Header ("appId") String appId
, @Header("ticket") String ticket,@Query("orderNo") String orderNo);
/**
* 乘客屏启动自动驾驶成功
* @param appId
* @param ticket
* @param data
* @return
*/
@Headers( {"Content-type:application/json;charset=UTF-8"} )
@POST( "/autopilot-car-hailing/cab/flow/v1/driver/taxi/passenger/startServicePilot" )
Observable<TaxiPassengerBaseRespBean> startServicePilotDone(@Header ("appId") String appId
, @Header("ticket") String ticket,@Body TaxiPassengerStartReqBean data);
}

View File

@@ -20,6 +20,7 @@ import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrderQueryRespBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerOrdersInServiceQueryRespBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerQueryOrderRouteResp;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerScoreUpdateOrderReqBean;
import com.mogo.och.taxi.passenger.bean.TaxiPassengerStartReqBean;
import com.mogo.och.taxi.passenger.constant.TaxiPassengerConst;
import com.mogo.commons.debug.DebugConfig;
@@ -176,7 +177,29 @@ public class TaxiPassengerServiceManager {
,MoGoAiCloudClientConfig.getInstance().getToken())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getSubscribeImpl(context, callback, "checkPhoneAndUpdateOrderStatus"));
.subscribe(getSubscribeImpl(context, callback, "arrivedAndScore"));
}
public void queryPilotStatus(Context context, String orderNo
,TaxiPassengerServiceCallback<TaxiPassengerBaseRespBean> callback){
mOCHTaxiServiceApi.queryPilotStatus(
MoGoAiCloudClientConfig.getInstance().getServiceAppId()
,MoGoAiCloudClientConfig.getInstance().getToken()
,orderNo)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getSubscribeImpl(context, callback, "queryPilotStatus"));
}
public void startServicePilotDone(Context context,String orderNo,TaxiPassengerStartReqBean.Result loc
,TaxiPassengerServiceCallback<TaxiPassengerBaseRespBean> callback){
mOCHTaxiServiceApi.startServicePilotDone(MoGoAiCloudClientConfig.getInstance().getServiceAppId()
,MoGoAiCloudClientConfig.getInstance().getToken()
,new TaxiPassengerStartReqBean(MoGoAiCloudClientConfig.getInstance().getSn()
,orderNo,loc))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getSubscribeImpl(context,callback,"startServicePilotDone"));
}
public void getWorldByStar(Context context,String start,TaxiPassengerServiceCallback<TaxiPassengerAllStarWorld> callback){
mOCHTaxiServiceApi.getWorldByStar(

View File

@@ -142,19 +142,28 @@ public class BaseTaxiPassengerPresenter extends Presenter<TaxiPassengerBaseFragm
}
@Override
public void onDriverHasCheckedPilotCondition(boolean isBoarded) {
CallerLogger.INSTANCE.d(M_TAXI_P+TAG,"isBoarded = "+isBoarded);
runOnUIThread(() -> {
mView.updateStartAutopilotBtnStatus(isBoarded);
});
}
private void updateOrderView(TaxiPassengerOrderQueryRespBean.Result order) {
CallerLogger.INSTANCE.d(M_TAXI_P+TAG,"updateOrderView = "+order.orderStatus);
// 70 取消订单
if (TaxiPassengerOrderStatusEnum.Cancel.getCode() == order.orderStatus){
runOnUIThread(() -> {
mView.showOrHideServingOrderFragment(false);
// mView.showOrHideStartAutopilotView(false);
mView.showOrHideStartAutopilotView(false,false);
mView.showOrHidePressengerCheckPager(false, "",
"", "", "", "");
mView.showOrHideArrivedEndLayout(false,"","");
});
TaxiPassengerModel.getInstance().recoverNaviInfo();
TaxiPassengerGeocodeSearchModel.getInstance(getContext()).destroyGeocodeSearch();
TaxiPassengerModel.getInstance().startOrStopReadyToAutopilotoop(false);
return;
}
// 20 司机到达上车点
@@ -166,13 +175,23 @@ public class BaseTaxiPassengerPresenter extends Presenter<TaxiPassengerBaseFragm
return;
}
// TODO: 2022/6/10 若乘客端确认已经上车,则显示开始行程按钮 并且不可点击 暗
// TODO: 2022/6/10 若司机端已经确认,则显示开始行程按钮 并且可点击
//TODO: 2022/6/10 若订单取消或者隐藏则隐藏开始行程按钮
// TODO: 2022/6/10 乘客已上车 需要开启轮询司机确认可以开启自动驾驶的接口
// TODO: 2022/6/10 若司机端已经确认,则显示开始行程按钮 并且可点击,第二步的轮询停止
//TODO: 2022/6/10 若订单取消或者隐藏则关掉开始行程界面,轮询也要取消
if (TaxiPassengerOrderStatusEnum.UserArriveAtStart.getCode() == order.orderStatus){
// mView.showOrHideStartAutopilotView(true);
runOnUIThread(() ->{
CallerLogger.INSTANCE.d(M_TAXI_P+TAG,"UserArriveAtStart");
mView.showOrHideStartAutopilotView(true,false);
});
//开启轮询司机是否已准备好开启自动驾驶的环境
TaxiPassengerModel.getInstance().startDriverReadyToAutopilotLoop();
}
if (TaxiPassengerOrderStatusEnum.OnTheWayToEnd.getCode() == order.orderStatus){
// mView.showOrHideStartAutopilotView(false);
runOnUIThread(() ->{
mView.showOrHideServingOrderFragment(true);
mView.showOrHideStartAutopilotView(false,false);
});
TaxiPassengerModel.getInstance().startOrStopReadyToAutopilotoop(false);
}
// 30 用户到达上车点 并通过了手机号后四位验证
// 40 服务中
@@ -182,7 +201,6 @@ public class BaseTaxiPassengerPresenter extends Presenter<TaxiPassengerBaseFragm
mView.showOrHideArrivedEndLayout(false,"","");
mView.showOrHidePressengerCheckPager(false, "",
"", "", "", "");
mView.showOrHideServingOrderFragment(true);
});
return;
}
@@ -221,4 +239,11 @@ public class BaseTaxiPassengerPresenter extends Presenter<TaxiPassengerBaseFragm
TaxiPassengerModel.getInstance().arrivedAndScore(taxiPassengerScoreUpdateOrderReqBean,aBoolean -> mView.showArrivedEndLayout2Thank(aBoolean));
}
/**
* 开启自动驾驶
*/
public void startAutopilot(){
TaxiPassengerModel.getInstance().startAutopilot();
}
}

View File

@@ -120,6 +120,11 @@ public class TaxiPassengerServingOrderPresenter extends Presenter<TaxiPassengerS
runOnUIThread(() -> mView.onCurrentRoadName(currentRoadName));
}
@Override
public void onDriverHasCheckedPilotCondition(boolean isBoarded) {
}
@Override
public void onVRModeChanged(boolean isVRMode) {

View File

@@ -1,8 +1,10 @@
package com.mogo.och.taxi.passenger.ui;
import android.animation.ObjectAnimator;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -154,6 +156,7 @@ public class TaxiPassengerBaseFragment extends MvpFragment<TaxiPassengerBaseFrag
super.onResume();
// mPresenter.startOrStopOrderLoop();
// showOrHideServingOrderFragment(true);
// showOrHideStartAutopilotView(true,true);
}
/**
@@ -174,29 +177,21 @@ public class TaxiPassengerBaseFragment extends MvpFragment<TaxiPassengerBaseFrag
private int mPrevAPStatus = -1;
public void onAutopilotStatusChanged(int status) {
getActivity().runOnUiThread(() -> {
// if (isStarting && IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING != status) {
// // 1. 主动开启自动驾驶中不为2为0、1则继续loading
// return;
// }
// if (isStarting && IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING == status
// && mPrevAPStatus != status) {
// // 2. 主动开启自动驾驶中为2则停止loading并isStarting = false
// startAutopilotDone(true);
// return;
// }
if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING != status) {
// 1. 主动开启自动驾驶中不为2为0、1则继续loading
return;
}
if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING == status
&& mPrevAPStatus != status) {
// 2. 主动开启自动驾驶中为2则停止loading并isStarting = false
if ( mStartAutopilotView != null && mStartAutopilotView.get() != null){
mStartAutopilotView.get().startOrStopLoadingAnim(false);
}
}
// 3. 其他过程直接更新
if (mPrevAPStatus != status){
autopilotStatusAnimchanged(status);
}
// if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING == mPrevAPStatus) {
// if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE == status) {
// // 2->1
//// AIAssist.getInstance(getContext()).speakTTSVoice("已进入人工驾驶模式");
// } else if (IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE == status) {
// // 2->0
//// AIAssist.getInstance(getContext()).speakTTSVoice("自动驾驶已停止,请人工接管");
// }
// }
mPrevAPStatus = status;
});
}
@@ -280,8 +275,6 @@ public class TaxiPassengerBaseFragment extends MvpFragment<TaxiPassengerBaseFrag
}
}
/**
* 显示或者隐藏乘客可点击自动驾驶页面
* 乘客验证成功,页面显示,按钮置于不可点击
@@ -293,10 +286,10 @@ public class TaxiPassengerBaseFragment extends MvpFragment<TaxiPassengerBaseFrag
if (isShow){
if (mStartAutopilotView == null || mStartAutopilotView.get() == null){
mStartAutopilotView = new WeakReference<>(new TaxiPassengerStartAutopilotView(getContext()));
mStartAutopilotView.get().setOnClickStartAutopilotBtnCallback(this);
}
OverlayViewUtils.showOverlayView(getActivity(),mStartAutopilotView.get());
mStartAutopilotView.get().setOnClickStartAutopilotBtnCallback(this);
mStartAutopilotView.get().updateStartAutopilotBtnStatus(isClickable);
updateStartAutopilotBtnStatus(isClickable);
}else {
if (mStartAutopilotView == null || mStartAutopilotView.get() == null){
return;
@@ -306,6 +299,13 @@ public class TaxiPassengerBaseFragment extends MvpFragment<TaxiPassengerBaseFrag
}
}
public void updateStartAutopilotBtnStatus(boolean isClickable){
if (mStartAutopilotView == null || mStartAutopilotView.get() == null){
return;
}
mStartAutopilotView.get().updateStartAutopilotBtnStatus(isClickable);
}
/**
* 显示或者隐藏到达乘客站点的洁面
* ① 取消订单 可有可无
@@ -387,6 +387,7 @@ public class TaxiPassengerBaseFragment extends MvpFragment<TaxiPassengerBaseFrag
@Override
public void onClickCallback() {
//todo 点击开始自动驾驶按钮
mPresenter.startAutopilot();
}
}

View File

@@ -1,16 +1,21 @@
package com.mogo.och.taxi.passenger.ui;
import android.content.Context;
import android.util.AttributeSet;
import android.graphics.drawable.AnimationDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.elegant.utils.UiThreadHandler;
import com.mogo.eagle.core.utilcode.util.ToastUtils;
import com.mogo.och.common.module.wigets.OCHBorderShadowLayout;
import com.mogo.och.common.module.wigets.sfv.FrameFinishCallback;
import com.mogo.och.common.module.wigets.sfv.FrameSurfaceView;
import com.mogo.och.taxi.passenger.R;
import com.mogo.och.taxi.passenger.callback.ITPClickStartAutopilotCallback;
import com.mogo.och.taxi.passenger.model.TaxiPassengerModel;
import java.util.Arrays;
/**
* @author: wangmingjun
@@ -19,25 +24,136 @@ import com.mogo.och.taxi.passenger.callback.ITPClickStartAutopilotCallback;
public class TaxiPassengerStartAutopilotView extends RelativeLayout implements View.OnClickListener {
private TextView mStartAutopilotBtn;
// private ImageView mAutopilotStartingImage;
private ITPClickStartAutopilotCallback mClickCallback;
public boolean isStarting = false;
private AnimationDrawable mAnimationBtnDrawable;
private AnimationDrawable mAnimationStartingDrawable;
private static final long TIMER_START_AUTOPILOT_INTERVAL = 10 * 1000L;
private Context mContext;
private View view;
private FrameSurfaceView svCarStartingFrame;
private FrameSurfaceView svBtnBgFrame;
private Integer[] btnBgAnimIds = new Integer[]{
R.drawable.image_00000,
R.drawable.image_00001,
R.drawable.image_00002,
R.drawable.image_00003,
R.drawable.image_00004,
R.drawable.image_00005,
R.drawable.image_00006,
R.drawable.image_00007,
R.drawable.image_00008,
R.drawable.image_00009,
R.drawable.image_00010,
R.drawable.image_00011,
R.drawable.image_00012,
R.drawable.image_00013,
R.drawable.image_00014,
R.drawable.image_00015,
R.drawable.image_00016,
R.drawable.image_00017,
R.drawable.image_00018,
R.drawable.image_00019,
R.drawable.image_00020,
R.drawable.image_00021,
R.drawable.image_00022,
R.drawable.image_00023,
R.drawable.image_00024,
R.drawable.image_00025,
R.drawable.image_00026,
R.drawable.image_00027,
R.drawable.image_00028,
R.drawable.image_00029,
R.drawable.image_00030,
R.drawable.image_00031,
R.drawable.image_00032,
R.drawable.image_00033,
R.drawable.image_00034,
R.drawable.image_00035,
R.drawable.image_00036,
R.drawable.image_00037,
R.drawable.image_00038,
R.drawable.image_00039,
R.drawable.image_00040,
R.drawable.image_00041,
R.drawable.image_00042,
R.drawable.image_00043,
R.drawable.image_00044,
R.drawable.image_00045,
R.drawable.image_00046,
R.drawable.image_00047,
R.drawable.image_00048,
R.drawable.image_00049,
R.drawable.image_00050,
R.drawable.image_00051,
R.drawable.image_00052,
R.drawable.image_00053,
R.drawable.image_00054,
R.drawable.image_00055,
R.drawable.image_00056,
R.drawable.image_00057,
R.drawable.image_00058,
R.drawable.image_00059,
R.drawable.image_00060,
R.drawable.image_00061,
R.drawable.image_00062,
R.drawable.image_00063,
R.drawable.image_00064,
R.drawable.image_00065,
R.drawable.image_00066,
R.drawable.image_00067,
R.drawable.image_00068,
R.drawable.image_00069,
R.drawable.image_00070,
R.drawable.image_00071,
R.drawable.image_00072,
R.drawable.image_00073,
R.drawable.image_00074
};
private Integer[] startingAnimIds = new Integer[]{
R.drawable.light_00000,
R.drawable.light_00001,
R.drawable.light_00002,
R.drawable.light_00003,
R.drawable.light_00004,
R.drawable.light_00005,
R.drawable.light_00006,
R.drawable.light_00007,
R.drawable.light_00008,
R.drawable.light_00009,
R.drawable.light_00010,
R.drawable.light_00011,
R.drawable.light_00012,
R.drawable.light_00013
};
public TaxiPassengerStartAutopilotView(Context context) {
super(context);
}
public TaxiPassengerStartAutopilotView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TaxiPassengerStartAutopilotView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initView(context);
}
private void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.taxi_p_start_autopilot_view, this, true);
mStartAutopilotBtn = findViewById(R.id.taxi_p_start_autopilot);
view = LayoutInflater.from(context).inflate(R.layout.taxi_p_start_autopilot_view, this,true);
mStartAutopilotBtn = view.findViewById(R.id.taxi_p_start_autopilot);
mStartAutopilotBtn.setOnClickListener(this);
// mAutopilotStartingImage = view.findViewById(R.id.taxi_p_autopilot_starting);
svCarStartingFrame = view.findViewById(R.id.taxi_p_autopilot_starting);
svCarStartingFrame.setBitmapIds(Arrays.asList(startingAnimIds));
svCarStartingFrame.setDuration(1680);
svBtnBgFrame = view.findViewById(R.id.taxi_p_start_autopilot_btn_sfv);
svBtnBgFrame.setBitmapIds(Arrays.asList(btnBgAnimIds));
svBtnBgFrame.setDuration(7500);
svCarStartingFrame.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
TaxiPassengerModel.getInstance().startServicePilotDone();
return false;
}
});
}
public void setOnClickStartAutopilotBtnCallback(ITPClickStartAutopilotCallback clickCallback){
@@ -47,11 +163,139 @@ public class TaxiPassengerStartAutopilotView extends RelativeLayout implements V
@Override
public void onClick(View v) {
if (v.getId() == R.id.taxi_p_start_autopilot){
ToastUtils.showShort("等待接口。。。。");
//开启动画和自动驾驶
if (!(boolean)mStartAutopilotBtn.getTag()){
ToastUtils.showLong(R.string.taxi_p_start_autopilot_un_click_tip);
return;
}
if (!isStarting){
startOrStopLoadingAnim(true);
if (mClickCallback != null) mClickCallback.onClickCallback();
}
}
}
public void updateStartAutopilotBtnStatus(boolean isClickable){
mStartAutopilotBtn.setClickable(isClickable);
svCarStartingFrame.setBackgroundResource(R.drawable.light_00000);
if (mStartAutopilotBtn == null) return;
mStartAutopilotBtn.setTag(isClickable);
mStartAutopilotBtn.setText(
mContext.getResources().getString(R.string.taxi_p_start_autopilot_txt));
if (isClickable){ //高亮可点击状态下UI
mStartAutopilotBtn.setTextColor(
mContext.getResources().getColor(R.color.taxi_p_start_autopilot_txt_color));
mStartAutopilotBtn.setBackground(null);
startAutopilotBgAnimatorDrawable(true);
}else {// 置灰色可点击状态下 UI
mStartAutopilotBtn.setBackground(
mContext.getResources().getDrawable(R.drawable.taxi_p_start_autopilot_txt_btn_bg));
mStartAutopilotBtn.setTextColor(
mContext.getResources().getColor(R.color.taxi_p_start_autopilot_txt_un_color));
startAutopilotBgAnimatorDrawable(false);
}
}
public void startAutopilotBgAnimatorDrawable(boolean isStart){
if (isStart){
svBtnBgFrame.setRepeatTimes(-1);
svBtnBgFrame.setFrameFinishCallback(new FrameFinishCallback() {
@Override
public void onFinishCallback() {
// svBtnBgFrame.setBackgroundResource(R.drawable.image_00000);
}
});
svBtnBgFrame.start();
}else {
svBtnBgFrame.reset();
svBtnBgFrame.setBackground(null);
}
// if (isStart){
// if (mAnimationBtnDrawable == null) {
// mAnimationBtnDrawable = (AnimationDrawable) mStartAutopilotBtn.getBackground();
// }
// if (mAnimationBtnDrawable.isRunning()) {
// return;
// }
// mAnimationBtnDrawable.selectDrawable(0);
// mAnimationBtnDrawable.start();
// }else {
// if (mAnimationBtnDrawable != null) {
// mAnimationBtnDrawable.stop();
// }
// mAnimationBtnDrawable = null;
// }
}
private void startingCarBgAnimatorDrawable(boolean isStart){
if (isStart){
svCarStartingFrame.setRepeatTimes(-1);
svCarStartingFrame.setFrameFinishCallback(new FrameFinishCallback() {
@Override
public void onFinishCallback() {
}
});
svCarStartingFrame.setBackground(null);
svCarStartingFrame.start();
}else {
svCarStartingFrame.reset();
svCarStartingFrame.setBackgroundResource(R.drawable.light_00000);
}
// if (isStart){
// if (mAnimationStartingDrawable == null) {
// mAnimationStartingDrawable = (AnimationDrawable) mAutopilotStartingImage.getBackground();
// }
// if (mAnimationStartingDrawable.isRunning()) {
// return;
// }
// mAnimationStartingDrawable.selectDrawable(0);
// mAnimationStartingDrawable.start();
// }else {
// if (mAnimationStartingDrawable != null) {
// mAnimationStartingDrawable.selectDrawable(0);
// mAnimationStartingDrawable.stop();
// }
// mAnimationStartingDrawable = null;
// }
}
public void startOrStopLoadingAnim(boolean start) {
startingCarBgAnimatorDrawable(start);
if (start) {
isStarting = true;
mStartAutopilotBtn.setText(getResources().getString(R.string.taxi_p_start_autopilot_loading));
mStartAutopilotBtn.setTextColor(getResources().getColor(R.color.taxi_p_start_autopilot_txt_color));
startingAutopilotCountDown();
} else {
isStarting = false;
updateStartAutopilotBtnStatus(true);
}
}
public void onAutopilotStatusSuccess(){
startOrStopLoadingAnim(false);
}
public void onAutopilotStatusFailure(){
startOrStopLoadingAnim(false);
}
private void startingAutopilotCountDown() {
UiThreadHandler.postDelayed(new Runnable() {
@Override
public void run() { //未启动成功10s后做处理
if (isStarting){ //判断动画是否在进行
ToastUtils.showLong(R.string.taxi_p_start_autopilot_fail_10s_tip);
startOrStopLoadingAnim(false);
}
}
},TIMER_START_AUTOPILOT_INTERVAL);
}
}

View File

@@ -49,10 +49,11 @@ public class TaxiPassengerTrafficLightView extends IViewTrafficLight {
* 展示红绿灯预警
*
* @param checkLightId 0-都是默认1-红2-黄3-绿
* @param lightSource 1:云端下发2:自车感知
*/
@Override
public void showWarningTrafficLight(int checkLightId) {
super.showWarningTrafficLight(checkLightId);
public void showWarningTrafficLight(int checkLightId,int lightSource) {
super.showWarningTrafficLight(checkLightId,lightSource);
mCurrentLightId = checkLightId;
updateTrafficLightIcon(checkLightId);
}

View File

@@ -0,0 +1,77 @@
package com.mogo.och.taxi.passenger.utils;
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.call.analytics.AnalyticsManager;
import com.mogo.eagle.core.utilcode.util.DateTimeUtils;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.taxi.passenger.constant.TaxiPassengerConst;
import java.util.HashMap;
/**
* OCH Taxi埋点工具
*
* Created on 2022/3/24
*/
public class TaxiPassengerAnalyticsManager {
private static final class SingletonHolder {
private static final TaxiPassengerAnalyticsManager INSTANCE = new TaxiPassengerAnalyticsManager();
}
public static TaxiPassengerAnalyticsManager getInstance() {
return TaxiPassengerAnalyticsManager.SingletonHolder.INSTANCE;
}
private String mStartAutopilotKey;
private HashMap<String, Object> mStartAutopilotParams = new HashMap<>();
private Runnable startAutopilotRunnable = () -> {
// 15s内未开启上报失败埋点
mStartAutopilotParams.put(TaxiPassengerConst.EVENT_PARAM_START_RESULT, false);
AnalyticsManager.INSTANCE.track(mStartAutopilotKey, mStartAutopilotParams);
};
/**
* 触发'开启自动驾驶'埋点流程
* 开启自动驾驶15s内成功则发送成功埋点否则发送失败埋点
* @param restart false点击'开始服务'启动)/true接管后点击'自动驾驶'按钮启动)
* @param send 是否直接发送埋点15s内开启成功则直接发送成功埋点
*/
public void triggerStartAutopilotEvent(
boolean restart, boolean send, String startName, String endName, String orderNo) {
mStartAutopilotKey = restart ?
TaxiPassengerConst.EVENT_KEY_RESTART_AUTOPILOT : TaxiPassengerConst.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(TaxiPassengerConst.EVENT_PARAM_SN, sn);
mStartAutopilotParams.put(TaxiPassengerConst.EVENT_PARAM_PLATE_NUM, TextUtils.isEmpty(plateNum) ? "" : plateNum);
mStartAutopilotParams.put(TaxiPassengerConst.EVENT_PARAM_ENV_ONLINE,
DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE ? true : false);
mStartAutopilotParams.put(TaxiPassengerConst.EVENT_PARAM_TIME, dateTime);
mStartAutopilotParams.put(TaxiPassengerConst.EVENT_PARAM_START_NAME, startName);
mStartAutopilotParams.put(TaxiPassengerConst.EVENT_PARAM_END_NAME, endName);
mStartAutopilotParams.put(TaxiPassengerConst.EVENT_PARAM_ORDER_NUMBER, orderNo);
if (send) {
// 开启成功,上报埋点
if (startAutopilotRunnable != null &&
UiThreadHandler.getsUiHandler().hasCallbacks(startAutopilotRunnable)) {
UiThreadHandler.removeCallbacks(startAutopilotRunnable);
}
mStartAutopilotParams.put(TaxiPassengerConst.EVENT_PARAM_START_RESULT, true);
AnalyticsManager.INSTANCE.track(mStartAutopilotKey, mStartAutopilotParams);
} else {
UiThreadHandler.postDelayed(startAutopilotRunnable, TaxiPassengerConst.LOOP_PERIOD_15S);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

View File

@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/image_00000"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00001"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00002"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00003"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00004"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00005"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00006"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00007"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00008"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00009"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00010"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00011"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00012"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00013"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00014"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00015"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00016"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00017"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00018"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00019"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00020"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00021"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00022"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00023"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00024"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00025"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00026"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00027"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00028"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00029"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00030"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00031"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00032"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00033"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00034"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00035"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00036"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00037"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00038"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00039"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00040"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00041"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00042"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00043"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00044"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00045"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00046"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00047"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00048"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00049"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00050"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00051"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00052"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00053"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00054"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00055"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00056"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00057"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00058"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00059"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00060"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00061"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00062"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00063"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00064"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00065"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00066"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00067"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00068"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00069"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00070"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00071"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00072"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00073"
android:duration="100"></item>
<item
android:drawable="@drawable/image_00074"
android:duration="100"></item>
</animation-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

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