cocos2d-x-js(cocos2d-x-3.17) 项目经验总结(二)

视频展示

视频展示本身没什么问题,但是当奇葩需求出现时就出现了问题,cocos在android端的绘制本身是在一个GLSurfaceView中进行的

public class Cocos2dxGLSurfaceView extends GLSurfaceView

而视频本身绘制也是SurfaceView,两个SurfaceView重叠就出现了意料之外的状况

最终作法
Cocos2dxActivity.java onCreateView方法中,在返回对象之前进行如下设置

glSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
        glSurfaceView.setZOrderOnTop(true);

Cocos2dxVideoHelper.java _createVideoView方法中,进行如下修改

mLayout.addView(videoView, 0, lParams);//yjh TODO 视频显示在最上面,游戏的glSurfaceView setZOrderMediaOverlay展示在视频上面
        //yjh TODO videoView.setZOrderOnTop(true);

经过一系列的修改优化


image.png

Cocos2dxActivity.java的最终代码如下

/****************************************************************************
Copyright (c) 2010-2013 cocos2d-x.org
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.

http://www.cocos2d-x.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
 ****************************************************************************/
package org.cocos2dx.lib;

import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.preference.PreferenceManager.OnActivityResultListener;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;

import org.cocos2dx.lib.Cocos2dxHelper.Cocos2dxHelperListener;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;

public abstract class Cocos2dxActivity extends AppCompatActivity implements Cocos2dxHelperListener {
    // ===========================================================
    // Constants
    // ===========================================================

    private final static String TAG = Cocos2dxActivity.class.getSimpleName();

    // ===========================================================
    // Fields
    // ===========================================================
    
    private Cocos2dxGLSurfaceView mGLSurfaceView = null;
    private int[] mGLContextAttrs = null;
    private Cocos2dxHandler mHandler = null;   
    private static Cocos2dxActivity sContext = null;
    private Cocos2dxVideoHelper mVideoHelper = null;
    private Cocos2dxWebViewHelper mWebViewHelper = null;
    private Cocos2dxEditBoxHelper mEditBoxHelper = null;
    protected boolean hasFocus = false;
    private boolean showVirtualButton = false;
    public boolean isResumed;

    public Cocos2dxGLSurfaceView getGLSurfaceView(){
        return  mGLSurfaceView;
    }

    public static Context getContext() {
        return sContext;
    }
    
    public void setKeepScreenOn(boolean value) {
        final boolean newValue = value;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mGLSurfaceView.setKeepScreenOn(newValue);
            }
        });
    }

    public void setEnableVirtualButton(boolean value) {
        this.showVirtualButton = value;
    }

    protected void onLoadNativeLibraries() {
        try {
            ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
            Bundle bundle = ai.metaData;
            String libName = bundle.getString("android.app.lib_name");
            System.loadLibrary(libName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // ===========================================================
    // Constructors
    // ===========================================================

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508
        /*if (!isTaskRoot()) {
            // Android launched another instance of the root activity into an existing task
            //  so just quietly finish and go away, dropping the user back into the activity
            //  at the top of the stack (ie: the last state of this task)
            finish();
            Log.w(TAG, "[Workaround] Ignore the activity started from icon!");
            return;
        }*/

        this.hideVirtualButton();

        getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0)
                    hideVirtualButton();
            }
        });

        onLoadNativeLibraries();

        sContext = this;
        this.mHandler = new Cocos2dxHandler(this);
        
        Cocos2dxHelper.init(this);
        
        this.mGLContextAttrs = getGLContextAttrs();
        this.init();

        if (mVideoHelper == null) {
            mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);
        }
        
        if(mWebViewHelper == null){
            mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
        }

        if(mEditBoxHelper == null){
            mEditBoxHelper = new Cocos2dxEditBoxHelper(mFrameLayout);
        }

        Window window = this.getWindow();
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

        // Audio configuration
        this.setVolumeControlStream(AudioManager.STREAM_MUSIC);

        Cocos2dxEngineDataManager.init(this, mGLSurfaceView);
    }

    //native method,call GLViewImpl::getGLContextAttrs() to get the OpenGL ES context attributions
    private static native int[] getGLContextAttrs();

    // ===========================================================
    // Getter & Setter
    // ===========================================================

    // ===========================================================
    // Methods for/from SuperClass/Interfaces
    // ===========================================================

    @Override
    protected void onResume() {
        Log.d(TAG, "onResume()");
        super.onResume();
        Cocos2dxAudioFocusManager.registerAudioFocusListener(this);
        this.hideVirtualButton();
        isResumed = true;
        resumeIfHasFocus();
        resumeVideoIfNeeded();
    }
    
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        Log.d(TAG, "onWindowFocusChanged() hasFocus=" + hasFocus);
        super.onWindowFocusChanged(hasFocus);

        this.hasFocus = hasFocus;
        resumeIfHasFocus();
    }
    
    private void resumeIfHasFocus() {
        System.out.println("====== resumeIfHasFocus hasFocus " + hasFocus  + " isResumed " + isResumed);
        if(hasFocus && isResumed) {
            this.hideVirtualButton();
            Cocos2dxHelper.onResume();
            mGLSurfaceView.onResume();

            Cocos2dxEngineDataManager.resume();
            System.out.println("====== onResume Cocos2dxEngineDataManager.resume() ");
        }
    }

    protected boolean isEnabled(){
        return hasFocus && isResumed;
    }

    @Override
    protected void onPause() {
        Log.d(TAG, "onPause()");
        super.onPause();
        isResumed = false;
        Cocos2dxAudioFocusManager.unregisterAudioFocusListener(this);
        Cocos2dxHelper.onPause();
        mGLSurfaceView.onPause();
        Cocos2dxEngineDataManager.pause();
        pauseVideoIfNeeded();
    }

    private void pauseVideoIfNeeded(){
        View childAt = mFrameLayout.getChildAt(0);
        if(childAt instanceof Cocos2dxVideoView){
            Cocos2dxVideoView videoView = (Cocos2dxVideoView) childAt;
            videoView.pauseVideo();
        }
    }

    private void resumeVideoIfNeeded(){
        View childAt = mFrameLayout.getChildAt(0);
        if(childAt instanceof Cocos2dxVideoView){
            Cocos2dxVideoView videoView = (Cocos2dxVideoView) childAt;
            videoView.resumeVideo();
        }
    }

    @Override
    protected void onDestroy() {
        Cocos2dxAudioFocusManager.unregisterAudioFocusListener(this);
        super.onDestroy();

        Cocos2dxEngineDataManager.destroy();
    }

    @Override
    public void showDialog(final String pTitle, final String pMessage) {
        Message msg = new Message();
        msg.what = Cocos2dxHandler.HANDLER_SHOW_DIALOG;
        msg.obj = new Cocos2dxHandler.DialogMessage(pTitle, pMessage);
        this.mHandler.sendMessage(msg);
    }
    
    @Override
    public void runOnGLThread(final Runnable pRunnable) {
        this.mGLSurfaceView.queueEvent(pRunnable);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        for (OnActivityResultListener listener : Cocos2dxHelper.getOnActivityResultListeners()) {
            listener.onActivityResult(requestCode, resultCode, data);
        }

        super.onActivityResult(requestCode, resultCode, data);
    }


    protected ResizeLayout mFrameLayout = null;
    // ===========================================================
    // Methods
    // ===========================================================
    public void init() {
        
        // FrameLayout
        ViewGroup.LayoutParams framelayout_params =
            new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                       ViewGroup.LayoutParams.MATCH_PARENT);

        mFrameLayout = new ResizeLayout(this);

        mFrameLayout.setLayoutParams(framelayout_params);

        // Cocos2dxEditText layout
        ViewGroup.LayoutParams edittext_layout_params =
            new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                       ViewGroup.LayoutParams.WRAP_CONTENT);
        Cocos2dxEditBox edittext = new Cocos2dxEditBox(this);
        edittext.setLayoutParams(edittext_layout_params);


        mFrameLayout.addView(edittext);

        // Cocos2dxGLSurfaceView
        this.mGLSurfaceView = this.onCreateView();

        // ...add to FrameLayout
        mFrameLayout.addView(this.mGLSurfaceView);

        // Switch to supported OpenGL (ARGB888) mode on emulator
        // this line dows not needed on new emulators and also it breaks stencil buffer
        //if (isAndroidEmulator())
        //   this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);

        this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
        this.mGLSurfaceView.setCocos2dxEditText(edittext);

        // Set framelayout as the content view
        setContentView(mFrameLayout);
    }

    
    public Cocos2dxGLSurfaceView onCreateView() {
        Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
        //this line is need on some device if we specify an alpha bits
        if(this.mGLContextAttrs[3] > 0) glSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);

        // use custom EGLConfigureChooser
        Cocos2dxEGLConfigChooser chooser = new Cocos2dxEGLConfigChooser(this.mGLContextAttrs);
        glSurfaceView.setEGLConfigChooser(chooser);

        glSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);//YJH TODO
        glSurfaceView.setZOrderMediaOverlay(true);//YJH TODO 将glSurfaceView setZOrderMediaOverlay 为true 展示在videoView上面

        return glSurfaceView;
    }

    protected void hideVirtualButton() {
        System.out.println("====== hideVirtualButton");
        if (showVirtualButton) {
            return;
        }

        if (Build.VERSION.SDK_INT >= 19) {
            // use reflection to remove dependence of API level

            Class viewClass = View.class;

            try {
                final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = Cocos2dxReflectionHelper.getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION");
                final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = Cocos2dxReflectionHelper.getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN");
                final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = Cocos2dxReflectionHelper.getConstantValue(viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION");
                final int SYSTEM_UI_FLAG_FULLSCREEN = Cocos2dxReflectionHelper.getConstantValue(viewClass, "SYSTEM_UI_FLAG_FULLSCREEN");
                final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = Cocos2dxReflectionHelper.getConstantValue(viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY");
                final int SYSTEM_UI_FLAG_LAYOUT_STABLE = Cocos2dxReflectionHelper.getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE");


//                getWindow().getDecorView().setSystemUiVisibility();
                final Object[] parameters = new Object[]{SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                        | SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                        | SYSTEM_UI_FLAG_IMMERSIVE_STICKY};
                Cocos2dxReflectionHelper.invokeInstanceMethod(getWindow().getDecorView(),
                        "setSystemUiVisibility",
                        new Class[]{Integer.TYPE},
                        parameters);
            } catch (NullPointerException e) {
                Log.e(TAG, "hideVirtualButton", e);
            }
        }
    }

   private static boolean isAndroidEmulator() {
      String model = Build.MODEL;
      Log.d(TAG, "model=" + model);
      String product = Build.PRODUCT;
      Log.d(TAG, "product=" + product);
      boolean isEmulator = false;
      if (product != null) {
         isEmulator = product.equals("sdk") || product.contains("_sdk") || product.contains("sdk_");
      }
      Log.d(TAG, "isEmulator=" + isEmulator);
      return isEmulator;
   }

    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================

    private class Cocos2dxEGLConfigChooser implements GLSurfaceView.EGLConfigChooser
    {
        private int[] mConfigAttributes;
        private  final int EGL_OPENGL_ES2_BIT = 0x04;
        private  final int EGL_OPENGL_ES3_BIT = 0x40;
        public Cocos2dxEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize, int multisamplingCount)
        {
            mConfigAttributes = new int[] {redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize, multisamplingCount};
        }
        public Cocos2dxEGLConfigChooser(int[] attributes)
        {
            mConfigAttributes = attributes;
        }

        @Override
        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)
        {
            int[][] EGLAttributes = {
                {
                    // GL ES 2 with user set
                    EGL10.EGL_RED_SIZE, mConfigAttributes[0],
                    EGL10.EGL_GREEN_SIZE, mConfigAttributes[1],
                    EGL10.EGL_BLUE_SIZE, mConfigAttributes[2],
                    EGL10.EGL_ALPHA_SIZE, mConfigAttributes[3],
                    EGL10.EGL_DEPTH_SIZE, mConfigAttributes[4],
                    EGL10.EGL_STENCIL_SIZE, mConfigAttributes[5],
                    EGL10.EGL_SAMPLE_BUFFERS, (mConfigAttributes[6] > 0) ? 1 : 0,
                    EGL10.EGL_SAMPLES, mConfigAttributes[6],
                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                    EGL10.EGL_NONE
                },
                {
                     // GL ES 2 with user set 16 bit depth buffer
                     EGL10.EGL_RED_SIZE, mConfigAttributes[0],
                     EGL10.EGL_GREEN_SIZE, mConfigAttributes[1],
                     EGL10.EGL_BLUE_SIZE, mConfigAttributes[2],
                     EGL10.EGL_ALPHA_SIZE, mConfigAttributes[3],
                     EGL10.EGL_DEPTH_SIZE, mConfigAttributes[4] >= 24 ? 16 : mConfigAttributes[4],
                     EGL10.EGL_STENCIL_SIZE, mConfigAttributes[5],
                     EGL10.EGL_SAMPLE_BUFFERS, (mConfigAttributes[6] > 0) ? 1 : 0,
                     EGL10.EGL_SAMPLES, mConfigAttributes[6],
                     EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                     EGL10.EGL_NONE
                },
                {
                     // GL ES 2 with user set 16 bit depth buffer without multisampling
                     EGL10.EGL_RED_SIZE, mConfigAttributes[0],
                     EGL10.EGL_GREEN_SIZE, mConfigAttributes[1],
                     EGL10.EGL_BLUE_SIZE, mConfigAttributes[2],
                     EGL10.EGL_ALPHA_SIZE, mConfigAttributes[3],
                     EGL10.EGL_DEPTH_SIZE, mConfigAttributes[4] >= 24 ? 16 : mConfigAttributes[4],
                     EGL10.EGL_STENCIL_SIZE, mConfigAttributes[5],
                     EGL10.EGL_SAMPLE_BUFFERS, 0,
                     EGL10.EGL_SAMPLES, 0,
                     EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                     EGL10.EGL_NONE
                },
                {
                    // GL ES 2 by default
                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                    EGL10.EGL_NONE
                }
            };

            EGLConfig result = null;
            for (int[] eglAtribute : EGLAttributes) {
                result = this.doChooseConfig(egl, display, eglAtribute);
                if (result != null)
                    return result;
            }

            Log.e(DEVICE_POLICY_SERVICE, "Can not select an EGLConfig for rendering.");
            return null;
        }

        private EGLConfig doChooseConfig(EGL10 egl, EGLDisplay display, int[] attributes) {
            EGLConfig[] configs = new EGLConfig[1];
            int[] matchedConfigNum = new int[1];
            boolean result = egl.eglChooseConfig(display, attributes, configs, 1, matchedConfigNum);
            if (result && matchedConfigNum[0] > 0) {
                return configs[0];
            }
            return null;
        }
    }
}

视频相关的Cocos2dxVideoHelper.java


/****************************************************************************
 Copyright (c) 2014-2016 Chukong Technologies Inc.
 Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.

 http://www.cocos2d-x.org

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/

package org.cocos2dx.lib;

import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.SparseArray;
import android.view.View;
import android.widget.FrameLayout;

import org.cocos2dx.lib.Cocos2dxVideoView.OnVideoEventListener;

import java.lang.ref.WeakReference;

public class Cocos2dxVideoHelper {

    private FrameLayout mLayout = null;
    private Cocos2dxActivity mActivity = null;
    private SparseArray sVideoViews = null;
    static VideoHandler mVideoHandler = null;
    static int duration;
    static int currentPosition;

    Cocos2dxVideoHelper(Cocos2dxActivity activity,FrameLayout layout)
    {
        mActivity = activity;
        mLayout = layout;

        mVideoHandler = new VideoHandler(this);
        sVideoViews = new SparseArray();
    }

    private static int videoTag = 0;
    private final static int VideoTaskCreate = 0;
    private final static int VideoTaskRemove = 1;
    private final static int VideoTaskSetSource = 2;
    private final static int VideoTaskSetRect = 3;
    private final static int VideoTaskStart = 4;
    private final static int VideoTaskPause = 5;
    private final static int VideoTaskResume = 6;
    private final static int VideoTaskStop = 7;
    private final static int VideoTaskSeek = 8;
    private final static int VideoTaskSetVisible = 9;
    private final static int VideoTaskRestart = 10;
    private final static int VideoTaskKeepRatio = 11;
    private final static int VideoTaskFullScreen = 12;
    private final static int VideoTaskDuration = 13;
    private final static int VideoTaskCurrentPosition = 14;
    private final static int VideoTaskSetVolume= 15;
    private final static int VideoTaskSetBrightness = 16;
    final static int KeyEventBack = 1000;

    static class VideoHandler extends Handler{
        WeakReference mReference;

        VideoHandler(Cocos2dxVideoHelper helper){
            mReference = new WeakReference(helper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case VideoTaskCreate: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._createVideoView(msg.arg1);
                    break;
                }
                case VideoTaskRemove: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._removeVideoView(msg.arg1);
                    break;
                }
                case VideoTaskSetSource: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._setVideoURL(msg.arg1, msg.arg2, (String)msg.obj);
                    break;
                }
                case VideoTaskStart: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._startVideo(msg.arg1);
                    break;
                }
                case VideoTaskSetRect: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    Rect rect = (Rect)msg.obj;
                    helper._setVideoRect(msg.arg1, rect.left, rect.top, rect.right, rect.bottom);
                    break;
                }
                case VideoTaskFullScreen:{
                    Cocos2dxVideoHelper helper = mReference.get();
                    Rect rect = (Rect)msg.obj;
                    if (msg.arg2 == 1) {
                        helper._setFullScreenEnabled(msg.arg1, true, rect.right, rect.bottom);
                    } else {
                        helper._setFullScreenEnabled(msg.arg1, false, rect.right, rect.bottom);
                    }
                    break;
                }
                case VideoTaskPause: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._pauseVideo(msg.arg1);
                    break;
                }
                case VideoTaskResume: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._resumeVideo(msg.arg1);
                    break;
                }
                case VideoTaskStop: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._stopVideo(msg.arg1);
                    break;
                }
                case VideoTaskSeek: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._seekVideoTo(msg.arg1, msg.arg2);
                    break;
                }

                case VideoTaskSetVolume:{
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._setVolume(msg.arg1, (Float) msg.obj);
                    break;
                }

                case VideoTaskSetBrightness:{
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._setBrightness(msg.arg1, (Float) msg.obj);
                    break;
                }

                case VideoTaskDuration:{
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._getVideoDuration(msg.arg1);
                    break;
                }

                case VideoTaskCurrentPosition:{
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._getVideoCurrentTime(msg.arg1);
                    break;
                }


                case VideoTaskSetVisible: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    if (msg.arg2 == 1) {
                        helper._setVideoVisible(msg.arg1, true);
                    } else {
                        helper._setVideoVisible(msg.arg1, false);
                    }
                    break;
                }
                case VideoTaskRestart: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper._restartVideo(msg.arg1);
                    break;
                }
                case VideoTaskKeepRatio: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    if (msg.arg2 == 1) {
                        helper._setVideoKeepRatio(msg.arg1, true);
                    } else {
                        helper._setVideoKeepRatio(msg.arg1, false);
                    }
                    break;
                }
                case KeyEventBack: {
                    Cocos2dxVideoHelper helper = mReference.get();
                    helper.onBackKeyEvent();
                    break;
                }
                default:
                    break;
            }

            super.handleMessage(msg);
        }
    }

    private class VideoEventRunnable implements Runnable
    {
        private int mVideoTag;
        private int mVideoEvent;

        public VideoEventRunnable(int tag,int event) {
            mVideoTag = tag;
            mVideoEvent = event;
        }
        @Override
        public void run() {
            nativeExecuteVideoCallback(mVideoTag, mVideoEvent);
        }

    }

    public static native void nativeExecuteVideoCallback(int index,int event);

    OnVideoEventListener videoEventListener = new OnVideoEventListener() {

        @Override
        public void onVideoEvent(int tag,int event) {
            mActivity.runOnGLThread(new VideoEventRunnable(tag, event));
        }
    };


    public static int createVideoWidget() {
        Message msg = Message.obtain();
        msg.what = VideoTaskCreate;
        msg.arg1 = videoTag;
        mVideoHandler.sendMessage(msg);

        return videoTag++;
    }

    private void _createVideoView(int index) {
        Cocos2dxVideoView videoView = new Cocos2dxVideoView(mActivity,index);
        sVideoViews.put(index, videoView);
        FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.WRAP_CONTENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        mLayout.addView(videoView, 0, lParams);//yjh TODO 视频显示在最上面,游戏的glSurfaceView setZOrderMediaOverlay展示在视频上面
        //yjh TODO videoView.setZOrderOnTop(true);
        videoView.setOnCompletionListener(videoEventListener);
    }

    public static void removeVideoWidget(int index){
        Message msg = Message.obtain();
        msg.what = VideoTaskRemove;
        msg.arg1 = index;
        mVideoHandler.sendMessage(msg);
    }

    private void _removeVideoView(int index) {
        Cocos2dxVideoView view = sVideoViews.get(index);
        if (view != null) {
            view.stopPlayback();
            sVideoViews.remove(index);
            mLayout.removeView(view);
        }
    }

    public static void setVideoUrl(int index, int videoSource, String videoUrl) {
        Message msg = Message.obtain();
        msg.what = VideoTaskSetSource;
        msg.arg1 = index;
        msg.arg2 = videoSource;
        msg.obj = videoUrl;
        mVideoHandler.sendMessage(msg);
    }

    private void _setVideoURL(int index, int videoSource, String videoUrl) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            switch (videoSource) {
                case 0:
                    videoView.setVideoFileName(videoUrl);
                    break;
                case 1:
                    videoView.setVideoURL(videoUrl);
                    break;
                default:
                    break;
            }
        }
    }

    public static void setVideoRect(int index, int left, int top, int maxWidth, int maxHeight) {
        Message msg = Message.obtain();
        msg.what = VideoTaskSetRect;
        msg.arg1 = index;
        msg.obj = new Rect(left, top, maxWidth, maxHeight);
        mVideoHandler.sendMessage(msg);
    }

    private void _setVideoRect(int index, int left, int top, int maxWidth, int maxHeight) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.setVideoRect(left,top,maxWidth,maxHeight);
        }
    }

    public static void setFullScreenEnabled(int index, boolean enabled, int width, int height) {
        Message msg = Message.obtain();
        msg.what = VideoTaskFullScreen;
        msg.arg1 = index;
        if (enabled) {
            msg.arg2 = 1;
        } else {
            msg.arg2 = 0;
        }
        msg.obj = new Rect(0, 0, width, height);
        mVideoHandler.sendMessage(msg);
    }

    private void _setFullScreenEnabled(int index, boolean enabled, int width,int height) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.setFullScreenEnabled(enabled, width, height);
        }
    }

    private void onBackKeyEvent() {
        int viewCount = sVideoViews.size();
        for (int i = 0; i < viewCount; i++) {
            int key = sVideoViews.keyAt(i);
            Cocos2dxVideoView videoView = sVideoViews.get(key);
            if (videoView != null) {
                videoView.setFullScreenEnabled(false, 0, 0);
                mActivity.runOnGLThread(new VideoEventRunnable(key, KeyEventBack));
            }
        }
    }

    public static void startVideo(int index) {
        Message msg = Message.obtain();
        msg.what = VideoTaskStart;
        msg.arg1 = index;
        mVideoHandler.sendMessage(msg);
    }

    private void _startVideo(int index) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.start();
        }
    }

    public static void pauseVideo(int index) {
        Message msg = Message.obtain();
        msg.what = VideoTaskPause;
        msg.arg1 = index;
        mVideoHandler.sendMessage(msg);
    }

    private void _pauseVideo(int index) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.pause();
        }
    }

    public static void resumeVideo(int index) {
        Message msg = Message.obtain();
        msg.what = VideoTaskResume;
        msg.arg1 = index;
        mVideoHandler.sendMessage(msg);
    }

    private void _resumeVideo(int index) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.resume();
        }
    }

    public static void stopVideo(int index) {
        Message msg = Message.obtain();
        msg.what = VideoTaskStop;
        msg.arg1 = index;
        mVideoHandler.sendMessage(msg);
    }

    private void _stopVideo(int index) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.stop();
        }
    }

    public static void restartVideo(int index) {
        Message msg = Message.obtain();
        msg.what = VideoTaskRestart;
        msg.arg1 = index;
        mVideoHandler.sendMessage(msg);
    }

    private void _restartVideo(int index) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.restart();
        }
    }

    public static void seekVideoTo(int index,int msec) {
        Message msg = Message.obtain();
        msg.what = VideoTaskSeek;
        msg.arg1 = index;
        msg.arg2 = msec;
        mVideoHandler.sendMessage(msg);
    }

    private void _seekVideoTo(int index,int msec) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.seekTo(msec);
        }
    }



    public static void setVolume(int index,float volume) {
        Message msg = Message.obtain();
        msg.what = VideoTaskSetVolume;
        msg.arg1 = index;
        msg.obj = volume;
        mVideoHandler.sendMessage(msg);
    }

    private void _setVolume(int index,float volume) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.setVolume(volume);
        }
    }

    public static void setBrightness(int index,float brightness) {
        Message msg = Message.obtain();
        msg.what = VideoTaskSetBrightness;
        msg.arg1 = index;
        msg.obj = brightness;
        mVideoHandler.sendMessage(msg);
    }

    private void _setBrightness(int index,float brightness) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.setBrightness(brightness);
        }
    }


    public static int getVideoDuration(int index) {
        Message msg = Message.obtain();
        msg.what = VideoTaskDuration;
        msg.arg1 = index;
        mVideoHandler.sendMessage(msg);
        SystemClock.sleep(100);
        return duration;
    }

    private void  _getVideoDuration(int index) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            duration  = videoView.getDuration() / 1000;
        }
    }

    public static int getVideoCurrentTime(int index) {
        Message msg = Message.obtain();
        msg.what = VideoTaskCurrentPosition;
        msg.arg1 = index;
        mVideoHandler.sendMessage(msg);
        SystemClock.sleep(100);
        return currentPosition;
    }

    private void _getVideoCurrentTime(int index) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            currentPosition  = videoView.getCurrentPosition() / 1000;
        }
    }


    public static void setVideoVisible(int index, boolean visible) {
        Message msg = Message.obtain();
        msg.what = VideoTaskSetVisible;
        msg.arg1 = index;
        if (visible) {
            msg.arg2 = 1;
        } else {
            msg.arg2 = 0;
        }

        mVideoHandler.sendMessage(msg);
    }

    private void _setVideoVisible(int index, boolean visible) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            if (visible) {
                videoView.fixSize();
                videoView.setVisibility(View.VISIBLE);
            } else {
                videoView.setVisibility(View.INVISIBLE);
            }
        }
    }

    public static void setVideoKeepRatioEnabled(int index, boolean enable) {
        Message msg = Message.obtain();
        msg.what = VideoTaskKeepRatio;
        msg.arg1 = index;
        if (enable) {
            msg.arg2 = 1;
        } else {
            msg.arg2 = 0;
        }
        mVideoHandler.sendMessage(msg);
    }

    private void _setVideoKeepRatio(int index, boolean enable) {
        Cocos2dxVideoView videoView = sVideoViews.get(index);
        if (videoView != null) {
            videoView.setKeepRatio(enable);
        }
    }
}

Cocos2dxVideoView.java


image.png
/*
 * Copyright (C) 2006 The Android Open Source Project
 * Copyright (c) 2014-2016 Chukong Technologies Inc.
 * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.cocos2dx.lib;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.MediaController.MediaPlayerControl;

import com.zhy.base.fileprovider.FileProvider7;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import static android.content.Context.AUDIO_SERVICE;
import static android.media.MediaPlayer.SEEK_CLOSEST;

public class Cocos2dxVideoView extends SurfaceView implements MediaPlayerControl {
    private String TAG = "Cocos2dxVideoView";

    private Uri mVideoUri;
    private int mDuration;

    // 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;

    /**
     * mCurrentState is a VideoView object's current state.
     * mTargetState is the state that a method caller intends to reach.
     * For instance, regardless the VideoView 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 SurfaceHolder mSurfaceHolder = null;
    private MediaPlayer mMediaPlayer = null;
    private int mVideoWidth = 0;
    private int mVideoHeight = 0;

    private OnVideoEventListener mOnVideoEventListener;
    private MediaPlayer.OnPreparedListener mOnPreparedListener;
    private int mCurrentBufferPercentage;
    private OnErrorListener mOnErrorListener;

    // recording the seek position while preparing
    private int mSeekWhenPrepared;

    protected Cocos2dxActivity mCocos2dxActivity = null;

    protected int mViewLeft = 0;
    protected int mViewTop = 0;
    protected int mViewWidth = 0;
    protected int mViewHeight = 0;

    protected int mVisibleLeft = 0;
    protected int mVisibleTop = 0;
    protected int mVisibleWidth = 0;
    protected int mVisibleHeight = 0;

    protected boolean mFullScreenEnabled = false;
    protected int mFullScreenWidth = 0;
    protected int mFullScreenHeight = 0;

    private int mViewTag = 0;

    private boolean isMp3;

    public Cocos2dxVideoView(Cocos2dxActivity activity, int tag) {
        super(activity);

        mViewTag = tag;
        mCocos2dxActivity = activity;
        initVideoView();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mVideoWidth == 0 || mVideoHeight == 0) {
            setMeasuredDimension(mViewWidth, mViewHeight);
            Log.i(TAG, "" + mViewWidth + ":" + mViewHeight);
        } else {
            setMeasuredDimension(mVisibleWidth, mVisibleHeight);
            Log.i(TAG, "" + mVisibleWidth + ":" + mVisibleHeight);
        }

    }

    public void setVideoRect(int left, int top, int maxWidth, int maxHeight) {
        mViewLeft = left;
        mViewTop = top;
        mViewWidth = maxWidth;
        mViewHeight = maxHeight;

        fixSize(mViewLeft, mViewTop, mViewWidth, mViewHeight);
    }

    public void setFullScreenEnabled(boolean enabled, int width, int height) {
        if (mFullScreenEnabled != enabled) {
            mFullScreenEnabled = enabled;
            if (width != 0 && height != 0) {
                mFullScreenWidth = width;
                mFullScreenHeight = height;
            }

            fixSize();
        }
    }

    public int resolveAdjustedSize(int desiredSize, int measureSpec) {
        int result = desiredSize;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                /* Parent says we can be as big as we want. Just don't be larger
                 * than max size imposed on ourselves.
                 */
                result = desiredSize;
                break;

            case MeasureSpec.AT_MOST:
                /* Parent says we can be as big as we want, up to specSize.
                 * Don't be larger than specSize, and don't be larger than
                 * the max size imposed on ourselves.
                 */
                result = Math.min(desiredSize, specSize);
                break;

            case MeasureSpec.EXACTLY:
                // No choice. Do what we are told.
                result = specSize;
                break;
        }

        return result;
    }

    private boolean mNeedResume = false;

    @Override
    public void setVisibility(int visibility) {
        if (visibility == INVISIBLE) {
            mNeedResume = isPlaying();
            if (mNeedResume) {
                mSeekWhenPrepared = getCurrentPosition();
            }
        } else if (mNeedResume) {
            start();
            mNeedResume = false;
        }
        super.setVisibility(visibility);
    }

    private void initVideoView() {
        mVideoWidth = 0;
        mVideoHeight = 0;
        getHolder().addCallback(mSHCallback);
        //Fix issue#11516:Can't play video on Android 2.3.x
        getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        setFocusable(true);
        setFocusableInTouchMode(true);
        mCurrentState = STATE_IDLE;
        mTargetState = STATE_IDLE;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        /*YJH if((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP)
        {
            if (isPlaying()) {
                pause();
            } else if(mCurrentState == STATE_PAUSED){
                resume();
            }
        }*/

        return false;
    }

    private boolean mIsAssetRouse = false;
    private String mVideoFilePath = null;
    private static final String AssetResourceRoot = "assets/";

    public void setVideoFileName(String path) {
        if (path.startsWith(AssetResourceRoot)) {
            path = path.substring(AssetResourceRoot.length());
        }
        if (path.startsWith("/")) {
            mIsAssetRouse = false;
            setVideoURI(FileProvider7.getUriForFile(getContext(), new File(path)), null);
        } else {
            mVideoFilePath = path;
            mIsAssetRouse = true;
            setVideoURI(Uri.parse(path), null);
        }
    }

    public void setVideoURL(String url) {
        mIsAssetRouse = false;
        setVideoURI(Uri.parse(url), null);
    }

    /**
     * @hide
     */
    private void setVideoURI(Uri uri, Map headers) {
        mVideoUri = uri;
        String path = mVideoUri.getPath();
        if(!TextUtils.isEmpty(path))
            isMp3 = path.toLowerCase().endsWith(".mp3") || path.toLowerCase().endsWith(".wav");
        mSeekWhenPrepared = 0;
        mVideoWidth = 0;
        mVideoHeight = 0;
        openVideo();

        if(isMp3)
            return;

        requestLayout();
        invalidate();
    }

    public void stopPlayback() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
            mCurrentState = STATE_IDLE;
            mTargetState = STATE_IDLE;
        }
    }

    private void openVideo() {

        // Tell the music playback service to pause
        // TODO: these constants need to be published somewhere in the framework.
        Intent i = new Intent("com.android.music.musicservicecommand");
        i.putExtra("command", "pause");
        mCocos2dxActivity.sendBroadcast(i);

        if (mSurfaceHolder == null && !isMp3) {
            // not ready for playback just yet, will try again later
            return;
        }

        if(isMp3){
            getHolder().removeCallback(mSHCallback);
        }

        if (mIsAssetRouse) {
            if (mVideoFilePath == null)
                return;
        } else if (mVideoUri == null) {
            return;
        }

        // we shouldn't clear the target state, because somebody might have
        // called start() previously
        release(false);

        try {
            //if (mMediaPlayer == null) {
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setOnPreparedListener(mPreparedListener);
            mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
            mMediaPlayer.setOnCompletionListener(mCompletionListener);
            mMediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
                @Override
                public void onSeekComplete(MediaPlayer mp) {
                    mOnVideoEventListener.onVideoEvent(mViewTag, mCurrentState == STATE_PAUSED
                            || (mNeedAutoPlay != null && !mNeedAutoPlay) ? EVENT_PAUSED : EVENT_PLAYING);
                }
            });
            mMediaPlayer.setOnErrorListener(mErrorListener);
            mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
            mMediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
                @Override
                public boolean onInfo(MediaPlayer mp, int what, int extra) {
                    if (mOnVideoEventListener != null) {
                        switch (what) {
                            case MediaPlayer.MEDIA_INFO_BUFFERING_START:
                                mOnVideoEventListener.onVideoEvent(mViewTag, EVENT_STALLED);
                                break;
                            case MediaPlayer.MEDIA_INFO_BUFFERING_END:
                                //视频缓冲完成后,回调播放状态。
                                mOnVideoEventListener.onVideoEvent(mViewTag, mCurrentState == STATE_PAUSED
                                        || (mNeedAutoPlay != null && !mNeedAutoPlay) ? EVENT_PAUSED : EVENT_PLAYING);
                                mNeedAutoPlay = null;
                                System.out.println("====== MEDIA_INFO_BUFFERING_END ");
                                break;
                        }
                    }

                    return false;
                }
            });

            if(!isMp3)
                mMediaPlayer.setDisplay(mSurfaceHolder);

            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mMediaPlayer.setScreenOnWhilePlaying(true);
            //}

            mDuration = -1;
            mCurrentBufferPercentage = 0;
            if (mIsAssetRouse) {
                AssetFileDescriptor afd = mCocos2dxActivity.getAssets().openFd(mVideoFilePath);
                mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
            } else {
                mMediaPlayer.setDataSource(mCocos2dxActivity, mVideoUri);
            }

            mMediaPlayer.prepareAsync();

            /**
             * Don't set the target state here either, but preserve the target state that was there before.
             */
            mCurrentState = STATE_PREPARING;
        } catch (IOException ex) {
            Log.w(TAG, "Unable to open content: " + mVideoUri, 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: " + mVideoUri, ex);
            mCurrentState = STATE_ERROR;
            mTargetState = STATE_ERROR;
            mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
            return;
        }
    }

    private boolean mKeepRatio = false;

    public void setKeepRatio(boolean enabled) {
        mKeepRatio = enabled;
        fixSize();
    }

    public void fixSize() {
        if (mFullScreenEnabled) {
            fixSize(0, 0, mFullScreenWidth, mFullScreenHeight);
        } else {
            fixSize(mViewLeft, mViewTop, mViewWidth, mViewHeight);
        }
    }

    public void fixSize(int left, int top, int width, int height) {
        if (mVideoWidth == 0 || mVideoHeight == 0) {
            mVisibleLeft = left;
            mVisibleTop = top;
            mVisibleWidth = width;
            mVisibleHeight = height;
        } else if (width != 0 && height != 0) {
            if (mKeepRatio) {
                if (mVideoWidth * height > width * mVideoHeight) {
                    mVisibleWidth = width;
                    mVisibleHeight = width * mVideoHeight / mVideoWidth;
                } else if (mVideoWidth * height < width * mVideoHeight) {
                    mVisibleWidth = height * mVideoWidth / mVideoHeight;
                    mVisibleHeight = height;
                }
                mVisibleLeft = left + (width - mVisibleWidth) / 2;
                mVisibleTop = top + (height - mVisibleHeight) / 2;
            } else {
                mVisibleLeft = left;
                mVisibleTop = top;
                mVisibleWidth = width;
                mVisibleHeight = height;
            }
        } else {
            mVisibleLeft = left;
            mVisibleTop = top;
            mVisibleWidth = mVideoWidth;
            mVisibleHeight = mVideoHeight;
        }

        getHolder().setFixedSize(mVisibleWidth, mVisibleHeight);

        FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
                FrameLayout.LayoutParams.WRAP_CONTENT);
        lParams.leftMargin = mVisibleLeft;
        lParams.topMargin = mVisibleTop;
        lParams.gravity = Gravity.TOP | Gravity.LEFT;
        setLayoutParams(lParams);
    }

    protected
    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) {
                        getHolder().setFixedSize(mVideoWidth, mVideoHeight);
                    }
                }
            };


    MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
        public void onPrepared(MediaPlayer mp) {
            mCurrentState = STATE_PREPARED;

            if (mOnPreparedListener != null) {
                mOnPreparedListener.onPrepared(mMediaPlayer);
            }

            mVideoWidth = mp.getVideoWidth();
            mVideoHeight = mp.getVideoHeight();

            // mSeekWhenPrepared may be changed after seekTo() call
            int seekToPosition = mSeekWhenPrepared;
            if (seekToPosition != 0) {
                seekTo(seekToPosition);
            }

            if (mVideoWidth != 0 && mVideoHeight != 0) {
                fixSize();
            }

            if (mTargetState == STATE_PLAYING) {
                start();
            }
        }
    };

    private MediaPlayer.OnCompletionListener mCompletionListener =
            new MediaPlayer.OnCompletionListener() {
                public void onCompletion(MediaPlayer mp) {
                    mCurrentState = STATE_PLAYBACK_COMPLETED;
                    mTargetState = STATE_PLAYBACK_COMPLETED;

                    release(true);
                    if (mOnVideoEventListener != null) {
                        mOnVideoEventListener.onVideoEvent(mViewTag, EVENT_COMPLETED);
                    }
                }
            };


    private static final int EVENT_PLAYING = 0;
    private static final int EVENT_PAUSED = 1;
    private static final int EVENT_STOPPED = 2;
    private static final int EVENT_COMPLETED = 3;
    private static final int EVENT_ERROR = 4;//error
    private static final int EVENT_STALLED = 5;//loading

    public interface OnVideoEventListener {
        void onVideoEvent(int tag, int event);
    }

    private MediaPlayer.OnErrorListener mErrorListener =
            new MediaPlayer.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 (mOnVideoEventListener != null) {
                        mOnVideoEventListener.onVideoEvent(mViewTag, EVENT_ERROR);
                    }


                    /* 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 = mCocos2dxActivity.getResources();
//                        int messageId;
//
//                        if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
//                            // messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback;
//                            messageId = r.getIdentifier("VideoView_error_text_invalid_progressive_playback", "string", "android");
//                        } else {
//                            // messageId = com.android.internal.R.string.VideoView_error_text_unknown;
//                            messageId = r.getIdentifier("VideoView_error_text_unknown", "string", "android");
//                        }
//
//                        int titleId = r.getIdentifier("VideoView_error_title", "string", "android");
//                        int buttonStringId = r.getIdentifier("VideoView_error_button", "string", "android");
//
//                        new AlertDialog.Builder(mCocos2dxActivity)
//                                .setTitle(r.getString(titleId))
//                                .setMessage(messageId)
//                                .setPositiveButton(r.getString(buttonStringId),
//                                        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 (mOnVideoEventListener != null) {
//                                                    mOnVideoEventListener.onVideoEvent(mViewTag, EVENT_ERROR);
//                                                }
//                                            }
//                                        })
//                                .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.
     *
     * @param l The callback that will be run
     */
    public void setOnPreparedListener(MediaPlayer.OnPreparedListener l) {
        mOnPreparedListener = l;
    }

    /**
     * Register a callback to be invoked when the end of a media file
     * has been reached during play back.
     *
     * @param l The callback that will be run
     */
    public void setOnCompletionListener(OnVideoEventListener l) {
        mOnVideoEventListener = l;
    }

    /**
     * Register a callback to be invoked when an error occurs
     * during play back or setup.  If no listener is specified,
     * or if the listener returned false, VideoView will inform
     * the user of any errors.
     *
     * @param l The callback that will be run
     */
    public void setOnErrorListener(OnErrorListener l) {
        mOnErrorListener = l;
    }

    /**
     * 记录非主动点击暂停导致的视频暂停,这种情况需要在resume时自动播放
     */
    private Boolean mNeedAutoPlay;
    public void pauseVideo(){
        if(isInPlaybackState()){
            mSeekWhenPrepared = getCurrentPosition();
        }
        mNeedAutoPlay = isPlaying();
        System.out.println("====== pauseVideo mNeedAutoPlay: " + mNeedAutoPlay);
        pause();
    }

    public void resumeVideo(){
        if((mSurfaceHolder != null || isMp3) && mNeedAutoPlay != null && mNeedAutoPlay){
            if(mSurfaceHolder != null){
                //有些手机锁屏时不会销毁mSurfaceHolder 这里直接resume
                mSeekWhenPrepared = 0;
                resume();
            }else{
                //处理华为手机播放mp3音乐时home再回来时不能自动播放
                mTargetState = STATE_PLAYING;
                openVideo();
            }
            mNeedAutoPlay = null;
        }
    }


    SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() {
        public void surfaceChanged(SurfaceHolder holder, int format,
                                   int w, int h) {
            System.out.println("====== surfaceChanged ");
            boolean isValidState = mTargetState == STATE_PLAYING;
            boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);
            if(mMediaPlayer != null){
                if (mSeekWhenPrepared != 0) {
                    seekTo(mSeekWhenPrepared);
                }
                if(hasValidSize){
                    if(mNeedAutoPlay != null && mNeedAutoPlay){
                        start();
                    }else if(isValidState){
                        start();
                    }
                }
            }
        }

        public void surfaceCreated(SurfaceHolder holder) {
            mSurfaceHolder = holder;
            openVideo();
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            // after we return from this we can't use the surface any more
            mSurfaceHolder = null;

            release(true);
        }
    };

    /*
     * 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;
            }
        }
    }

    public void start() {
        if (isInPlaybackState()) {
            mMediaPlayer.start();
            mCurrentState = STATE_PLAYING;
            if (mOnVideoEventListener != null) {
                mOnVideoEventListener.onVideoEvent(mViewTag, EVENT_PLAYING);
            }
        }
        mTargetState = STATE_PLAYING;
    }

    public void pause() {
        if (isInPlaybackState()) {
            if (mMediaPlayer.isPlaying()) {
                mMediaPlayer.pause();
                mCurrentState = STATE_PAUSED;
                if (mOnVideoEventListener != null) {
                    mOnVideoEventListener.onVideoEvent(mViewTag, EVENT_PAUSED);
                }
            }
        }
        mTargetState = STATE_PAUSED;
    }

    public void stop() {

        if(isMp3){
            mVideoFilePath = null;
            mVideoUri = null;
            stopPlayback();
        }else if (isInPlaybackState()) {
            if (mMediaPlayer.isPlaying()) {
                stopPlayback();
                if (mOnVideoEventListener != null) {
                    mOnVideoEventListener.onVideoEvent(mViewTag, EVENT_STOPPED);
                }
            }
        }
    }

    public void suspend() {
        release(false);
    }

    public void resume() {
        if (isInPlaybackState()) {
            if (mCurrentState == STATE_PAUSED) {
                mMediaPlayer.start();
                mCurrentState = STATE_PLAYING;
                if (mOnVideoEventListener != null) {
                    mOnVideoEventListener.onVideoEvent(mViewTag, EVENT_PLAYING);
                }
            }
        }
    }

    public void restart() {
        if (isInPlaybackState()) {
            mMediaPlayer.seekTo(0);
            mMediaPlayer.start();
            mCurrentState = STATE_PLAYING;
            mTargetState = STATE_PLAYING;
        }
    }

    // cache duration as mDuration for faster access
    public int getDuration() {
        if (isInPlaybackState()) {
            if (mDuration > 0) {
                return mDuration;
            }
            mDuration = mMediaPlayer.getDuration();
            return mDuration;
        }
        mDuration = -1;
        return mDuration;
    }

    public int getCurrentPosition() {
        if (isInPlaybackState()) {
            return mMediaPlayer.getCurrentPosition();
        }
        return 0;
    }

    public void seekTo(int msec) {
        if (isInPlaybackState()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                mMediaPlayer.seekTo(msec, SEEK_CLOSEST);
            }else {
                mMediaPlayer.seekTo(msec);
            }
            mSeekWhenPrepared = 0;
        } else {
            mSeekWhenPrepared = msec;
        }
    }

    public boolean isPlaying() {
        return isInPlaybackState() && mMediaPlayer.isPlaying();
    }

    public void setVolume(float volume) {
        AudioManager audioManager = (AudioManager) getContext().getSystemService(AUDIO_SERVICE);
        if (audioManager != null) {
            int current = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            int max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
            if(volume < 0){
                volume = (float) Math.min(Math.ceil(volume * max), -1);
            }else if(volume > 0) {
                volume = (float) Math.max(Math.ceil(volume * max), 1);
            }
            current = (int) (current + volume);
            current = current < 0 ? 0: current > max ? max : current;
            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, current, AudioManager.FLAG_PLAY_SOUND);
        }
    }

    public void setBrightness(float brightness) {
        Context context = getContext();
        if (context instanceof Activity) {
            Activity activity = (Activity) context;
            WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
            if(lp.screenBrightness == WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE){
                lp.screenBrightness = getBrightness(activity);
            }

            lp.screenBrightness += brightness;
            if (lp.screenBrightness > 1) {
                lp.screenBrightness = 1;
            } else if (lp.screenBrightness < 0.01) {
                lp.screenBrightness = (float) 0.01;
            }
            activity.getWindow().setAttributes(lp);
        }
    }

    private int getBrightness(Activity activity){
        int value = 0;
        ContentResolver cr = activity.getContentResolver();
        try {
            value = Settings.System.getInt(cr, Settings.System.SCREEN_BRIGHTNESS)/255;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }

    public int getBufferPercentage() {
        if (mMediaPlayer != null) {
            return mCurrentBufferPercentage;
        }
        return 0;
    }

    public boolean isInPlaybackState() {
        return (mMediaPlayer != null &&
                mCurrentState != STATE_ERROR &&
                mCurrentState != STATE_IDLE &&
                mCurrentState != STATE_PREPARING);
    }

    @Override
    public boolean canPause() {
        return true;
    }

    @Override
    public boolean canSeekBackward() {
        return true;
    }

    @Override
    public boolean canSeekForward() {
        return true;
    }

    public int getAudioSessionId() {
        return mMediaPlayer.getAudioSessionId();
    }


    @Override
    protected void onDetachedFromWindow() {
        Context context = getContext();
        if (context instanceof Activity) {
            Activity activity = (Activity) context;
            WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
            lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;//activity恢复之前亮度
            activity.getWindow().setAttributes(lp);
        }
        super.onDetachedFromWindow();
    }
}

github地址 https://github.com/kevin-mob/cocos2djs

你可能感兴趣的:(cocos2d-x-js(cocos2d-x-3.17) 项目经验总结(二))