下沉可共用的自定义控件

Signed-off-by: 董宏宇 <martindhy@gmail.com>
This commit is contained in:
董宏宇
2021-09-22 11:33:06 +08:00
parent 1757b848ef
commit ef31c9ee89
38 changed files with 161 additions and 211 deletions

View File

@@ -54,14 +54,6 @@ public class V2XConst {
public static final String V2X_ACC_OFF_TIME_STR = "v2x_acc_off_time_str";
public static final String V2X_STRATEGY_PUSH = "v2x_strategy_push";
public static final int STATE_ERROR = -1;
public static final int STATE_IDLE = 0;
public static final int STATE_PREPARING = 1;
public static final int STATE_PREPARED = 2;
public static final int STATE_PLAYING = 3;
public static final int STATE_PAUSED = 4;
public static final int STATE_PLAYBACK_COMPLETED = 5;
public static final int STATE_SEEKING = 6;
/**
* V2X 埋点

View File

@@ -14,7 +14,7 @@ import com.mogo.module.v2x.R;
import com.mogo.module.v2x.V2XServiceManager;
import com.mogo.module.v2x.listener.V2XWindowStatusListener;
import com.mogo.module.v2x.scenario.view.IV2XWindow;
import com.mogo.module.v2x.view.TextureVideoView;
import com.mogo.eagle.core.widget.TextureVideoView;
import com.mogo.utils.logger.Logger;
import static com.mogo.module.v2x.V2XConst.MODULE_NAME;

View File

@@ -1,366 +0,0 @@
package com.mogo.module.v2x.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import androidx.annotation.Nullable;
import com.mogo.module.v2x.utils.ImageUtil;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* 加载图片帧动画
*/
public class AnimationView extends View implements Handler.Callback {
public static final int DEFAULT_ANIM_TIME = 20;
public static final int PROCESS_DATA = 1;
public static final int PROCESS_ANIM_FINISH = 1 << 1;
public static final int PROCESS_DELAY = 1 << 2;
public AnimData mCurAnimData;
public int mCurAnimPos;
public boolean mIsRepeat;
public int mAnimTime;
private Handler mHandler;
private ProcessAnimThread mProcessThread;
private Bitmap mCurShowBmp;
private List<AnimData> mAnimDataList = new ArrayList<>();
public AnimationView(Context context) {
this(context, null);
}
public AnimationView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AnimationView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mHandler = new Handler(this);
mProcessThread = new ProcessAnimThread(getContext(), mHandler);
mAnimTime = DEFAULT_ANIM_TIME;
}
public void setIsRepeat(boolean repeat) {
mIsRepeat = repeat;
}
private int mGravity;
public void setGravity(int gravity) {
mGravity = gravity;
invalidate();
}
public void setData(List<AnimData> list) {
if (list != null) {
mAnimDataList.clear();
mAnimDataList.addAll(list);
}
}
private Matrix mTempMatrix = new Matrix();
@Override
protected void onDraw(Canvas canvas) {
if (mCurShowBmp != null && !mCurShowBmp.isRecycled()) {
int x = 0;
int y = 0;
float scaleX = 1f;
float scaleY = 1f;
switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
x = 0;
break;
case Gravity.RIGHT:
x = this.getWidth() - mCurShowBmp.getWidth();
break;
case Gravity.CENTER_HORIZONTAL:
x = (this.getWidth() - mCurShowBmp.getWidth()) / 2;
break;
case Gravity.FILL_HORIZONTAL: {
int w = mCurShowBmp.getWidth();
if (w > 0) {
scaleX = (float) this.getWidth() / (float) w;
}
break;
}
default:
break;
}
switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
y = 0;
break;
case Gravity.BOTTOM:
y = this.getHeight() - mCurShowBmp.getHeight();
break;
case Gravity.CENTER_VERTICAL:
y = (this.getHeight() - mCurShowBmp.getHeight()) / 2;
break;
case Gravity.FILL_VERTICAL: {
int h = mCurShowBmp.getHeight();
if (h > 0) {
scaleY = (float) this.getHeight() / (float) h;
}
break;
}
default:
break;
}
if (scaleX == 1 && scaleY != 1) {
scaleX = scaleY;
switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.RIGHT:
x = this.getWidth() - (int) (mCurShowBmp.getWidth() * scaleX);
break;
case Gravity.CENTER_HORIZONTAL:
x = (this.getWidth() - (int) (mCurShowBmp.getWidth() * scaleX)) / 2;
break;
}
} else if (scaleX != 1 && scaleY == 1) {
scaleY = scaleX;
switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.BOTTOM:
y = this.getHeight() - (int) (mCurShowBmp.getHeight() * scaleY);
break;
case Gravity.CENTER_VERTICAL:
y = (this.getHeight() - (int) (mCurShowBmp.getHeight() * scaleY)) / 2;
break;
}
}
mTempMatrix.reset();
mTempMatrix.postScale(scaleX, scaleY);
mTempMatrix.postTranslate(x, y);
canvas.drawBitmap(mCurShowBmp, mTempMatrix, null);
}
}
private boolean mHasStarted = false;
public void start() {
mHasStarted = true;
if (mWidth == 0 || mHeight == 0) {
return;
}
startPlay();
}
private void startPlay() {
if (mAnimDataList != null && mAnimDataList.size() > 0) {
mCurAnimPos = 0;
AnimData animData = mAnimDataList.get(mCurAnimPos);
mCurShowBmp = ImageUtil.getBitmap(getContext(), animData.filePath, mWidth, mHeight);
invalidate();
if (mListener != null) {
mListener.onAnimChange(mCurAnimPos, mCurShowBmp);
}
checkIsPlayNext();
}
}
private void playNext(final int curAnimPosition) {
Message msg = Message.obtain();
msg.what = PROCESS_DELAY;
msg.arg1 = curAnimPosition;
mHandler.sendMessageDelayed(msg, mAnimTime);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
quit();
}
private void quit() {
mHasStarted = false;
if (mProcessThread != null) {
mProcessThread.clearAll();
}
}
private int mWidth;
private int mHeight;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
if (mProcessThread != null) {
mProcessThread.setSize(w, h);
}
if (mHasStarted) {
startPlay();
}
}
private boolean mHavePause = false;
public void pause() {
mHavePause = true;
mHandler.removeMessages(PROCESS_DELAY);
}
public void resume() {
if (mHavePause && mHasStarted) {
checkIsPlayNext();
}
}
@Override
public boolean handleMessage(Message msg) {
//此 handleMessage 在主线程被调用
switch (msg.what) {
case PROCESS_ANIM_FINISH: {
Bitmap bitmap = (Bitmap) msg.obj;
if (bitmap != null) {
if (mCurShowBmp != null) {
mCurShowBmp.recycle();
mCurShowBmp = null;
}
mCurShowBmp = bitmap;
if (mListener != null) {
mListener.onAnimChange(mCurAnimPos, bitmap);
}
invalidate();
}
checkIsPlayNext();
break;
}
case PROCESS_DELAY: {
int curAnimPosition = msg.arg1;
AnimData data = mAnimDataList.get(curAnimPosition);
mProcessThread.processData(data);
break;
}
}
return true;
}
private void checkIsPlayNext() {
mCurAnimPos++;
if (mCurAnimPos >= mAnimDataList.size()) {
if (mIsRepeat) {
mCurAnimPos = 0;
playNext(mCurAnimPos);
} else {
if (mListener != null) {
mListener.onAnimEnd();
}
}
} else {
playNext(mCurAnimPos);
}
}
private AnimCallBack mListener;
public void setAnimCallBack(AnimCallBack callBack) {
mListener = callBack;
}
public interface AnimCallBack {
void onAnimChange(int position, Bitmap bitmap);
void onAnimEnd();
}
public static class AnimData {
public Object filePath;
}
public static class ProcessAnimThread {
private HandlerThread mHandlerThread;
private Handler mProcessHandler;
private Handler mUiHandler;
private AnimData mCurAnimData;
private int mWidth;
private int mHeight;
private WeakReference<Context> mContext;
public ProcessAnimThread(Context context, Handler handler) {
mUiHandler = handler;
mContext = new WeakReference<Context>(context);
init();
}
public void setSize(int width, int height) {
mWidth = width;
mHeight = height;
}
private void init() {
mHandlerThread = new HandlerThread("process_anim_thread");
mHandlerThread.start();
mProcessHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// 消息是在子线程 HandlerThread 里面被处理,所以这里的 handleMessage 在
//子线程里被调用
switch (msg.what) {
case PROCESS_DATA: {
AnimData animData = (AnimData) msg.obj;
Bitmap bitmap = ImageUtil.getBitmap(mContext.get(), animData.filePath, mWidth, mHeight);
if (bitmap != null) {
Message finishMsg = Message.obtain();
finishMsg.what = PROCESS_ANIM_FINISH;
finishMsg.obj = bitmap;
//消息处理完毕,使用主线程的 Handler 将消息发送到主线程
mUiHandler.sendMessage(finishMsg);
}
break;
}
}
return true;
}
});
}
public void processData(AnimData animData) {
if (animData != null) {
Message msg = Message.obtain();
msg.what = PROCESS_DATA;
msg.obj = animData;
mProcessHandler.sendMessage(msg);
}
}
public void clearAll() {
mHandlerThread.quit();
mHandlerThread = null;
}
}
}

View File

@@ -17,6 +17,7 @@ import com.mogo.cloud.trafficlive.api.ITrafficCarLiveCallBack;
import com.mogo.cloud.trafficlive.api.MoGoAiCloudTrafficLive;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.voice.AIAssist;
import com.mogo.eagle.core.widget.RoundLayout;
import com.mogo.map.location.MogoLocation;
import com.mogo.module.v2x.R;
import com.mogo.module.v2x.V2XServiceManager;

View File

@@ -1,131 +0,0 @@
package com.mogo.module.v2x.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.customview.widget.ViewDragHelper;
import com.mogo.module.v2x.V2XConst;
import com.mogo.utils.logger.Logger;
import org.jetbrains.annotations.NotNull;
/**
* author : donghongyu
* e-mail : 1358506549@qq.com
* date : 2020/5/25 11:13 AM
* desc :
* version: 1.0
*/
public class GestureTopCloseLayout extends ConstraintLayout {
private ViewDragHelper mViewDragHelper;
private ViewCloseListener mViewCloseListener;
public GestureTopCloseLayout(Context context) {
this(context, null);
}
public GestureTopCloseLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GestureTopCloseLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
private int mTop;
@Override
public boolean tryCaptureView(@NotNull View child, int pointerId) {
return true;
}
@Override
public int clampViewPositionVertical(@NotNull View child, int top, int dy) {
final int bottomBound = getPaddingTop();
return Math.min(top, bottomBound);
}
@Override
public int clampViewPositionHorizontal(@NotNull View child, int left, int dx) {
//取得左边界的坐标
final int leftBound = getPaddingLeft();
//取得右边界的坐标
final int rightBound = getWidth() - child.getWidth() - child.getPaddingRight() - leftBound;
//这个地方的含义就是 如果left的值 在leftbound和rightBound之间 那么就返回left
//如果left的值 比 leftbound 还要小 那么就说明 超过了左边界 那我们只能返回给他左边界的值
//如果right的值 比 rightbound 还要大 那么就说明 超过了右边界,那我们只能返回给他右边界的值
return Math.min(Math.max(left, leftBound), rightBound);
}
@Override //不重写该方法默认返回0返回0时若还设置了点击事件则水平方向不能移动
public int getViewHorizontalDragRange(@NotNull View child) {
return getMeasuredWidth() - child.getMeasuredWidth();
}
@Override //不重写该方法默认返回0返回0时若还设置了点击事件则竖直方向不能移动
public int getViewVerticalDragRange(@NotNull View child) {
return getMeasuredHeight() - child.getMeasuredHeight();
}
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
Logger.w(V2XConst.MODULE_NAME, "onViewPositionChanged==top=" + top +
" changedView.getHeight()==" + changedView.getHeight());
if (top < 0) {
mTop = top;
if (top <= -changedView.getHeight()) {
mViewDragHelper.smoothSlideViewTo(changedView, 0, getPaddingTop());
invalidate();
if (mViewCloseListener != null) {
mViewCloseListener.onClose();
}
}
}
}
//手指释放的时候回调
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
Logger.w(V2XConst.MODULE_NAME, "onViewReleased==mTop=" + mTop + " getHeight=" + getHeight());
if (mTop < 0) {
mViewDragHelper.settleCapturedViewAt(0, -getHeight());
invalidate();
}
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mViewDragHelper.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
invalidate();
}
}
public void setViewCloseListener(ViewCloseListener mViewCloseListener) {
this.mViewCloseListener = mViewCloseListener;
}
public interface ViewCloseListener {
void onClose();
}
}

View File

@@ -1,78 +0,0 @@
package com.mogo.module.v2x.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.mogo.module.v2x.R;
/**
* author : donghongyu
* e-mail : 1358506549@qq.com
* date : 2020/5/27 2:08 PM
* desc :
* version: 1.0
*/
public class RoundConstraintLayout extends ConstraintLayout {
private float roundLayoutRadius = 14f;
private Path roundPath;
private RectF rectF;
public RoundConstraintLayout(Context context) {
this(context, null);
}
public RoundConstraintLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundLayout);
roundLayoutRadius = typedArray.getDimensionPixelSize(R.styleable.RoundLayout_roundLayoutRadius, (int) roundLayoutRadius);
typedArray.recycle();
init();
}
public RoundConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init() {
setWillNotDraw(false);//如果你继承的是ViewGroup,注意此行,否则draw方法是不会回调的;
roundPath = new Path();
rectF = new RectF();
}
private void setRoundPath() {
//添加一个圆角矩形到path中, 如果要实现任意形状的View, 只需要手动添加path就行
roundPath.addRoundRect(rectF, roundLayoutRadius, roundLayoutRadius, Path.Direction.CW);
}
public void setRoundLayoutRadius(float roundLayoutRadius) {
this.roundLayoutRadius = roundLayoutRadius;
setRoundPath();
postInvalidate();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
rectF.set(0f, 0f, getMeasuredWidth(), getMeasuredHeight());
setRoundPath();
}
@Override
public void draw(Canvas canvas) {
if (roundLayoutRadius > 0f) {
canvas.clipPath(roundPath);
}
super.draw(canvas);
}
}

View File

@@ -1,79 +0,0 @@
package com.mogo.module.v2x.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
import com.mogo.module.v2x.R;
/**
* author : donghongyu
* e-mail : 1358506549@qq.com
* date : 2020/3/25 11:39 AM
* desc :
* version: 1.0
*/
public class RoundLayout extends RelativeLayout {
private float roundLayoutRadius = 14f;
private Path roundPath;
private RectF rectF;
public RoundLayout(Context context) {
this(context, null);
}
public RoundLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundLayout);
roundLayoutRadius = typedArray.getDimensionPixelSize(R.styleable.RoundLayout_roundLayoutRadius, (int) roundLayoutRadius);
typedArray.recycle();
init();
}
private void init() {
setWillNotDraw(false);//如果你继承的是ViewGroup,注意此行,否则draw方法是不会回调的;
roundPath = new Path();
rectF = new RectF();
}
private void setRoundPath() {
//添加一个圆角矩形到path中, 如果要实现任意形状的View, 只需要手动添加path就行
roundPath.addRoundRect(rectF, roundLayoutRadius, roundLayoutRadius, Path.Direction.CW);
}
public void setRoundLayoutRadius(float roundLayoutRadius) {
this.roundLayoutRadius = roundLayoutRadius;
setRoundPath();
postInvalidate();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
rectF.set(0f, 0f, getMeasuredWidth(), getMeasuredHeight());
setRoundPath();
}
@Override
public void draw(Canvas canvas) {
if (roundLayoutRadius > 0f) {
canvas.clipPath(roundPath);
}
super.draw(canvas);
}
}

View File

@@ -1,590 +0,0 @@
package com.mogo.module.v2x.view;
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.TextureView;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.MediaController.MediaPlayerControl;
import com.mogo.module.v2x.R;
import com.mogo.module.v2x.V2XConst;
import com.mogo.utils.logger.Logger;
import java.io.IOException;
import java.util.Map;
import static com.mogo.module.v2x.V2XConst.MODULE_NAME;
/**
* Displays a video file. The VideoView class can load images from various
* sources (such as resources or content providers), takes care of computing its
* measurement from the video so that it can be used in any layout manager, and
* provides various display options such as scaling and tinting.
*/
public class TextureVideoView extends TextureView implements MediaPlayerControl {
private String TAG = "V2XModuleProvider";
// settable by the client
private Uri mUri;
private Map<String, String> mHeaders;
private int mDuration;
// all possible internal states
private int mCurrentState = V2XConst.STATE_IDLE;
private int mTargetState = mCurrentState, mTagetStateBackup = mCurrentState;
private int mPrepareState = V2XConst.STATE_PREPARED;
// All the stuff we need for playing and showing a video
private Surface mSurface = null;
private MediaPlayer mMediaPlayer = null;
private int mVideoWidth, mVideoHeight;
private OnCompletionListener mOnCompletionListener;
private MediaPlayer.OnPreparedListener mOnPreparedListener;
private int mCurrentBufferPercentage;
private OnErrorListener mOnErrorListener;
private MediaPlayer.OnInfoListener mOnInfoListener;
private int mSeekWhenPrepared;
private boolean isFocusLoss = true;
private boolean isSuspendFromActivity = false;
private int progressWhileSuspend = 0;
public static final int MEDIA_INFO_VIDEO_NOT_SUPPORTED = 860;
public static final int MEDIA_INFO_AUDIO_NOT_SUPPORTED = 862;
public TextureVideoView(Context context) {
super(context);
initVideoView();
}
public TextureVideoView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
initVideoView();
}
public TextureVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initVideoView();
}
@SuppressLint("NewApi")
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(TextureVideoView.class.getName());
}
@SuppressLint("NewApi")
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(TextureVideoView.class.getName());
}
private void initVideoView() {
mVideoWidth = 0;
mVideoHeight = 0;
setSurfaceTextureListener(mSurfaceTextureListener);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
mCurrentState = V2XConst.STATE_IDLE;
mTargetState = V2XConst.STATE_IDLE;
}
public boolean requestAudioFocus() {
return true;
}
public void setVideoPath(String path) {
setVideoURI(Uri.parse(path));
}
public void setVideoURI(Uri uri) {
setVideoURI(uri, null);
}
public void setVideoURI(Uri uri, Map<String, String> headers) {
if (uri == null) {
Logger.i(MODULE_NAME, "setVideoURI--- uri = null");
} else {
Logger.i(MODULE_NAME, "setVideoURI--- uri = " + uri.getPath());
}
mUri = uri;
mHeaders = headers;
mSeekWhenPrepared = 0;
isFocusLoss = !requestAudioFocus();
openVideo();
requestLayout();
invalidate();
}
public void stopPlayback() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
mTargetState = mCurrentState = V2XConst.STATE_IDLE;
}
}
@SuppressLint("NewApi")
private void openVideo() {
Logger.i(MODULE_NAME, "openVideo");
if (mUri == null) {
Logger.i(MODULE_NAME, "mUri == null ");
}
if (mSurface == null) {
Logger.i(MODULE_NAME, "mSurface == null ");
}
if (mUri == null || mSurface == null || isSuspendFromActivity) {
Logger.i(MODULE_NAME, "isSuspendFromActivity = " + isSuspendFromActivity);
return;
}
release(false);
try {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mDuration = -1;
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mCurrentBufferPercentage = 0;
mMediaPlayer.setDataSource(getContext(), mUri, mHeaders);
if (mSurface != null) {
/*
Canvas mCanvas = mSurface.lockCanvas(new Rect());
mCanvas.drawColor(Color.BLACK);
mSurface.unlockCanvasAndPost(mCanvas);
invalidate();
*/
mMediaPlayer.setSurface(mSurface);
}
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.setVolume(0, 0);
mMediaPlayer.prepare();
mPrepareState = mCurrentState = V2XConst.STATE_PREPARING;
} catch (IOException | SecurityException | IllegalStateException | IllegalArgumentException ex) {
ex.printStackTrace();
onError();
}
}
private void onError() {
mTargetState = mCurrentState = V2XConst.STATE_ERROR;
if (mErrorListener != null)
mErrorListener.onError(mMediaPlayer,
MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
}
MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new MediaPlayer.OnVideoSizeChangedListener() {
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
if (mVideoWidth != 0 && mVideoHeight != 0) {
Logger.d(MODULE_NAME, "OnVideoSizeChangedListener mVideoWidth:" + " mVideoWidth:" + mVideoWidth + " mVideoHeight:" + mVideoHeight);
}
}
};
MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
Logger.i(MODULE_NAME, "MediaPlayer.OnPreparedListener");
mPrepareState = mCurrentState = V2XConst.STATE_PREPARED;
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
int seekToPosition = mSeekWhenPrepared;
// Logger.i(MODULE_NAME, "seekToPosition = " + seekToPosition);
if (seekToPosition != 0) {
seekTo(seekToPosition);
}
if (mTargetState == V2XConst.STATE_PLAYING) {
start();
}
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared(mMediaPlayer);
}
mp.setOnInfoListener((mp1, what, extra) -> {
if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START)
setBackgroundColor(Color.TRANSPARENT);
return true;
});
}
};
private OnCompletionListener mCompletionListener = new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
Logger.i(MODULE_NAME, "MediaPlayer.OnCompletionListener");
mTargetState = mCurrentState = V2XConst.STATE_PLAYBACK_COMPLETED;
if (mOnCompletionListener != null) {
mOnCompletionListener.onCompletion(mMediaPlayer);
}
}
};
private OnSeekCompleteListener mSeekCompleteListener = mp -> {
Logger.i(MODULE_NAME, "MediaPlayer.OnSeekCompleteListener");
try {
mCurrentState = mMediaPlayer.isPlaying() ? V2XConst.STATE_PLAYING : V2XConst.STATE_PAUSED;
} catch (Exception e) {
mCurrentState = V2XConst.STATE_PLAYING;
e.printStackTrace();
}
};
private OnErrorListener mErrorListener = new OnErrorListener() {
public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
Logger.i(MODULE_NAME, "MediaPlayer.onError");
mTargetState = mPrepareState = mCurrentState = V2XConst.STATE_ERROR;
if (mOnErrorListener != null) {
mOnErrorListener.onError(mMediaPlayer, framework_err,
impl_err);
}
release(false);
return true;
}
};
private MediaPlayer.OnInfoListener mInfoListener = new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
Logger.i(MODULE_NAME, "MediaPlayer.OnInfoListener---what = " + what + ";extra = " + extra);
int messageId = 0;
if (what == MEDIA_INFO_VIDEO_NOT_SUPPORTED) {
messageId = R.string.VideoView_info_text_video_not_supported;
} else if (what == MEDIA_INFO_AUDIO_NOT_SUPPORTED) {
messageId = R.string.file_not_support;
}
if (messageId != 0) {
if (mOnInfoListener != null) {
mOnInfoListener.onInfo(mp, what, extra);
}
return true;
}
return false;
}
};
private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer mp, int percent) {
Logger.i(MODULE_NAME, "MediaPlayer.OnBufferingUpdateListener");
mCurrentBufferPercentage = percent;
}
};
/**
* Register a callback to be invoked when the media file is loaded and ready
* to go.
*
* @param l The callback that will be run
*/
public void setOnPreparedListener(MediaPlayer.OnPreparedListener l) {
mOnPreparedListener = l;
}
/**
* Register a callback to be invoked when the end of a media file has been
* reached during playback.
*
* @param l The callback that will be run
*/
public void setOnCompletionListener(OnCompletionListener l) {
mOnCompletionListener = l;
}
/**
* Register a callback to be invoked when an error occurs during playback or
* setup. If no listener is specified, or if the listener returned false,
* VideoView will inform the user of any errors.
*
* @param l The callback that will be run
*/
public void setOnErrorListener(OnErrorListener l) {
mOnErrorListener = l;
}
public void setOnInfoListener(MediaPlayer.OnInfoListener l) {
mOnInfoListener = l;
}
SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Logger.i(MODULE_NAME, "onSurfaceTextureAvailable");
mSurface = new Surface(surface);
openVideo();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Logger.d(MODULE_NAME, "onSurfaceTextureSizeChanged mVideoWidth:" + " mVideoWidth:" + mVideoWidth + " mVideoHeight:" + mVideoHeight);
boolean isValidState = (mTargetState == V2XConst.STATE_PLAYING);
boolean hasValidSize = (mVideoWidth == width && mVideoHeight == height);
if (mMediaPlayer != null && isValidState && hasValidSize) {
if (mSeekWhenPrepared != 0) {
seekTo(mSeekWhenPrepared);
}
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
if (!isFocusLoss) start();
}
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Logger.i(MODULE_NAME, "onSurfaceTextureDestroyed");
mSurface = null;
release(false);
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
/*
* release the media player in any state
*/
private void release(boolean cleartargetstate) {
Logger.i(MODULE_NAME, "release ---cleartargetstate=" + cleartargetstate);
mCurrentState = V2XConst.STATE_IDLE;
if (cleartargetstate) {
mTargetState = V2XConst.STATE_IDLE;
new Thread(new Runnable() {
@Override
public void run() {
try {
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
try {
mMediaPlayer.stop();
} catch (IllegalStateException e) {
try {
this.wait(1000);
mMediaPlayer.stop();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
if (mMediaPlayer != null) {
mMediaPlayer.release();// release会做reset
}
mMediaPlayer = null;
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
}).start();
} else {
if (mMediaPlayer != null) {
try {
mMediaPlayer.stop();
mMediaPlayer.reset();
mMediaPlayer.release();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
mMediaPlayer = null;
}
}
public boolean isPrepared() {
return mPrepareState == V2XConst.STATE_PREPARED
|| isSuspendFromActivity;
}
public void start() {
Logger.i(MODULE_NAME, "TextureVideoView---start");
if (isFocusLoss) {
mTagetStateBackup = V2XConst.STATE_PLAYING;
return;
}
//Logger.i(MODULE_NAME, "mCurrentState = " + mCurrentState);
if (isInPlaybackState()) {
Logger.i(MODULE_NAME, "MediaPlayer.start");
mMediaPlayer.start();
mCurrentState = V2XConst.STATE_PLAYING;
}
mTargetState = V2XConst.STATE_PLAYING;
}
public void pause() {
Logger.i(MODULE_NAME, "TextureVideoView---pause");
Logger.i(MODULE_NAME, "mCurrentState = " + mCurrentState);
if (isInPlaybackState()) {
if (mMediaPlayer.isPlaying()) {
Logger.i(MODULE_NAME, " MediaPlayer.pause");
mMediaPlayer.pause();
mCurrentState = V2XConst.STATE_PAUSED;
}
}
mTagetStateBackup = mTargetState = V2XConst.STATE_PAUSED;
}
public void suspend() {
Logger.i(MODULE_NAME, "TextureVideoView---suspend");
if (!isSuspendFromActivity) {
isSuspendFromActivity = true;
if (mMediaPlayer != null) {
if (isInPlaybackState()) {
progressWhileSuspend = mMediaPlayer.getCurrentPosition();
}
}
release(false);
}
}
public void resume() {
Logger.i(MODULE_NAME, "TextureVideoView---resume");
isSuspendFromActivity = false;
openVideo();
seekTo(progressWhileSuspend);
if (mTagetStateBackup == V2XConst.STATE_IDLE)
mTagetStateBackup = mTargetState;
Logger.i(MODULE_NAME, "isFocusLoss = " + isFocusLoss);
Logger.i(MODULE_NAME, "isInPlaybackState() = " + isInPlaybackState());
if (isFocusLoss) {
if (isInPlaybackState()) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
mCurrentState = V2XConst.STATE_PAUSED;
}
}
}
}
// cache duration as mDuration for faster access
public int getDuration() {
if (isInPlaybackState()) {
if (mDuration > 0) {
return mDuration;
}
mDuration = mMediaPlayer.getDuration();
return mDuration;
}
mDuration = -1;
return mDuration;
}
public int getCurrentPosition() {
if (isInPlaybackState()) {
return mMediaPlayer.getCurrentPosition();
}
return 0;
}
public void seekTo(int msec) {
Logger.i(MODULE_NAME, "TextureVideoView---seekTo---msec = " + msec);
if (isInPlaybackState() && mCurrentState != V2XConst.STATE_SEEKING
&& msec > 0 && msec < getDuration()) {
mCurrentState = V2XConst.STATE_SEEKING;
Logger.i(MODULE_NAME, "MediaPlayer.seekTo(msec) = " + msec);
mMediaPlayer.seekTo(msec);
mSeekWhenPrepared = 0;
} else {
mSeekWhenPrepared = msec;
progressWhileSuspend = msec;
}
}
public boolean isPlaying() {
return isInPlaybackState() && mMediaPlayer.isPlaying();
}
public int getBufferPercentage() {
if (mMediaPlayer != null) {
return mCurrentBufferPercentage;
}
return 0;
}
private boolean isInPlaybackState() {
return (mMediaPlayer != null && mCurrentState != V2XConst.STATE_ERROR
&& mCurrentState != V2XConst.STATE_IDLE && mCurrentState != V2XConst.STATE_PREPARING);
}
@Override
public boolean canPause() {
return false;
}
@Override
public boolean canSeekBackward() {
return false;
}
@Override
public boolean canSeekForward() {
return false;
}
public String getVideoPath() {
if (mUri != null) {
return mUri.getPath();
}
return null;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
setMeasuredDimension(width, height);
}
public boolean isPause() {
return mTargetState == V2XConst.STATE_PAUSED;
}
@Override
public int getAudioSessionId() {
return 0;
}
public int getCurrentState() {
Logger.i(MODULE_NAME, "mCurrentState == " + mCurrentState);
return mCurrentState;
}
}

View File

@@ -16,6 +16,7 @@ import androidx.core.content.ContextCompat;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.voice.AIAssist;
import com.mogo.eagle.core.widget.RoundLayout;
import com.mogo.module.common.entity.MarkerCarInfo;
import com.mogo.module.v2x.R;
import com.mogo.module.v2x.V2XServiceManager;

View File

@@ -15,6 +15,7 @@ import androidx.core.content.ContextCompat;
import com.mogo.cloud.trafficlive.api.ITrafficIntersectionLiveCallBack;
import com.mogo.cloud.trafficlive.api.MoGoAiCloudTrafficLive;
import com.mogo.commons.voice.AIAssist;
import com.mogo.eagle.core.widget.RoundLayout;
import com.mogo.map.location.MogoLocation;
import com.mogo.module.common.entity.MarkerCarInfo;
import com.mogo.module.v2x.R;

View File

@@ -16,6 +16,7 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import com.mogo.commons.voice.AIAssist;
import com.mogo.eagle.core.widget.RoundLayout;
import com.mogo.module.common.entity.MarkerCarInfo;
import com.mogo.module.v2x.R;
import com.mogo.module.v2x.utils.V2XUtils;