From d0b0cc1236f22ce781ca64f72e2dcebf405059c9 Mon Sep 17 00:00:00 2001 From: tongchenfei Date: Tue, 20 Oct 2020 14:57:48 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E6=96=B0=E5=BB=BAprovider=E6=89=BF?= =?UTF-8?q?=E8=BD=BD=E6=8A=BD=E7=A6=BB=E5=85=8D=E5=94=A4=E9=86=92=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/unwake/GlobalUnwakeConst.java | 44 ++++++++++++++ .../service/unwake/GlobalUnwakeManager.java | 57 +++++++++++++++++++ .../com/mogo/module/share/ShareControl.java | 13 ----- 3 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeConst.java create mode 100644 modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeManager.java diff --git a/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeConst.java b/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeConst.java new file mode 100644 index 0000000000..7f9ea74ab2 --- /dev/null +++ b/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeConst.java @@ -0,0 +1,44 @@ +package com.mogo.module.service.unwake; + +/** + * 全局免唤醒常量类 + * + * @author tongchenfei + */ +public class GlobalUnwakeConst { + public static final String MODULE_NAME = "GlobalUnwakeManager"; + public static final String PATH_NAME = "/global/unwake"; + + // command + public static final String VOICE_CMD_GO_TO_SHARE = "com.zhidao.share"; + /** + * 这个是实时路况,不是拥堵,拥堵放在了extention模块里面处理 + */ + public static final String VOICE_CMD_PUB_ROAD_CONDITION = "com.zhidao.pathfinder.report.roadCondition"; + /** + * 故障求助 + */ + public static final String VOICE_CMD_PUB_TROUBLE_HELP = "com.zhidao.auxiliaryDriving" + + ".pubTroubleHelp"; + /** + * 关闭分享框 唤醒 + */ + public static final String VOICE_CMD_SHARE_DIALOG_CLOSE = "com.zhidao.share.close"; + /** + * 两次未回复关闭分享对话框 + */ + public static final String VOICE_CMD_NO_REPLY_SHARE_DIALOG_CLOSE = "com.zhidao.share.dialog" + + ".close"; + /** + * 免唤醒词上报拥堵,但是现在当唤醒词使用 + */ + public static final String UNWAKE_UPLOAD_ROAD_CONDITION = "command_upload_roadcondition"; + /** + * 免唤醒词上报实时路况 + */ + public static final String UNWAKE_UPLOAD_REAL_TIME_TRAFFIC = "command_upload_real_time_traffic"; + + + // 词 + public static final String[] UPLOAD_REAL_TIME_TRAFFIC = {"上报实时路况", "上报路况"}; +} diff --git a/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeManager.java b/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeManager.java new file mode 100644 index 0000000000..6a19314179 --- /dev/null +++ b/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeManager.java @@ -0,0 +1,57 @@ +package com.mogo.module.service.unwake; + +import android.content.Context; +import android.content.Intent; + +import com.alibaba.android.arouter.facade.template.IProvider; +import com.mogo.commons.voice.AIAssist; +import com.mogo.commons.voice.IMogoVoiceCmdCallBack; +import com.mogo.module.common.MogoApisHandler; +import com.mogo.service.intent.IMogoIntentListener; +import com.mogo.service.intent.IMogoIntentManager; +import com.mogo.utils.logger.Logger; + +import static com.mogo.module.service.unwake.GlobalUnwakeConst.UNWAKE_UPLOAD_REAL_TIME_TRAFFIC; +import static com.mogo.module.service.unwake.GlobalUnwakeConst.UNWAKE_UPLOAD_ROAD_CONDITION; +import static com.mogo.module.service.unwake.GlobalUnwakeConst.UPLOAD_REAL_TIME_TRAFFIC; +import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_GO_TO_SHARE; +import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_NO_REPLY_SHARE_DIALOG_CLOSE; +import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_PUB_ROAD_CONDITION; +import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_PUB_TROUBLE_HELP; +import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_SHARE_DIALOG_CLOSE; + +/** + * 全局免唤醒管理 + * 有几个Module仅仅是因为要注册全局免唤醒词,加到了BaseModule中,为了将这部分Module从BaseModule中去掉,特抽离全局免唤醒词注册监听逻辑 + * + * @author tongchenfei + */ +public class GlobalUnwakeManager implements IProvider, IMogoIntentListener, IMogoVoiceCmdCallBack { + private IMogoIntentManager intentManager; + private static final String TAG = "GlobalUnwakeManager"; + @Override + public void init(Context context) { + intentManager = MogoApisHandler.getInstance().getApis().getIntentManagerApi(); + intentManager.registerIntentListener(VOICE_CMD_GO_TO_SHARE, this); + intentManager.registerIntentListener(VOICE_CMD_PUB_TROUBLE_HELP, this); + intentManager.registerIntentListener(VOICE_CMD_PUB_ROAD_CONDITION, this); + intentManager.registerIntentListener(VOICE_CMD_SHARE_DIALOG_CLOSE, this); + intentManager.registerIntentListener(VOICE_CMD_NO_REPLY_SHARE_DIALOG_CLOSE, this); + intentManager.registerIntentListener(UNWAKE_UPLOAD_ROAD_CONDITION, this); + + // 免唤醒词注册,全局免唤醒 + AIAssist.getInstance(context).registerUnWakeupCommand(UNWAKE_UPLOAD_REAL_TIME_TRAFFIC, + UPLOAD_REAL_TIME_TRAFFIC, this); + } + + @Override + public void onIntentReceived(String intentStr, Intent intent) { + // 此处只接受处理语音相关广播 + Logger.d(TAG, "收到唤醒词指令: " + intentStr); + switch (intentStr) { + + default: + break; + } + } +} diff --git a/modules/mogo-module-share/src/main/java/com/mogo/module/share/ShareControl.java b/modules/mogo-module-share/src/main/java/com/mogo/module/share/ShareControl.java index a05c8b67e9..3a7982f76e 100644 --- a/modules/mogo-module-share/src/main/java/com/mogo/module/share/ShareControl.java +++ b/modules/mogo-module-share/src/main/java/com/mogo/module/share/ShareControl.java @@ -107,7 +107,6 @@ public class ShareControl implements IMogoShareManager, IMogoIntentListener, private BaseFloatDialog mShareDialog; private IMogoAuthorizeModuleManager authorizeModuleManager; - private IMogoIntentManager intentManager; @Override public void showShareDialog() { @@ -197,18 +196,6 @@ public class ShareControl implements IMogoShareManager, IMogoIntentListener, TipToast.tip("未授权,无法分享"); } }); - intentManager = ServiceApisManager.serviceApis.getIntentManagerApi(); - intentManager.registerIntentListener(VOICE_CMD_GO_TO_SHARE, this); - intentManager.registerIntentListener(VOICE_CMD_PUB_TROUBLE_HELP, this); - intentManager.registerIntentListener(VOICE_CMD_PUB_ROAD_CONDITION, this); - intentManager.registerIntentListener(VOICE_CMD_SHARE_DIALOG_CLOSE, this); - intentManager.registerIntentListener(VOICE_CMD_NO_REPLY_SHARE_DIALOG_CLOSE, this); - intentManager.registerIntentListener(UNWAKE_UPLOAD_ROAD_CONDITION, this); - - // 免唤醒词注册,全局免唤醒 - AIAssist.getInstance(mContext).registerUnWakeupCommand(UNWAKE_UPLOAD_REAL_TIME_TRAFFIC, - UPLOAD_REAL_TIME_TRAFFIC, ShareControl.this); - // if (!DebugConfig.isLauncher()) { ServiceApisManager.serviceApis.getStatusManagerApi().registerStatusChangedListener(TAG, StatusDescriptor.MAIN_PAGE_RESUME, new IMogoStatusChangedListener() { From a7effe362f30b1b9e9321a00668f6a021f8d9c05 Mon Sep 17 00:00:00 2001 From: liujing Date: Tue, 20 Oct 2020 12:02:02 +0800 Subject: [PATCH 2/5] =?UTF-8?q?TextureVideoView=E6=9B=BF=E6=8D=A2=E6=9C=80?= =?UTF-8?q?=E6=96=B0-=E5=A4=A7=E5=B0=8F=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scene/road/V2XRoadVideoWindow.java | 40 +- .../module/v2x/view/TextureVideoView.java | 910 +++++++++++------- 2 files changed, 604 insertions(+), 346 deletions(-) diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadVideoWindow.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadVideoWindow.java index da6d1b592b..268fa90b4b 100644 --- a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadVideoWindow.java +++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/scenario/scene/road/V2XRoadVideoWindow.java @@ -53,13 +53,21 @@ public class V2XRoadVideoWindow extends RelativeLayout implements IV2XWindow, ID private void initView(Context context) { LayoutInflater.from(context).inflate(R.layout.window_road_video, this); - mVideoView = findViewById(R.id.roadVideoView); + mVideoView = findViewById(R.id.roadVideoView);/*播放器*/ + windowPalyImageView = findViewById(R.id.window_video_play);/*播放键*/ + mThumbnailImageView = findViewById(R.id.thumbnail_image);/*第一帧图片*/ closeImage = findViewById(R.id.roadVideoClose); - mThumbnailImageView = findViewById(R.id.thumbnail_image); - windowPalyImageView = findViewById(R.id.window_video_play); closeImage.setOnClickListener(v -> { close(); }); + mVideoView.setOnClickListener(v -> { + mThumbnailImageView.setVisibility(View.GONE); + if (mVideoView.isPlaying()) { + videoPause(); + } else { + videoResume(); + } + }); } @Override @@ -93,7 +101,6 @@ public class V2XRoadVideoWindow extends RelativeLayout implements IV2XWindow, ID mVideoView.setVisibility(VISIBLE); mVideoView.setVideoPath(path); mVideoView.setOnPreparedListener(mediaPlayer -> { - Logger.w(MODULE_NAME, "全屏准备。。。。。"); mThumbnailImageView.setVisibility(View.GONE); windowPalyImageView.setVisibility(View.GONE); }); @@ -101,9 +108,28 @@ public class V2XRoadVideoWindow extends RelativeLayout implements IV2XWindow, ID } /* - * 视频播放结束 - * */ - private void videoPlayEnd(String path){ + * 视频暂停播放 + * */ + private void videoPause() { + mVideoView.pause(); + windowPalyImageView.setVisibility(View.VISIBLE); + windowPalyImageView.setOnClickListener(v -> { + videoResume(); + }); + } + + /* + * 视频暂停后继续播放 + * */ + private void videoResume() { + mThumbnailImageView.setVisibility(View.INVISIBLE); + mVideoView.resume(); + } + + /* + * 视频播放结束 + * */ + private void videoPlayEnd(String path) { Bitmap bitmap = BitmapHelper.getVideoThumbnail(path); mThumbnailImageView.setVisibility(View.VISIBLE); mThumbnailImageView.setImageBitmap(bitmap); diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java index b628bcd25f..89d30e4a5a 100644 --- a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java +++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java @@ -1,5 +1,3 @@ -package com.mogo.module.v2x.view; - /* * Copyright (C) 2006 The Android Open Source Project * @@ -16,129 +14,245 @@ package com.mogo.module.v2x.view; * limitations under the License. */ -import android.annotation.SuppressLint; +package com.mogo.module.v2x.view; + +import android.app.AlertDialog; import android.content.Context; -import android.graphics.Color; +import android.content.DialogInterface; +import android.content.res.Resources; 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.media.MediaPlayer.OnInfoListener; import android.net.Uri; +import android.opengl.GLES20; +import android.os.Build; import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.Surface; import android.view.TextureView; +import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.MediaController; 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; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; /** - * 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. + * Displays a video file. The TextureVideoView 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.

+ * + * Note: VideoView does not retain its full state when going into the + * background. In particular, it does not restore the current play state, + * play position or selected tracks. Applications should + * save and restore these on their own in + * {@link android.app.Activity#onSaveInstanceState} and + * {@link android.app.Activity#onRestoreInstanceState}.

+ * Also note that the audio session id (from {@link #getAudioSessionId}) may + * change from its previously returned value when the VideoView is restored.

+ * + * This code is based on the official Android sources for 7.1.1_r13 with the following differences: + *

    + *
  1. extends {@link TextureView} instead of a {@link android.view.SurfaceView} + * allowing proper view animations
  2. + *
  3. removes code that uses hidden APIs and thus is not available (e.g. subtitle support)
  4. + *
  5. various small fixes and improvements
  6. + *
*/ -public class TextureVideoView extends TextureView implements MediaPlayerControl { - private String TAG = "V2XModuleProvider"; +public class TextureVideoView extends TextureView + implements MediaPlayerControl { + private static final String TAG = "TextureVideoView"; + + // all possible internal states + private static final int STATE_ERROR = -1; + private static final int STATE_IDLE = 0; + private static final int STATE_PREPARING = 1; + private static final int STATE_PREPARED = 2; + private static final int STATE_PLAYING = 3; + private static final int STATE_PAUSED = 4; + private static final int STATE_PLAYBACK_COMPLETED = 5; + // settable by the client private Uri mUri; private Map 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; + // mCurrentState is a TextureVideoView object's current state. + // mTargetState is the state that a method caller intends to reach. + // For instance, regardless the TextureVideoView object's current state, + // calling pause() intends to bring the object to a target state + // of STATE_PAUSED. + private int mCurrentState = STATE_IDLE; + private int mTargetState = STATE_IDLE; // All the stuff we need for playing and showing a video private Surface mSurface = null; private MediaPlayer mMediaPlayer = null; - private int mVideoWidth, mVideoHeight; - + private int mAudioSession; + private int mVideoWidth; + private int mVideoHeight; + private MediaController mMediaController; 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; + private OnInfoListener mOnInfoListener; + private int mSeekWhenPrepared; // recording the seek position while preparing + private boolean mCanPause; + private boolean mCanSeekBack; + private boolean mCanSeekForward; + private boolean mShouldRequestAudioFocus = true; public TextureVideoView(Context context) { - super(context); - initVideoView(); + this(context, null); } public TextureVideoView(Context context, AttributeSet attrs) { - super(context, attrs, 0); - initVideoView(); + this(context, attrs, 0); } public TextureVideoView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - initVideoView(); + + mVideoWidth = 0; + mVideoHeight = 0; + + setSurfaceTextureListener(mSurfaceTextureListener); + + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + + mCurrentState = STATE_IDLE; + mTargetState = STATE_IDLE; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " + // + MeasureSpec.toString(heightMeasureSpec) + ")"); + + int width = getDefaultSize(mVideoWidth, widthMeasureSpec); + int height = getDefaultSize(mVideoHeight, heightMeasureSpec); + if (mVideoWidth > 0 && mVideoHeight > 0) { + + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) { + // the size is fixed + width = widthSpecSize; + height = heightSpecSize; + + // for compatibility, we adjust size based on aspect ratio + if ( mVideoWidth * height < width * mVideoHeight ) { + //Log.i("@@@", "image too wide, correcting"); + width = height * mVideoWidth / mVideoHeight; + } else if ( mVideoWidth * height > width * mVideoHeight ) { + //Log.i("@@@", "image too tall, correcting"); + height = width * mVideoHeight / mVideoWidth; + } + } else if (widthSpecMode == MeasureSpec.EXACTLY) { + // only the width is fixed, adjust the height to match aspect ratio if possible + width = widthSpecSize; + height = width * mVideoHeight / mVideoWidth; + if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { + // couldn't match aspect ratio within the constraints + height = heightSpecSize; + } + } else if (heightSpecMode == MeasureSpec.EXACTLY) { + // only the height is fixed, adjust the width to match aspect ratio if possible + height = heightSpecSize; + width = height * mVideoWidth / mVideoHeight; + if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { + // couldn't match aspect ratio within the constraints + width = widthSpecSize; + } + } else { + // neither the width nor the height are fixed, try to use actual video size + width = mVideoWidth; + height = mVideoHeight; + if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { + // too tall, decrease both width and height + height = heightSpecSize; + width = height * mVideoWidth / mVideoHeight; + } + if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { + // too wide, decrease both width and height + width = widthSpecSize; + height = width * mVideoHeight / mVideoWidth; + } + } + } else { + // no size yet, just adopt the given spec sizes + } + setMeasuredDimension(width, height); } - @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 int resolveAdjustedSize(int desiredSize, int measureSpec) { + return getDefaultSize(desiredSize, measureSpec); } + /** + * Sets video path. + * + * @param path the path of the video. + */ public void setVideoPath(String path) { setVideoURI(Uri.parse(path)); } + /** + * Sets video URI. + * + * @param uri the URI of the video. + */ public void setVideoURI(Uri uri) { setVideoURI(uri, null); } + /** + * Sets video URI using specific headers. + * + * @param uri the URI of the video. + * @param headers the headers for the URI request. + * Note that the cross domain redirection is allowed by default, but that can be + * changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value + * to disallow or allow cross domain redirection. + */ public void setVideoURI(Uri uri, Map 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(); @@ -149,232 +263,351 @@ public class TextureVideoView extends TextureView implements MediaPlayerControl mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; - mTargetState = mCurrentState = V2XConst.STATE_IDLE; + mCurrentState = STATE_IDLE; + mTargetState = STATE_IDLE; + if (mShouldRequestAudioFocus) { + AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); + am.abandonAudioFocus(null); + } } + clearSurface(); } - @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); + /** + * Clears the surface texture by attaching a GL context and clearing it. + * Code taken from Hugo Gresse's answer on stackoverflow.com. + */ + private void clearSurface() { + if (mSurface == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { return; } + + EGL10 egl = (EGL10) EGLContext.getEGL(); + EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + egl.eglInitialize(display, null); + + int[] attribList = { + EGL10.EGL_RED_SIZE, 8, + EGL10.EGL_GREEN_SIZE, 8, + EGL10.EGL_BLUE_SIZE, 8, + EGL10.EGL_ALPHA_SIZE, 8, + EGL10.EGL_RENDERABLE_TYPE, EGL10.EGL_WINDOW_BIT, + EGL10.EGL_NONE, 0, // placeholder for recordable [@-3] + EGL10.EGL_NONE + }; + EGLConfig[] configs = new EGLConfig[1]; + int[] numConfigs = new int[1]; + egl.eglChooseConfig(display, attribList, configs, configs.length, numConfigs); + EGLConfig config = configs[0]; + EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, new int[]{ + 12440, 2, EGL10.EGL_NONE + }); + EGLSurface eglSurface = egl.eglCreateWindowSurface(display, config, mSurface, new int[]{ + EGL10.EGL_NONE + }); + + egl.eglMakeCurrent(display, eglSurface, eglSurface, context); + GLES20.glClearColor(0, 0, 0, 1); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + egl.eglSwapBuffers(display, eglSurface); + egl.eglDestroySurface(display, eglSurface); + egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + egl.eglDestroyContext(display, context); + egl.eglTerminate(display); + } + + private void openVideo() { + if (mUri == null || mSurface == null) { + // not ready for playback just yet, will try again later + return; + } + // we shouldn't clear the target state, because somebody might have + // called start() previously release(false); + + if (mShouldRequestAudioFocus) { + AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); + am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); + } + try { mMediaPlayer = new MediaPlayer(); + + if (mAudioSession != 0) { + mMediaPlayer.setAudioSessionId(mAudioSession); + } else { + mAudioSession = mMediaPlayer.getAudioSessionId(); + } mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); - mDuration = -1; mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); - mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); - mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener); mMediaPlayer.setOnInfoListener(mInfoListener); + mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); 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.setDataSource(getContext().getApplicationContext(), mUri, mHeaders); + 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(); + mMediaPlayer.prepareAsync(); + + // we don't set the target state here either, but preserve the + // target state that was there before. + mCurrentState = STATE_PREPARING; + attachMediaController(); + } catch (IOException ex) { + Log.w(TAG, "Unable to open content: " + mUri, ex); + mCurrentState = STATE_ERROR; + mTargetState = STATE_ERROR; + mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + return; + } catch (IllegalArgumentException ex) { + Log.w(TAG, "Unable to open content: " + mUri, ex); + mCurrentState = STATE_ERROR; + mTargetState = STATE_ERROR; + mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + return; } } - private void onError() { - mTargetState = mCurrentState = V2XConst.STATE_ERROR; - if (mErrorListener != null) - mErrorListener.onError(mMediaPlayer, - MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + public void setMediaController(MediaController controller) { + if (mMediaController != null) { + mMediaController.hide(); + } + mMediaController = controller; + attachMediaController(); } - 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); - } + private void attachMediaController() { + if (mMediaPlayer != null && mMediaController != null) { + mMediaController.setMediaPlayer(this); + View anchorView = this.getParent() instanceof View ? + (View)this.getParent() : this; + mMediaController.setAnchorView(anchorView); + mMediaController.setEnabled(isInPlaybackState()); } - }; + } + + 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) { + getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight); + requestLayout(); + } + } + }; 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(); + mCurrentState = STATE_PREPARED; + + mCanPause = mCanSeekBack = mCanSeekForward = true; - 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); + if (mMediaController != null) { + mMediaController.setEnabled(true); } - } - }; + mVideoWidth = mp.getVideoWidth(); + mVideoHeight = mp.getVideoHeight(); - 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); + int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call + if (seekToPosition != 0) { + seekTo(seekToPosition); } - 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); + if (mVideoWidth != 0 && mVideoHeight != 0) { + //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight); + getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight); + // We won't get a "surface changed" callback if the surface is already the right size, so + // start the video here instead of in the callback. + if (mTargetState == STATE_PLAYING) { + start(); + if (mMediaController != null) { + mMediaController.show(); + } + } else if (!isPlaying() && + (seekToPosition != 0 || getCurrentPosition() > 0)) { + if (mMediaController != null) { + // Show the media controls when we're paused into a video and make 'em stick. + mMediaController.show(0); + } + } + } else { + // We don't know the video size yet, but should start anyway. + // The video size might be reported to us later. + if (mTargetState == STATE_PLAYING) { + start(); } - 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; - } - }; + private OnCompletionListener mCompletionListener = + new OnCompletionListener() { + public void onCompletion(MediaPlayer mp) { + mCurrentState = STATE_PLAYBACK_COMPLETED; + mTargetState = STATE_PLAYBACK_COMPLETED; + if (mMediaController != null) { + mMediaController.hide(); + } + if (mOnCompletionListener != null) { + mOnCompletionListener.onCompletion(mMediaPlayer); + } + } + }; + + private OnInfoListener mInfoListener = + new OnInfoListener() { + public boolean onInfo(MediaPlayer mp, int arg1, int arg2) { + if (mOnInfoListener != null) { + mOnInfoListener.onInfo(mp, arg1, arg2); + } + return true; + } + }; + + private OnErrorListener mErrorListener = + new OnErrorListener() { + public boolean onError(MediaPlayer mp, int framework_err, int impl_err) { + Log.d(TAG, "Error: " + framework_err + "," + impl_err); + mCurrentState = STATE_ERROR; + mTargetState = STATE_ERROR; + if (mMediaController != null) { + mMediaController.hide(); + } + + /* If an error handler has been supplied, use it and finish. */ + if (mOnErrorListener != null) { + if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) { + return true; + } + } + + /* Otherwise, pop up an error dialog so the user knows that + * something bad has happened. Only try and pop up the dialog + * if we're attached to a window. When we're going away and no + * longer have a window, don't bother showing the user an error. + */ + if (getWindowToken() != null) { + Resources r = getContext().getResources(); + int messageId; + + if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) { + messageId = android.R.string.VideoView_error_text_invalid_progressive_playback; + } else { + messageId = android.R.string.VideoView_error_text_unknown; + } + + new AlertDialog.Builder(getContext()) + .setMessage(messageId) + .setPositiveButton(android.R.string.VideoView_error_button, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + /* If we get here, there is no onError listener, so + * at least inform them that the video is over. + */ + if (mOnCompletionListener != null) { + mOnCompletionListener.onCompletion(mMediaPlayer); + } + } + }) + .setCancelable(false) + .show(); + } + return true; + } + }; + + private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = + new MediaPlayer.OnBufferingUpdateListener() { + public void onBufferingUpdate(MediaPlayer mp, int percent) { + mCurrentBufferPercentage = percent; + } + }; /** - * Register a callback to be invoked when the media file is loaded and ready - * to go. + * 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) { + 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. + * 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) { + 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. + * 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, TextureVideoView will inform + * the user of any errors. * * @param l The callback that will be run */ - public void setOnErrorListener(OnErrorListener l) { + public void setOnErrorListener(OnErrorListener l) + { mOnErrorListener = l; } - public void setOnInfoListener(MediaPlayer.OnInfoListener l) { + /** + * Register a callback to be invoked when an informational event + * occurs during playback or setup. + * + * @param l The callback that will be run + */ + public void setOnInfoListener(OnInfoListener l) { mOnInfoListener = l; } - SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() { + SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() + { @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - Logger.i(MODULE_NAME, "onSurfaceTextureAvailable"); + public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) { + boolean isValidState = (mTargetState == STATE_PLAYING); + boolean hasValidSize = (width > 0 && height > 0); + if (mMediaPlayer != null && isValidState && hasValidSize) { + if (mSeekWhenPrepared != 0) { + seekTo(mSeekWhenPrepared); + } + start(); + } + } + + @Override + public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) { 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(); + public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) { + // after we return from this we can't use the surface any more + if (mSurface != null) { + mSurface.release(); + mSurface = null; } + if (mMediaController != null) mMediaController.hide(); + release(true); + return true; } - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - Logger.i(MODULE_NAME, "onSurfaceTextureDestroyed"); - mSurface = null; - release(false); - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - + public void onSurfaceTextureUpdated(final SurfaceTexture surface) { + // do nothing } }; @@ -382,131 +615,125 @@ public class TextureVideoView extends TextureView implements MediaPlayerControl * 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(); - } - } + if (mMediaPlayer != null) { + mMediaPlayer.reset(); + mMediaPlayer.release(); mMediaPlayer = null; + mCurrentState = STATE_IDLE; + if (cleartargetstate) { + mTargetState = STATE_IDLE; + } + if (mShouldRequestAudioFocus) { + AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); + am.abandonAudioFocus(null); + } } } - public boolean isPrepared() { - return mPrepareState == V2XConst.STATE_PREPARED - || isSuspendFromActivity; + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (isInPlaybackState() && mMediaController != null) { + toggleMediaControlsVisiblity(); + } + return false; } + @Override + public boolean onTrackballEvent(MotionEvent ev) { + if (isInPlaybackState() && mMediaController != null) { + toggleMediaControlsVisiblity(); + } + return false; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && + keyCode != KeyEvent.KEYCODE_VOLUME_UP && + keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && + keyCode != KeyEvent.KEYCODE_VOLUME_MUTE && + keyCode != KeyEvent.KEYCODE_MENU && + keyCode != KeyEvent.KEYCODE_CALL && + keyCode != KeyEvent.KEYCODE_ENDCALL; + if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) { + if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || + keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { + if (mMediaPlayer.isPlaying()) { + pause(); + mMediaController.show(); + } else { + start(); + mMediaController.hide(); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { + if (!mMediaPlayer.isPlaying()) { + start(); + mMediaController.hide(); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP + || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { + if (mMediaPlayer.isPlaying()) { + pause(); + mMediaController.show(); + } + return true; + } else { + toggleMediaControlsVisiblity(); + } + } + + return super.onKeyDown(keyCode, event); + } + + private void toggleMediaControlsVisiblity() { + if (mMediaController.isShowing()) { + mMediaController.hide(); + } else { + mMediaController.show(); + } + } + + @Override 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; + mCurrentState = STATE_PLAYING; } - mTargetState = V2XConst.STATE_PLAYING; + mTargetState = STATE_PLAYING; } + @Override 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; + mCurrentState = STATE_PAUSED; } } - mTagetStateBackup = mTargetState = V2XConst.STATE_PAUSED; + mTargetState = 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); - } + 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 + @Override public int getDuration() { if (isInPlaybackState()) { - if (mDuration > 0) { - return mDuration; - } - mDuration = mMediaPlayer.getDuration(); - return mDuration; + return mMediaPlayer.getDuration(); } - mDuration = -1; - return mDuration; + + return -1; } + @Override public int getCurrentPosition() { if (isInPlaybackState()) { return mMediaPlayer.getCurrentPosition(); @@ -514,25 +741,22 @@ public class TextureVideoView extends TextureView implements MediaPlayerControl return 0; } + @Override 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); + if (isInPlaybackState()) { mMediaPlayer.seekTo(msec); mSeekWhenPrepared = 0; } else { mSeekWhenPrepared = msec; - progressWhileSuspend = msec; } } + @Override public boolean isPlaying() { return isInPlaybackState() && mMediaPlayer.isPlaying(); } + @Override public int getBufferPercentage() { if (mMediaPlayer != null) { return mCurrentBufferPercentage; @@ -541,50 +765,58 @@ public class TextureVideoView extends TextureView implements MediaPlayerControl } private boolean isInPlaybackState() { - return (mMediaPlayer != null && mCurrentState != V2XConst.STATE_ERROR - && mCurrentState != V2XConst.STATE_IDLE && mCurrentState != V2XConst.STATE_PREPARING); + return (mMediaPlayer != null && + mCurrentState != STATE_ERROR && + mCurrentState != STATE_IDLE && + mCurrentState != STATE_PREPARING); } @Override public boolean canPause() { - return false; + return mCanPause; } @Override public boolean canSeekBackward() { - return false; + return mCanSeekBack; } @Override public boolean canSeekForward() { - return false; + return mCanSeekForward; } - 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; + if (mAudioSession == 0) { + MediaPlayer foo = new MediaPlayer(); + mAudioSession = foo.getAudioSessionId(); + foo.release(); + } + return mAudioSession; } - public int getCurrentState() { - Logger.i(MODULE_NAME, "mCurrentState == " + mCurrentState); - return mCurrentState; + /** + * Sets the request audio focus flag. If enabled, {@link TextureVideoView} will request + * audio focus when opening a video by calling {@link AudioManager}. This flag + * should be set before calling {@link TextureVideoView#setVideoPath(String)} or + * {@link TextureVideoView#setVideoURI(Uri)}. By default, {@link TextureVideoView} will + * request audio focus. + * + * @param shouldRequestAudioFocus If {@code true}, {@link TextureVideoView} will request + * audio focus before opening a video, else audio focus is not requested + */ + public void setShouldRequestAudioFocus(boolean shouldRequestAudioFocus) { + mShouldRequestAudioFocus = shouldRequestAudioFocus; } + + /** + * Returns the current state of the audio focus request flag. + * + * @return {@code true}, if {@link TextureVideoView} will request + * audio focus before opening a video, else {@code false} + */ + public boolean shouldRequestAudioFocus() { + return mShouldRequestAudioFocus; + } + } From fd11615922731700576c78b6f9679f62c328e129 Mon Sep 17 00:00:00 2001 From: liujing Date: Tue, 20 Oct 2020 16:30:12 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=99=A8=E5=A4=A7?= =?UTF-8?q?=E5=B0=8F=E8=87=AA=E5=AE=9A=E4=B9=89!=3D=E8=A7=86=E9=A2=91size?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/mogo/module/v2x/view/TextureVideoView.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java index 89d30e4a5a..25d6690a3c 100644 --- a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java +++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java @@ -394,8 +394,11 @@ public class TextureVideoView extends TextureView mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); if (mVideoWidth != 0 && mVideoHeight != 0) { - getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight); - requestLayout(); + /* + * 播放器大小等于获取的视频尺寸!!!!!!!!!!!!!!!!! + * */ +// getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight); +// requestLayout(); } } }; @@ -421,7 +424,7 @@ public class TextureVideoView extends TextureView } if (mVideoWidth != 0 && mVideoHeight != 0) { //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight); - getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight); +// getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight);/*播放器大小等于获取的视频尺寸!!!!!!!!!!!!!!!!!*/ // We won't get a "surface changed" callback if the surface is already the right size, so // start the video here instead of in the callback. if (mTargetState == STATE_PLAYING) { From d158ab5ba209eb77c05f19c09ddcbf1ab1a757a2 Mon Sep 17 00:00:00 2001 From: tongchenfei Date: Tue, 20 Oct 2020 20:21:06 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=8A=BD=E7=A6=BB=E5=88=86=E4=BA=AB?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=B8=AD=E7=9A=84=E5=94=A4=E9=86=92=E8=AF=8D?= =?UTF-8?q?=E5=92=8C=E5=85=8D=E5=94=A4=E9=86=92=E6=8C=87=E4=BB=A4=EF=BC=8C?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E5=88=86=E4=BA=AB=E6=A8=A1=E5=9D=97=E7=9A=84?= =?UTF-8?q?baseModule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mogo/launcher/MogoApplication.java | 4 +- .../com/mogo/module/main/SchemeIntent.java | 10 ++ .../service/unwake/GlobalUnwakeConst.java | 5 +- .../service/unwake/GlobalUnwakeManager.java | 32 +++- .../com/mogo/module/share/ShareControl.java | 169 ++++++++---------- .../com/mogo/service/MogoServicePaths.java | 5 + .../mogo/service/share/IMogoShareManager.java | 3 +- .../voice/IMogoGlobalVoiceManager.java | 16 ++ 8 files changed, 150 insertions(+), 94 deletions(-) create mode 100644 services/mogo-service-api/src/main/java/com/mogo/service/voice/IMogoGlobalVoiceManager.java diff --git a/app/src/main/java/com/mogo/launcher/MogoApplication.java b/app/src/main/java/com/mogo/launcher/MogoApplication.java index b77aa4cede..6e680df8e7 100644 --- a/app/src/main/java/com/mogo/launcher/MogoApplication.java +++ b/app/src/main/java/com/mogo/launcher/MogoApplication.java @@ -78,7 +78,7 @@ public class MogoApplication extends AbsMogoApplication { MogoModulePaths.addModule( new MogoModule( TanluApiConst.MODULE_PATH, TanluApiConst.MODULE_NAME ) ); MogoModulePaths.addBaseModule( new MogoModule( TanluConstants.TAG, TanluConstants.MODEL_NAME ) ); - MogoModulePaths.addBaseModule( new MogoModule( MogoServicePaths.PATH_SHARE, "ShareControl" ) ); + MogoModulePaths.addModule( new MogoModule( MogoServicePaths.PATH_SHARE, "ShareControl" ) ); MogoModulePaths.addModule( new MogoModule( LeftPanelConst.PATH_NAME, LeftPanelConst.MODULE_NAME ) ); @@ -88,6 +88,8 @@ public class MogoApplication extends AbsMogoApplication { MogoModulePaths.addBaseModule( new MogoModule( MogoServicePaths.PATH_MOGO_MONITOR, "MogoMonitor" ) ); MogoModulePaths.addModule( new MogoModule( PushUIConstants.PATH, PushUIConstants.NAME ) ); + MogoModulePaths.addBaseModule(new MogoModule(MogoServicePaths.PATH_GLOBAL_UNWAKE,"GlobalUnwake")); + if ( !DebugConfig.isLauncher() ) { PersistentManager.getInstance().initManager( this ); Intent intent = new Intent( this, MogoMainService.class ); diff --git a/modules/mogo-module-main/src/main/java/com/mogo/module/main/SchemeIntent.java b/modules/mogo-module-main/src/main/java/com/mogo/module/main/SchemeIntent.java index 81d435746f..afe5deee67 100644 --- a/modules/mogo-module-main/src/main/java/com/mogo/module/main/SchemeIntent.java +++ b/modules/mogo-module-main/src/main/java/com/mogo/module/main/SchemeIntent.java @@ -37,6 +37,8 @@ public class SchemeIntent implements IMogoStatusChangedListener { public static final String TYPE_SHOW_ONLINE_CAR_PANEL = "showOnlineCarPanel"; + public static final String TYPE_SHOW_SHARE_PANEL = "showSharePanel"; + private IMogoServiceApis mApis; private Context mContext; @@ -150,6 +152,9 @@ public class SchemeIntent implements IMogoStatusChangedListener { break; case TYPE_SHOW_ONLINE_CAR_PANEL: handleShowOnlineCarPanel( target ); + case TYPE_SHOW_SHARE_PANEL: + handleShowSharePanel(); + break; default: break; @@ -199,6 +204,11 @@ public class SchemeIntent implements IMogoStatusChangedListener { mApis.getOnlineCarPanelApi().showPanel(); } + private void handleShowSharePanel() { + Logger.d(TAG, "handleShowSharePanel"); + // todo 跳转到热心指数 + } + @Override public void onStatusChanged( StatusDescriptor descriptor, boolean isTrue ) { if ( descriptor == StatusDescriptor.MAIN_PAGE_RESUME ) { diff --git a/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeConst.java b/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeConst.java index 7f9ea74ab2..fd85c4e3f7 100644 --- a/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeConst.java +++ b/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeConst.java @@ -7,7 +7,6 @@ package com.mogo.module.service.unwake; */ public class GlobalUnwakeConst { public static final String MODULE_NAME = "GlobalUnwakeManager"; - public static final String PATH_NAME = "/global/unwake"; // command public static final String VOICE_CMD_GO_TO_SHARE = "com.zhidao.share"; @@ -38,6 +37,10 @@ public class GlobalUnwakeConst { */ public static final String UNWAKE_UPLOAD_REAL_TIME_TRAFFIC = "command_upload_real_time_traffic"; + /** + * 唤醒词查询热心指数 + */ + public static final String VOICE_QUERY_HEART_INDEX = "com.zhidao.query.myshare.index"; // 词 public static final String[] UPLOAD_REAL_TIME_TRAFFIC = {"上报实时路况", "上报路况"}; diff --git a/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeManager.java b/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeManager.java index 6a19314179..e98f703a5a 100644 --- a/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeManager.java +++ b/modules/mogo-module-service/src/main/java/com/mogo/module/service/unwake/GlobalUnwakeManager.java @@ -2,11 +2,14 @@ package com.mogo.module.service.unwake; import android.content.Context; import android.content.Intent; +import android.net.Uri; +import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.facade.template.IProvider; import com.mogo.commons.voice.AIAssist; import com.mogo.commons.voice.IMogoVoiceCmdCallBack; import com.mogo.module.common.MogoApisHandler; +import com.mogo.service.MogoServicePaths; import com.mogo.service.intent.IMogoIntentListener; import com.mogo.service.intent.IMogoIntentManager; import com.mogo.utils.logger.Logger; @@ -19,18 +22,23 @@ import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_NO_REPL import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_PUB_ROAD_CONDITION; import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_PUB_TROUBLE_HELP; import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_CMD_SHARE_DIALOG_CLOSE; +import static com.mogo.module.service.unwake.GlobalUnwakeConst.VOICE_QUERY_HEART_INDEX; /** * 全局免唤醒管理 + * 包括唤醒词指令和全局免唤醒词指令 * 有几个Module仅仅是因为要注册全局免唤醒词,加到了BaseModule中,为了将这部分Module从BaseModule中去掉,特抽离全局免唤醒词注册监听逻辑 * * @author tongchenfei */ +@Route(path = MogoServicePaths.PATH_GLOBAL_UNWAKE) public class GlobalUnwakeManager implements IProvider, IMogoIntentListener, IMogoVoiceCmdCallBack { private IMogoIntentManager intentManager; + private Context context; private static final String TAG = "GlobalUnwakeManager"; @Override public void init(Context context) { + Logger.d(TAG, "全局免唤醒模块初始化===="); intentManager = MogoApisHandler.getInstance().getApis().getIntentManagerApi(); intentManager.registerIntentListener(VOICE_CMD_GO_TO_SHARE, this); intentManager.registerIntentListener(VOICE_CMD_PUB_TROUBLE_HELP, this); @@ -49,9 +57,31 @@ public class GlobalUnwakeManager implements IProvider, IMogoIntentListener, IMog // 此处只接受处理语音相关广播 Logger.d(TAG, "收到唤醒词指令: " + intentStr); switch (intentStr) { - + // 分享相关唤醒词 + case VOICE_CMD_GO_TO_SHARE: + case VOICE_CMD_PUB_TROUBLE_HELP: + case VOICE_CMD_PUB_ROAD_CONDITION: + case VOICE_CMD_SHARE_DIALOG_CLOSE: + case VOICE_CMD_NO_REPLY_SHARE_DIALOG_CLOSE: + case UNWAKE_UPLOAD_ROAD_CONDITION: + MogoApisHandler.getInstance().getApis().getShareManager().onGlobalUnwake(intentStr, intent); + break; + case VOICE_QUERY_HEART_INDEX: + Intent start = new Intent( Intent.ACTION_VIEW ); + start.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); + start.setData( Uri.parse( "mogo://launcher/main/switch2?type=showSharePanel" ) ); + context.startActivity( start ); + break; default: break; } } + + @Override + public void onCmdSelected(String cmd) { + Logger.d(TAG, "收到免唤醒词指令: " + cmd); + if (UNWAKE_UPLOAD_REAL_TIME_TRAFFIC.equals(cmd)) { + MogoApisHandler.getInstance().getApis().getShareManager().onGlobalUnwake(cmd, null); + } + } } diff --git a/modules/mogo-module-share/src/main/java/com/mogo/module/share/ShareControl.java b/modules/mogo-module-share/src/main/java/com/mogo/module/share/ShareControl.java index 3a7982f76e..2b98266a01 100644 --- a/modules/mogo-module-share/src/main/java/com/mogo/module/share/ShareControl.java +++ b/modules/mogo-module-share/src/main/java/com/mogo/module/share/ShareControl.java @@ -97,8 +97,7 @@ import static com.mogo.service.share.IMogoTanluProvider.TYPE_TRAFFIC_CHECK; * @since 2020-01-10 */ @Route(path = MogoServicePaths.PATH_SHARE) -public class ShareControl implements IMogoShareManager, IMogoIntentListener, - IMogoVoiceCmdCallBack, Handler.Callback { +public class ShareControl implements IMogoShareManager, Handler.Callback { private static final String TAG = "ShareControl"; private Context mContext; @@ -228,94 +227,83 @@ public class ShareControl implements IMogoShareManager, IMogoIntentListener, } @Override - public void onCmdSelected(String cmd) { - Logger.d(TAG, "收到免唤醒词指令: " + cmd); - switch (cmd) { - case UNWAKE_CANCEL_SHARE: - dismissShareDialog(); - break; - case UNWAKE_UPLOAD_ROAD_BLOCK: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_BLOCK, false); - } else { - uploadFromVoice(TYPE_BLOCK); - } - break; - case UNWAKE_UPLOAD_TRAFFIC_CHECK: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_TRAFFIC_CHECK, false); - } else { - uploadFromVoice(TYPE_TRAFFIC_CHECK); - } - break; - case UNWAKE_UPLOAD_ROAD_CLOSURE: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_CLOSURE, false); - } else { - uploadFromVoice(TYPE_CLOSURE); - } - break; - case UNWAKE_UPLOAD_ACCIDENT: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_ACCIDENT, false); - } else { - uploadFromVoice(TYPE_ACCIDENT); - } - break; - case UNWAKE_UPLOAD_REAL_TIME_TRAFFIC: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_REAL_TIME_TRAFFIC, false); - } else { - uploadFromVoice(TYPE_REAL_TIME_TRAFFIC); - } - break; - case UNWAKE_UPLOAD_SEEK_HELP: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_SEEK_HELP, TYPE_BLOCK, false); - } else { - seekHelp(); - } - - break; - case UNWAKE_UPLOAD_STAGNANT_WATER: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_STAGNANT_WATER, false); - } else { - uploadFromVoice(TYPE_STAGNANT_WATER); - } - break; - case UNWAKE_UPLOAD_ROAD_ICY: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_ROAD_ICY, false); - } else { - uploadFromVoice(TYPE_ROAD_ICY); - } - break; - case UNWAKE_UPLOAD_DENSE_FOG: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_DENSE_FOG, false); - } else { - uploadFromVoice(TYPE_DENSE_FOG); - } - break; - case UNWAKE_UPLOAD_ROAD_CONSTRUCTION: - if (needAuth()) { - goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_ROAD_CONSTRUCTION, false); - } else { - uploadFromVoice(TYPE_ROAD_CONSTRUCTION); - } - break; - default: - break; - } - } - - @Override - public void onIntentReceived(String intentStr, Intent intent) { - // 此处只接受处理语音相关广播 - Logger.d(TAG, "收到唤醒词指令: " + intentStr); + public void onGlobalUnwake(String cmd, Intent intent) { try { - switch (intentStr) { + switch (cmd) { + case UNWAKE_CANCEL_SHARE: + dismissShareDialog(); + break; + case UNWAKE_UPLOAD_ROAD_BLOCK: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_BLOCK, false); + } else { + uploadFromVoice(TYPE_BLOCK); + } + break; + case UNWAKE_UPLOAD_TRAFFIC_CHECK: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_TRAFFIC_CHECK, false); + } else { + uploadFromVoice(TYPE_TRAFFIC_CHECK); + } + break; + case UNWAKE_UPLOAD_ROAD_CLOSURE: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_CLOSURE, false); + } else { + uploadFromVoice(TYPE_CLOSURE); + } + break; + case UNWAKE_UPLOAD_ACCIDENT: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_ACCIDENT, false); + } else { + uploadFromVoice(TYPE_ACCIDENT); + } + break; + case UNWAKE_UPLOAD_REAL_TIME_TRAFFIC: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_REAL_TIME_TRAFFIC, false); + } else { + uploadFromVoice(TYPE_REAL_TIME_TRAFFIC); + } + break; + case UNWAKE_UPLOAD_SEEK_HELP: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_SEEK_HELP, TYPE_BLOCK, false); + } else { + seekHelp(); + } + + break; + case UNWAKE_UPLOAD_STAGNANT_WATER: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_STAGNANT_WATER, false); + } else { + uploadFromVoice(TYPE_STAGNANT_WATER); + } + break; + case UNWAKE_UPLOAD_ROAD_ICY: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_ROAD_ICY, false); + } else { + uploadFromVoice(TYPE_ROAD_ICY); + } + break; + case UNWAKE_UPLOAD_DENSE_FOG: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_DENSE_FOG, false); + } else { + uploadFromVoice(TYPE_DENSE_FOG); + } + break; + case UNWAKE_UPLOAD_ROAD_CONSTRUCTION: + if (needAuth()) { + goAuth(STEP_AFTER_AUTH_TYPE_UPLOAD, TYPE_ROAD_CONSTRUCTION, false); + } else { + uploadFromVoice(TYPE_ROAD_CONSTRUCTION); + } + break; case VOICE_CMD_PUB_TROUBLE_HELP: if (needAuth()) { goAuth(STEP_AFTER_AUTH_TYPE_SEEK_HELP, TYPE_DENSE_FOG, false); @@ -431,7 +419,8 @@ public class ShareControl implements IMogoShareManager, IMogoIntentListener, break; } } catch (Exception e) { - Logger.e(TAG, e, "ShareControl exception"); + Logger.e(TAG, e, "onGlobalUnwake exception"); + e.printStackTrace(); } } diff --git a/services/mogo-service-api/src/main/java/com/mogo/service/MogoServicePaths.java b/services/mogo-service-api/src/main/java/com/mogo/service/MogoServicePaths.java index bca6a29631..4cb53099a2 100644 --- a/services/mogo-service-api/src/main/java/com/mogo/service/MogoServicePaths.java +++ b/services/mogo-service-api/src/main/java/com/mogo/service/MogoServicePaths.java @@ -260,4 +260,9 @@ public class MogoServicePaths { * 在线好友面板 */ public static final String PATH_ONLINE_CAR_PANEL = "/onlinecar/panel"; + + /** + * 全局免唤醒 + */ + public static final String PATH_GLOBAL_UNWAKE = "/global/unwake"; } diff --git a/services/mogo-service-api/src/main/java/com/mogo/service/share/IMogoShareManager.java b/services/mogo-service-api/src/main/java/com/mogo/service/share/IMogoShareManager.java index ca0285449c..6ec41a931a 100644 --- a/services/mogo-service-api/src/main/java/com/mogo/service/share/IMogoShareManager.java +++ b/services/mogo-service-api/src/main/java/com/mogo/service/share/IMogoShareManager.java @@ -4,12 +4,13 @@ import android.content.Context; import com.alibaba.android.arouter.facade.template.IProvider; import com.mogo.service.module.IMogoModuleProvider; +import com.mogo.service.voice.IMogoGlobalVoiceManager; /** * 分享框管理接口 * @author tongchenfei */ -public interface IMogoShareManager extends IProvider { +public interface IMogoShareManager extends IProvider, IMogoGlobalVoiceManager { /** * 显示分享框 */ diff --git a/services/mogo-service-api/src/main/java/com/mogo/service/voice/IMogoGlobalVoiceManager.java b/services/mogo-service-api/src/main/java/com/mogo/service/voice/IMogoGlobalVoiceManager.java new file mode 100644 index 0000000000..b16230830c --- /dev/null +++ b/services/mogo-service-api/src/main/java/com/mogo/service/voice/IMogoGlobalVoiceManager.java @@ -0,0 +1,16 @@ +package com.mogo.service.voice; + +import android.content.Intent; + +/** + * 全局免唤醒指令处理监听 + * + * @author tongchenfei + */ +public interface IMogoGlobalVoiceManager { + /** + * 收到全局免唤醒指令和唤醒词指令 + * @param cmd 具体指令 + */ + void onGlobalUnwake(String cmd, Intent intent); +} From 02d805ebc41ec40ab85e620940cdd5c210114317 Mon Sep 17 00:00:00 2001 From: liujing Date: Tue, 20 Oct 2020 20:52:04 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E8=BF=94=E5=9B=9E=E8=80=81=E7=89=88?= =?UTF-8?q?=E6=9C=AC-=E6=9A=82=E6=97=B6=E4=B8=8D=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/v2x/view/TextureVideoView.java | 919 +++++++----------- 1 file changed, 342 insertions(+), 577 deletions(-) diff --git a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java index 25d6690a3c..b628bcd25f 100644 --- a/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java +++ b/modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/view/TextureVideoView.java @@ -1,3 +1,5 @@ +package com.mogo.module.v2x.view; + /* * Copyright (C) 2006 The Android Open Source Project * @@ -14,245 +16,129 @@ * limitations under the License. */ -package com.mogo.module.v2x.view; - -import android.app.AlertDialog; +import android.annotation.SuppressLint; import android.content.Context; -import android.content.DialogInterface; -import android.content.res.Resources; +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.OnInfoListener; +import android.media.MediaPlayer.OnSeekCompleteListener; import android.net.Uri; -import android.opengl.GLES20; -import android.os.Build; import android.util.AttributeSet; -import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.Surface; import android.view.TextureView; -import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.MediaController; 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 javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; +import static com.mogo.module.v2x.V2XConst.MODULE_NAME; /** - * Displays a video file. The TextureVideoView 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.

- * - * Note: VideoView does not retain its full state when going into the - * background. In particular, it does not restore the current play state, - * play position or selected tracks. Applications should - * save and restore these on their own in - * {@link android.app.Activity#onSaveInstanceState} and - * {@link android.app.Activity#onRestoreInstanceState}.

- * Also note that the audio session id (from {@link #getAudioSessionId}) may - * change from its previously returned value when the VideoView is restored.

- * - * This code is based on the official Android sources for 7.1.1_r13 with the following differences: - *

    - *
  1. extends {@link TextureView} instead of a {@link android.view.SurfaceView} - * allowing proper view animations
  2. - *
  3. removes code that uses hidden APIs and thus is not available (e.g. subtitle support)
  4. - *
  5. various small fixes and improvements
  6. - *
+ * 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 static final String TAG = "TextureVideoView"; - - // all possible internal states - private static final int STATE_ERROR = -1; - private static final int STATE_IDLE = 0; - private static final int STATE_PREPARING = 1; - private static final int STATE_PREPARED = 2; - private static final int STATE_PLAYING = 3; - private static final int STATE_PAUSED = 4; - private static final int STATE_PLAYBACK_COMPLETED = 5; - +public class TextureVideoView extends TextureView implements MediaPlayerControl { + private String TAG = "V2XModuleProvider"; // settable by the client private Uri mUri; private Map mHeaders; + private int mDuration; - // mCurrentState is a TextureVideoView object's current state. - // mTargetState is the state that a method caller intends to reach. - // For instance, regardless the TextureVideoView object's current state, - // calling pause() intends to bring the object to a target state - // of STATE_PAUSED. - private int mCurrentState = STATE_IDLE; - private int mTargetState = STATE_IDLE; + // 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 mAudioSession; - private int mVideoWidth; - private int mVideoHeight; - private MediaController mMediaController; + private int mVideoWidth, mVideoHeight; + private OnCompletionListener mOnCompletionListener; private MediaPlayer.OnPreparedListener mOnPreparedListener; private int mCurrentBufferPercentage; private OnErrorListener mOnErrorListener; - private OnInfoListener mOnInfoListener; - private int mSeekWhenPrepared; // recording the seek position while preparing - private boolean mCanPause; - private boolean mCanSeekBack; - private boolean mCanSeekForward; - private boolean mShouldRequestAudioFocus = true; + 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) { - this(context, null); + super(context); + initVideoView(); } public TextureVideoView(Context context, AttributeSet attrs) { - this(context, attrs, 0); + super(context, attrs, 0); + initVideoView(); } public TextureVideoView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - - mVideoWidth = 0; - mVideoHeight = 0; - - setSurfaceTextureListener(mSurfaceTextureListener); - - setFocusable(true); - setFocusableInTouchMode(true); - requestFocus(); - - mCurrentState = STATE_IDLE; - mTargetState = STATE_IDLE; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", " - // + MeasureSpec.toString(heightMeasureSpec) + ")"); - - int width = getDefaultSize(mVideoWidth, widthMeasureSpec); - int height = getDefaultSize(mVideoHeight, heightMeasureSpec); - if (mVideoWidth > 0 && mVideoHeight > 0) { - - int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) { - // the size is fixed - width = widthSpecSize; - height = heightSpecSize; - - // for compatibility, we adjust size based on aspect ratio - if ( mVideoWidth * height < width * mVideoHeight ) { - //Log.i("@@@", "image too wide, correcting"); - width = height * mVideoWidth / mVideoHeight; - } else if ( mVideoWidth * height > width * mVideoHeight ) { - //Log.i("@@@", "image too tall, correcting"); - height = width * mVideoHeight / mVideoWidth; - } - } else if (widthSpecMode == MeasureSpec.EXACTLY) { - // only the width is fixed, adjust the height to match aspect ratio if possible - width = widthSpecSize; - height = width * mVideoHeight / mVideoWidth; - if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { - // couldn't match aspect ratio within the constraints - height = heightSpecSize; - } - } else if (heightSpecMode == MeasureSpec.EXACTLY) { - // only the height is fixed, adjust the width to match aspect ratio if possible - height = heightSpecSize; - width = height * mVideoWidth / mVideoHeight; - if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { - // couldn't match aspect ratio within the constraints - width = widthSpecSize; - } - } else { - // neither the width nor the height are fixed, try to use actual video size - width = mVideoWidth; - height = mVideoHeight; - if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { - // too tall, decrease both width and height - height = heightSpecSize; - width = height * mVideoWidth / mVideoHeight; - } - if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { - // too wide, decrease both width and height - width = widthSpecSize; - height = width * mVideoHeight / mVideoWidth; - } - } - } else { - // no size yet, just adopt the given spec sizes - } - setMeasuredDimension(width, height); + 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()); } - public int resolveAdjustedSize(int desiredSize, int measureSpec) { - return getDefaultSize(desiredSize, measureSpec); + 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; } - /** - * Sets video path. - * - * @param path the path of the video. - */ public void setVideoPath(String path) { setVideoURI(Uri.parse(path)); } - /** - * Sets video URI. - * - * @param uri the URI of the video. - */ public void setVideoURI(Uri uri) { setVideoURI(uri, null); } - /** - * Sets video URI using specific headers. - * - * @param uri the URI of the video. - * @param headers the headers for the URI request. - * Note that the cross domain redirection is allowed by default, but that can be - * changed with key/value pairs through the headers parameter with - * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value - * to disallow or allow cross domain redirection. - */ public void setVideoURI(Uri uri, Map 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(); @@ -263,354 +149,232 @@ public class TextureVideoView extends TextureView mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; - mCurrentState = STATE_IDLE; - mTargetState = STATE_IDLE; - if (mShouldRequestAudioFocus) { - AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); - am.abandonAudioFocus(null); - } + mTargetState = mCurrentState = V2XConst.STATE_IDLE; } - clearSurface(); - } - - /** - * Clears the surface texture by attaching a GL context and clearing it. - * Code taken from Hugo Gresse's answer on stackoverflow.com. - */ - private void clearSurface() { - if (mSurface == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - return; - } - - EGL10 egl = (EGL10) EGLContext.getEGL(); - EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - egl.eglInitialize(display, null); - - int[] attribList = { - EGL10.EGL_RED_SIZE, 8, - EGL10.EGL_GREEN_SIZE, 8, - EGL10.EGL_BLUE_SIZE, 8, - EGL10.EGL_ALPHA_SIZE, 8, - EGL10.EGL_RENDERABLE_TYPE, EGL10.EGL_WINDOW_BIT, - EGL10.EGL_NONE, 0, // placeholder for recordable [@-3] - EGL10.EGL_NONE - }; - EGLConfig[] configs = new EGLConfig[1]; - int[] numConfigs = new int[1]; - egl.eglChooseConfig(display, attribList, configs, configs.length, numConfigs); - EGLConfig config = configs[0]; - EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, new int[]{ - 12440, 2, EGL10.EGL_NONE - }); - EGLSurface eglSurface = egl.eglCreateWindowSurface(display, config, mSurface, new int[]{ - EGL10.EGL_NONE - }); - - egl.eglMakeCurrent(display, eglSurface, eglSurface, context); - GLES20.glClearColor(0, 0, 0, 1); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - egl.eglSwapBuffers(display, eglSurface); - egl.eglDestroySurface(display, eglSurface); - egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - egl.eglDestroyContext(display, context); - egl.eglTerminate(display); } + @SuppressLint("NewApi") private void openVideo() { - if (mUri == null || mSurface == null) { - // not ready for playback just yet, will try again later + 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; } - // we shouldn't clear the target state, because somebody might have - // called start() previously release(false); - - if (mShouldRequestAudioFocus) { - AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); - am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); - } - try { mMediaPlayer = new MediaPlayer(); - - if (mAudioSession != 0) { - mMediaPlayer.setAudioSessionId(mAudioSession); - } else { - mAudioSession = mMediaPlayer.getAudioSessionId(); - } mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); + mDuration = -1; mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); - mMediaPlayer.setOnInfoListener(mInfoListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); + mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener); + mMediaPlayer.setOnInfoListener(mInfoListener); mCurrentBufferPercentage = 0; - mMediaPlayer.setDataSource(getContext().getApplicationContext(), mUri, mHeaders); - mMediaPlayer.setSurface(mSurface); + 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.prepareAsync(); - - // we don't set the target state here either, but preserve the - // target state that was there before. - mCurrentState = STATE_PREPARING; - attachMediaController(); - } catch (IOException ex) { - Log.w(TAG, "Unable to open content: " + mUri, ex); - mCurrentState = STATE_ERROR; - mTargetState = STATE_ERROR; - mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); - return; - } catch (IllegalArgumentException ex) { - Log.w(TAG, "Unable to open content: " + mUri, ex); - mCurrentState = STATE_ERROR; - mTargetState = STATE_ERROR; - mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); - return; + mMediaPlayer.setVolume(0, 0); + mMediaPlayer.prepare(); + mPrepareState = mCurrentState = V2XConst.STATE_PREPARING; + } catch (IOException | SecurityException | IllegalStateException | IllegalArgumentException ex) { + ex.printStackTrace(); + onError(); } } - public void setMediaController(MediaController controller) { - if (mMediaController != null) { - mMediaController.hide(); - } - mMediaController = controller; - attachMediaController(); + private void onError() { + mTargetState = mCurrentState = V2XConst.STATE_ERROR; + if (mErrorListener != null) + mErrorListener.onError(mMediaPlayer, + MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); } - private void attachMediaController() { - if (mMediaPlayer != null && mMediaController != null) { - mMediaController.setMediaPlayer(this); - View anchorView = this.getParent() instanceof View ? - (View)this.getParent() : this; - mMediaController.setAnchorView(anchorView); - mMediaController.setEnabled(isInPlaybackState()); - } - } - - 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) { - /* - * 播放器大小等于获取的视频尺寸!!!!!!!!!!!!!!!!! - * */ -// getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight); -// requestLayout(); - } - } - }; - - MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() { - public void onPrepared(MediaPlayer mp) { - mCurrentState = STATE_PREPARED; - - mCanPause = mCanSeekBack = mCanSeekForward = true; - - if (mOnPreparedListener != null) { - mOnPreparedListener.onPrepared(mMediaPlayer); - } - if (mMediaController != null) { - mMediaController.setEnabled(true); - } + MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new MediaPlayer.OnVideoSizeChangedListener() { + public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); - - int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call - if (seekToPosition != 0) { - seekTo(seekToPosition); - } if (mVideoWidth != 0 && mVideoHeight != 0) { - //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight); -// getSurfaceTexture().setDefaultBufferSize(mVideoWidth, mVideoHeight);/*播放器大小等于获取的视频尺寸!!!!!!!!!!!!!!!!!*/ - // We won't get a "surface changed" callback if the surface is already the right size, so - // start the video here instead of in the callback. - if (mTargetState == STATE_PLAYING) { - start(); - if (mMediaController != null) { - mMediaController.show(); - } - } else if (!isPlaying() && - (seekToPosition != 0 || getCurrentPosition() > 0)) { - if (mMediaController != null) { - // Show the media controls when we're paused into a video and make 'em stick. - mMediaController.show(0); - } - } - } else { - // We don't know the video size yet, but should start anyway. - // The video size might be reported to us later. - if (mTargetState == STATE_PLAYING) { - start(); - } + Logger.d(MODULE_NAME, "OnVideoSizeChangedListener mVideoWidth:" + " mVideoWidth:" + mVideoWidth + " mVideoHeight:" + mVideoHeight); } } }; - private OnCompletionListener mCompletionListener = - new OnCompletionListener() { - public void onCompletion(MediaPlayer mp) { - mCurrentState = STATE_PLAYBACK_COMPLETED; - mTargetState = STATE_PLAYBACK_COMPLETED; - if (mMediaController != null) { - mMediaController.hide(); - } - if (mOnCompletionListener != null) { - mOnCompletionListener.onCompletion(mMediaPlayer); - } + 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 OnInfoListener mInfoListener = - new OnInfoListener() { - public boolean onInfo(MediaPlayer mp, int arg1, int arg2) { - if (mOnInfoListener != null) { - mOnInfoListener.onInfo(mp, arg1, arg2); - } - return true; - } - }; - - private OnErrorListener mErrorListener = - new OnErrorListener() { - public boolean onError(MediaPlayer mp, int framework_err, int impl_err) { - Log.d(TAG, "Error: " + framework_err + "," + impl_err); - mCurrentState = STATE_ERROR; - mTargetState = STATE_ERROR; - if (mMediaController != null) { - mMediaController.hide(); - } - - /* If an error handler has been supplied, use it and finish. */ - if (mOnErrorListener != null) { - if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) { - return true; - } - } - - /* Otherwise, pop up an error dialog so the user knows that - * something bad has happened. Only try and pop up the dialog - * if we're attached to a window. When we're going away and no - * longer have a window, don't bother showing the user an error. - */ - if (getWindowToken() != null) { - Resources r = getContext().getResources(); - int messageId; - - if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) { - messageId = android.R.string.VideoView_error_text_invalid_progressive_playback; - } else { - messageId = android.R.string.VideoView_error_text_unknown; - } - - new AlertDialog.Builder(getContext()) - .setMessage(messageId) - .setPositiveButton(android.R.string.VideoView_error_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - /* If we get here, there is no onError listener, so - * at least inform them that the video is over. - */ - if (mOnCompletionListener != null) { - mOnCompletionListener.onCompletion(mMediaPlayer); - } - } - }) - .setCancelable(false) - .show(); - } - return true; - } - }; - - private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = - new MediaPlayer.OnBufferingUpdateListener() { - public void onBufferingUpdate(MediaPlayer mp, int percent) { - mCurrentBufferPercentage = percent; - } - }; + 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. + * 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) - { + 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. + * 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) - { + 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, TextureVideoView will inform - * the user of any errors. + * 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) - { + public void setOnErrorListener(OnErrorListener l) { mOnErrorListener = l; } - /** - * Register a callback to be invoked when an informational event - * occurs during playback or setup. - * - * @param l The callback that will be run - */ - public void setOnInfoListener(OnInfoListener l) { + public void setOnInfoListener(MediaPlayer.OnInfoListener l) { mOnInfoListener = l; } - SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() - { + SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() { @Override - public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) { - boolean isValidState = (mTargetState == STATE_PLAYING); - boolean hasValidSize = (width > 0 && height > 0); - if (mMediaPlayer != null && isValidState && hasValidSize) { - if (mSeekWhenPrepared != 0) { - seekTo(mSeekWhenPrepared); - } - start(); - } - } - - @Override - public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) { + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + Logger.i(MODULE_NAME, "onSurfaceTextureAvailable"); mSurface = new Surface(surface); openVideo(); } @Override - public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) { - // after we return from this we can't use the surface any more - if (mSurface != null) { - mSurface.release(); - mSurface = null; + 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(); } - if (mMediaController != null) mMediaController.hide(); - release(true); - return true; } + @Override - public void onSurfaceTextureUpdated(final SurfaceTexture surface) { - // do nothing + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + Logger.i(MODULE_NAME, "onSurfaceTextureDestroyed"); + mSurface = null; + release(false); + return false; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + } }; @@ -618,125 +382,131 @@ public class TextureVideoView extends TextureView * release the media player in any state */ private void release(boolean cleartargetstate) { - if (mMediaPlayer != null) { - mMediaPlayer.reset(); - mMediaPlayer.release(); - mMediaPlayer = null; - mCurrentState = STATE_IDLE; - if (cleartargetstate) { - mTargetState = STATE_IDLE; - } - if (mShouldRequestAudioFocus) { - AudioManager am = (AudioManager) getContext().getApplicationContext().getSystemService(Context.AUDIO_SERVICE); - am.abandonAudioFocus(null); - } - } - } + Logger.i(MODULE_NAME, "release ---cleartargetstate=" + cleartargetstate); + mCurrentState = V2XConst.STATE_IDLE; - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (isInPlaybackState() && mMediaController != null) { - toggleMediaControlsVisiblity(); - } - return false; - } + if (cleartargetstate) { + mTargetState = V2XConst.STATE_IDLE; - @Override - public boolean onTrackballEvent(MotionEvent ev) { - if (isInPlaybackState() && mMediaController != null) { - toggleMediaControlsVisiblity(); - } - return false; - } + 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(); + } + } + } - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) - { - boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && - keyCode != KeyEvent.KEYCODE_VOLUME_UP && - keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && - keyCode != KeyEvent.KEYCODE_VOLUME_MUTE && - keyCode != KeyEvent.KEYCODE_MENU && - keyCode != KeyEvent.KEYCODE_CALL && - keyCode != KeyEvent.KEYCODE_ENDCALL; - if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) { - if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || - keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { - if (mMediaPlayer.isPlaying()) { - pause(); - mMediaController.show(); - } else { - start(); - mMediaController.hide(); + if (mMediaPlayer != null) { + mMediaPlayer.release();// release会做reset + } + mMediaPlayer = null; + } catch (IllegalStateException e) { + e.printStackTrace(); + } } - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { - if (!mMediaPlayer.isPlaying()) { - start(); - mMediaController.hide(); - } - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP - || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { - if (mMediaPlayer.isPlaying()) { - pause(); - mMediaController.show(); - } - return true; - } else { - toggleMediaControlsVisiblity(); - } - } - - return super.onKeyDown(keyCode, event); - } - - private void toggleMediaControlsVisiblity() { - if (mMediaController.isShowing()) { - mMediaController.hide(); + }).start(); } else { - mMediaController.show(); + if (mMediaPlayer != null) { + try { + mMediaPlayer.stop(); + mMediaPlayer.reset(); + mMediaPlayer.release(); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + mMediaPlayer = null; } } - @Override + public boolean isPrepared() { + return mPrepareState == V2XConst.STATE_PREPARED + || isSuspendFromActivity; + } + public void start() { - if (isInPlaybackState()) { - mMediaPlayer.start(); - mCurrentState = STATE_PLAYING; + Logger.i(MODULE_NAME, "TextureVideoView---start"); + if (isFocusLoss) { + mTagetStateBackup = V2XConst.STATE_PLAYING; + return; } - mTargetState = STATE_PLAYING; + //Logger.i(MODULE_NAME, "mCurrentState = " + mCurrentState); + if (isInPlaybackState()) { + Logger.i(MODULE_NAME, "MediaPlayer.start"); + mMediaPlayer.start(); + mCurrentState = V2XConst.STATE_PLAYING; + } + mTargetState = V2XConst.STATE_PLAYING; } - @Override 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 = STATE_PAUSED; + mCurrentState = V2XConst.STATE_PAUSED; } } - mTargetState = STATE_PAUSED; + mTagetStateBackup = mTargetState = V2XConst.STATE_PAUSED; } public void suspend() { - release(false); + 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; + } + } + } } - @Override + // cache duration as mDuration for faster access public int getDuration() { if (isInPlaybackState()) { - return mMediaPlayer.getDuration(); + if (mDuration > 0) { + return mDuration; + } + mDuration = mMediaPlayer.getDuration(); + return mDuration; } - - return -1; + mDuration = -1; + return mDuration; } - @Override public int getCurrentPosition() { if (isInPlaybackState()) { return mMediaPlayer.getCurrentPosition(); @@ -744,22 +514,25 @@ public class TextureVideoView extends TextureView return 0; } - @Override public void seekTo(int msec) { - if (isInPlaybackState()) { + 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; } } - @Override public boolean isPlaying() { return isInPlaybackState() && mMediaPlayer.isPlaying(); } - @Override public int getBufferPercentage() { if (mMediaPlayer != null) { return mCurrentBufferPercentage; @@ -768,58 +541,50 @@ public class TextureVideoView extends TextureView } private boolean isInPlaybackState() { - return (mMediaPlayer != null && - mCurrentState != STATE_ERROR && - mCurrentState != STATE_IDLE && - mCurrentState != STATE_PREPARING); + return (mMediaPlayer != null && mCurrentState != V2XConst.STATE_ERROR + && mCurrentState != V2XConst.STATE_IDLE && mCurrentState != V2XConst.STATE_PREPARING); } @Override public boolean canPause() { - return mCanPause; + return false; } @Override public boolean canSeekBackward() { - return mCanSeekBack; + return false; } @Override public boolean canSeekForward() { - return mCanSeekForward; + return false; } - public int getAudioSessionId() { - if (mAudioSession == 0) { - MediaPlayer foo = new MediaPlayer(); - mAudioSession = foo.getAudioSessionId(); - foo.release(); + public String getVideoPath() { + if (mUri != null) { + return mUri.getPath(); } - return mAudioSession; + return null; } - /** - * Sets the request audio focus flag. If enabled, {@link TextureVideoView} will request - * audio focus when opening a video by calling {@link AudioManager}. This flag - * should be set before calling {@link TextureVideoView#setVideoPath(String)} or - * {@link TextureVideoView#setVideoURI(Uri)}. By default, {@link TextureVideoView} will - * request audio focus. - * - * @param shouldRequestAudioFocus If {@code true}, {@link TextureVideoView} will request - * audio focus before opening a video, else audio focus is not requested - */ - public void setShouldRequestAudioFocus(boolean shouldRequestAudioFocus) { - mShouldRequestAudioFocus = shouldRequestAudioFocus; + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = getDefaultSize(mVideoWidth, widthMeasureSpec); + int height = getDefaultSize(mVideoHeight, heightMeasureSpec); + setMeasuredDimension(width, height); } - /** - * Returns the current state of the audio focus request flag. - * - * @return {@code true}, if {@link TextureVideoView} will request - * audio focus before opening a video, else {@code false} - */ - public boolean shouldRequestAudioFocus() { - return mShouldRequestAudioFocus; + 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; + } }