From a235631106525d89c709cab4f169a60a2ba317f8 Mon Sep 17 00:00:00 2001 From: wangmingjun Date: Thu, 23 Jun 2022 20:10:17 +0800 Subject: [PATCH] [Taxi-d 280, Taxi-p 130] opt --- .../module/wigets/sfv/BaseSurfaceView.java | 174 ++++++++ .../wigets/sfv/FrameFinishCallback.java | 5 + .../module/wigets/sfv/FrameSurfaceView.java | 404 ++++++++++++++++++ .../module/wigets/sfv/LinkedBitmap.java | 12 + .../wigets/sfv/LinkedBlockingQueue.java | 205 +++++++++ .../ui/TaxiPassengerBaseFragment.java | 28 +- .../ui/TaxiPassengerStartAutopilotView.java | 229 ++++++++-- .../layout/taxi_p_start_autopilot_view.xml | 33 +- .../src/main/res/values/strings.xml | 2 + 9 files changed, 1023 insertions(+), 69 deletions(-) create mode 100644 OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/BaseSurfaceView.java create mode 100644 OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/FrameFinishCallback.java create mode 100644 OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/FrameSurfaceView.java create mode 100644 OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/LinkedBitmap.java create mode 100644 OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/LinkedBlockingQueue.java diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/BaseSurfaceView.java b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/BaseSurfaceView.java new file mode 100644 index 0000000000..57186d98e9 --- /dev/null +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/BaseSurfaceView.java @@ -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(); + } + } +} \ No newline at end of file diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/FrameFinishCallback.java b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/FrameFinishCallback.java new file mode 100644 index 0000000000..09cd4dea1f --- /dev/null +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/FrameFinishCallback.java @@ -0,0 +1,5 @@ +package com.mogo.och.common.module.wigets.sfv; + +public interface FrameFinishCallback { + void onFinishCallback(); +} diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/FrameSurfaceView.java b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/FrameSurfaceView.java new file mode 100644 index 0000000000..1503e13f07 --- /dev/null +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/FrameSurfaceView.java @@ -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 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 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 bitmapIds; + private BitmapFactory.Options options; + + public DecodeRunnable(int index, List 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; + } + } + } +} diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/LinkedBitmap.java b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/LinkedBitmap.java new file mode 100644 index 0000000000..d3752c3bb0 --- /dev/null +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/LinkedBitmap.java @@ -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; + +} \ No newline at end of file diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/LinkedBlockingQueue.java b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/LinkedBlockingQueue.java new file mode 100644 index 0000000000..6b95bd5551 --- /dev/null +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/sfv/LinkedBlockingQueue.java @@ -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(); + } +} \ No newline at end of file diff --git a/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/TaxiPassengerBaseFragment.java b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/TaxiPassengerBaseFragment.java index 76d1716330..b435fc7590 100644 --- a/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/TaxiPassengerBaseFragment.java +++ b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/TaxiPassengerBaseFragment.java @@ -279,20 +279,20 @@ public class TaxiPassengerBaseFragment extends MvpFragment(new TaxiPassengerStartAutopilotView(getContext())); -// mStartAutopilotView.get().setOnClickStartAutopilotBtnCallback(this); -// } -// OverlayViewUtils.showOverlayView(getActivity(),mStartAutopilotView.get()); -// updateStartAutopilotBtnStatus(isClickable); -// }else { -// if (mStartAutopilotView == null || mStartAutopilotView.get() == null){ -// return; -// } -// mStartAutopilotView.get().setOnClickStartAutopilotBtnCallback(null); -// OverlayViewUtils.dismissOverlayView(mStartAutopilotView.get()); -// } + if (isShow){ + if (mStartAutopilotView == null || mStartAutopilotView.get() == null){ + mStartAutopilotView = new WeakReference<>(new TaxiPassengerStartAutopilotView(getContext())); + mStartAutopilotView.get().setOnClickStartAutopilotBtnCallback(this); + } + OverlayViewUtils.showOverlayView(getActivity(),mStartAutopilotView.get()); + updateStartAutopilotBtnStatus(isClickable); + }else { + if (mStartAutopilotView == null || mStartAutopilotView.get() == null){ + return; + } + mStartAutopilotView.get().setOnClickStartAutopilotBtnCallback(null); + OverlayViewUtils.dismissOverlayView(mStartAutopilotView.get()); + } } public void updateStartAutopilotBtnStatus(boolean isClickable){ diff --git a/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/TaxiPassengerStartAutopilotView.java b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/TaxiPassengerStartAutopilotView.java index aa4a0cd938..ea909c8976 100644 --- a/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/TaxiPassengerStartAutopilotView.java +++ b/OCH/mogo-och-taxi-passenger/src/main/java/com/mogo/och/taxi/passenger/ui/TaxiPassengerStartAutopilotView.java @@ -4,15 +4,18 @@ import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.view.LayoutInflater; import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; -import com.mogo.eagle.core.utilcode.util.UiThreadHandler; +import com.elegant.utils.UiThreadHandler; +import com.mogo.eagle.core.utilcode.util.ToastUtils; +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 java.util.Arrays; + /** * @author: wangmingjun * @date: 2022/6/14 @@ -20,7 +23,7 @@ import com.mogo.och.taxi.passenger.callback.ITPClickStartAutopilotCallback; public class TaxiPassengerStartAutopilotView extends RelativeLayout implements View.OnClickListener { private TextView mStartAutopilotBtn; - private ImageView mAutopilotStartingImage; +// private ImageView mAutopilotStartingImage; private ITPClickStartAutopilotCallback mClickCallback; public boolean isStarting = false; private AnimationDrawable mAnimationBtnDrawable; @@ -28,6 +31,101 @@ public class TaxiPassengerStartAutopilotView extends RelativeLayout implements V 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); @@ -38,8 +136,23 @@ public class TaxiPassengerStartAutopilotView extends RelativeLayout implements V private void initView(Context context) { view = LayoutInflater.from(context).inflate(R.layout.taxi_p_start_autopilot_view, this,true); mStartAutopilotBtn = view.findViewById(R.id.taxi_p_start_autopilot); - mAutopilotStartingImage = view.findViewById(R.id.taxi_p_autopilot_starting); 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){ @@ -50,6 +163,10 @@ public class TaxiPassengerStartAutopilotView extends RelativeLayout implements V public void onClick(View v) { if (v.getId() == R.id.taxi_p_start_autopilot){ //开启动画和自动驾驶 + 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(); @@ -58,22 +175,20 @@ public class TaxiPassengerStartAutopilotView extends RelativeLayout implements V } public void updateStartAutopilotBtnStatus(boolean isClickable){ + + svCarStartingFrame.setBackgroundResource(R.drawable.light_00000); + if (mStartAutopilotBtn == null) return; - mStartAutopilotBtn.setClickable(isClickable); + + mStartAutopilotBtn.setTag(isClickable); mStartAutopilotBtn.setText( mContext.getResources().getString(R.string.taxi_p_start_autopilot_txt)); - if (isClickable){ //可点击状态下UI - LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mStartAutopilotBtn.getLayoutParams(); - params.bottomMargin = 0; - mStartAutopilotBtn.setLayoutParams(params); + + if (isClickable){ //高亮可点击状态下UI mStartAutopilotBtn.setTextColor( mContext.getResources().getColor(R.color.taxi_p_start_autopilot_txt_color)); - mStartAutopilotBtn.setBackground(mContext.getResources().getDrawable(R.drawable.anmi_flow)); startAutopilotBgAnimatorDrawable(true); - }else {// 不可点击状态下 UI - LayoutParams params = (LayoutParams) mStartAutopilotBtn.getLayoutParams(); - params.bottomMargin = 294; - mStartAutopilotBtn.setLayoutParams(params); + }else {// 置灰色可点击状态下 UI mStartAutopilotBtn.setBackground( mContext.getResources().getDrawable(R.drawable.taxi_p_start_autopilot_txt_btn_bg)); mStartAutopilotBtn.setTextColor( @@ -84,48 +199,75 @@ public class TaxiPassengerStartAutopilotView extends RelativeLayout implements V public void startAutopilotBgAnimatorDrawable(boolean isStart){ if (isStart){ - if (mAnimationBtnDrawable == null) { - mAnimationBtnDrawable = (AnimationDrawable) mStartAutopilotBtn.getBackground(); - } - if (mAnimationBtnDrawable.isRunning()) { - return; - } - mAnimationBtnDrawable.selectDrawable(0); - mAnimationBtnDrawable.start(); + svBtnBgFrame.setRepeatTimes(-1); + svBtnBgFrame.setFrameFinishCallback(new FrameFinishCallback() { + @Override + public void onFinishCallback() { +// svBtnBgFrame.setBackgroundResource(R.drawable.image_00000); + } + }); + svBtnBgFrame.start(); }else { - if (mAnimationBtnDrawable != null) { - mAnimationBtnDrawable.stop(); - } - mAnimationBtnDrawable = null; + 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 startingAutopilotAnimatorDrawable(boolean isStart){ + private void startingCarBgAnimatorDrawable(boolean isStart){ if (isStart){ - if (mAnimationStartingDrawable == null) { - mAnimationStartingDrawable = (AnimationDrawable) mAutopilotStartingImage.getBackground(); - } - if (mAnimationStartingDrawable.isRunning()) { - return; - } - mAnimationStartingDrawable.selectDrawable(0); - mAnimationStartingDrawable.start(); + svCarStartingFrame.setRepeatTimes(-1); + svCarStartingFrame.setFrameFinishCallback(new FrameFinishCallback() { + @Override + public void onFinishCallback() { + } + }); + svCarStartingFrame.setBackground(null); + svCarStartingFrame.start(); }else { - if (mAnimationStartingDrawable != null) { - mAnimationStartingDrawable.selectDrawable(0); - mAnimationStartingDrawable.stop(); - } - mAnimationStartingDrawable = null; + 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) { - startingAutopilotAnimatorDrawable(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_un_color)); + mStartAutopilotBtn.setTextColor(getResources().getColor(R.color.taxi_p_start_autopilot_txt_color)); startingAutopilotCountDown(); } else { @@ -147,6 +289,7 @@ public class TaxiPassengerStartAutopilotView extends RelativeLayout implements V @Override public void run() { //未启动成功10s后做处理 if (isStarting){ //判断动画是否在进行 + ToastUtils.showLong(R.string.taxi_p_start_autopilot_fail_10s_tip); startOrStopLoadingAnim(false); } diff --git a/OCH/mogo-och-taxi-passenger/src/main/res/layout/taxi_p_start_autopilot_view.xml b/OCH/mogo-och-taxi-passenger/src/main/res/layout/taxi_p_start_autopilot_view.xml index 10fe0a34e8..56c052eb7b 100644 --- a/OCH/mogo-och-taxi-passenger/src/main/res/layout/taxi_p_start_autopilot_view.xml +++ b/OCH/mogo-och-taxi-passenger/src/main/res/layout/taxi_p_start_autopilot_view.xml @@ -6,15 +6,24 @@ android:layout_height="match_parent" tools:ignore="MissingDefaultResource" android:background="@drawable/taxi_p_passenger_start_panel_bg"> - + + + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintBottom_toBottomOf="parent"/> + app:layout_constraintLeft_toLeftOf="@+id/taxi_p_start_autopilot_btn_sfv" + app:layout_constraintRight_toRightOf="@+id/taxi_p_start_autopilot_btn_sfv" + app:layout_constraintTop_toTopOf="@+id/taxi_p_start_autopilot_btn_sfv" + app:layout_constraintBottom_toBottomOf="@+id/taxi_p_start_autopilot_btn_sfv"/> \ No newline at end of file diff --git a/OCH/mogo-och-taxi-passenger/src/main/res/values/strings.xml b/OCH/mogo-och-taxi-passenger/src/main/res/values/strings.xml index e3ed2949af..bc93dc9da1 100644 --- a/OCH/mogo-och-taxi-passenger/src/main/res/values/strings.xml +++ b/OCH/mogo-och-taxi-passenger/src/main/res/values/strings.xml @@ -34,4 +34,6 @@ 点击开始 启动中... + 自动驾驶启动失败,请与司机确认车辆状态 + 车辆尚未完成准备,不能启动自动驾驶 \ No newline at end of file